Skip to content

Commit 054a564

Browse files
authored
Merge pull request #1665 from SoftUni-Internal/v2-development
Merge dev into main. Integrate latest bug fixes and improvements.
2 parents 7836c07 + aee2dce commit 054a564

File tree

15 files changed

+296
-100
lines changed

15 files changed

+296
-100
lines changed
Lines changed: 170 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,195 @@
11
import { expect, test } from './fixtures/auth.fixture';
22

3-
test('has title', async ({ page }) => {
3+
test('Has title', async ({ page }) => {
44
await page.goto('/') ;
55

66
// Expect a title "to contain" a substring.
77
await expect(page).toHaveTitle(/SoftUni Judge Platform/);
88
});
99

10-
test('can log in', async ({ page, auth }) => {
10+
test('Can log in', async ({ page, auth }) => {
1111
await auth.loginDefaultTestUser();
1212
const response = await page.goto('/profile');
1313
expect(response?.status()).toBe(200);
1414
await expect(page).toHaveURL('/profile');
1515
});
1616

17-
test('can log out', async ({ page, auth }) => {
17+
test('Can log out', async ({ page, auth }) => {
1818
await auth.loginDefaultTestUser();
1919
await page.goto('/profile');
2020
await expect(page).toHaveURL('/profile');
2121

2222
await page.goto('/logout');
2323

2424
await expect(page.getByText('You are now successfully logged out')).toBeVisible();
25-
await page.waitForURL('/');
2625
await expect(page).toHaveURL('/');
2726

2827
const response = await page.goto('/profile');
29-
expect(response?.status()).not.toBe(200);
28+
await expect(page).toHaveURL('/login');
3029
});
30+
31+
test('Should contain the text "How to use SoftUni Judge Platform"', async ({ page }) => {
32+
await page.goto('/');
33+
34+
// Check if the text is present on the page
35+
const text = await page.locator('text=How to use SoftUni Judge Platform');
36+
await expect(text).toBeVisible();
37+
});
38+
39+
test('Should Check if the video is visible', async ({ page }) => {
40+
await page.goto('/');
41+
42+
const videoElement = await page.locator('[data-title="Guidelines for working with the SoftUni Judge platform"]');
43+
44+
// Check if the video is visible
45+
await expect(videoElement).toBeVisible();
46+
});
47+
48+
test('Verify Programming Basics dropdown contents', async ({ page }) => {
49+
// Navigate to the SoftUni Judge platform
50+
await page.goto('/');
51+
52+
// Wait for the dropdown to be visible and click it
53+
const dropdown = await page.getByText('Programming Basics', { exact: true }).click();
54+
55+
// Define the expected menu items in order
56+
const expectedItems = [
57+
'C# Basics',
58+
'Java Basics',
59+
'JS Basics',
60+
'Python Basics',
61+
'PB - More Exercises',
62+
'PB - Exams',
63+
'C++ Basics',
64+
'Go Basics'
65+
];
66+
67+
// Verify each menu item exists and is visible
68+
for (const itemText of expectedItems) {
69+
const item = await page.getByRole('link', { name: itemText });
70+
await expect(item).toBeVisible();
71+
}
72+
});
73+
74+
test('Verify Software Technologies dropdown contents', async ({ page }) => {
75+
// Navigate to the SoftUni Judge platform
76+
await page.goto('/');
77+
78+
// Wait for the dropdown to be visible and click it
79+
const dropdownQaulityAssurance = await page.getByText('Quality Assurance', { exact: true }).click();
80+
const dropdownQaFundamentals = await page.getByText('QA Fundamentals', { exact: true }).click();
81+
const dropdownSoftwareTechnologies = await page.getByText('Software Technologies', { exact: true }).click();
82+
// Define the expected menu items in order
83+
const expectedItems = [
84+
'Software Technologies - Exercises',
85+
'Software Technologies - Exams'
86+
];
87+
88+
// Verify each menu item exists and is visible
89+
for (const itemText of expectedItems) {
90+
const item = await page.getByRole('link', { name: itemText });
91+
await expect(item).toBeVisible();
92+
}
93+
});
94+
95+
test('Verify QA Fundamentals and Manual Testing dropdown contents', async ({ page }) => {
96+
// Navigate to the SoftUni Judge platform
97+
await page.goto('/');
98+
99+
// Wait for the dropdown to be visible and click it
100+
const dropdownQaulityAssurance = await page.getByText('Quality Assurance', { exact: true }).click();
101+
const dropdownQaFundamentals = await page.getByText('QA Fundamentals', { exact: true }).click();
102+
const dropdownSoftwareTechnologies = await page.getByText('QA Fundamentals and Manual Testing', { exact: true }).click();
103+
// Define the expected menu items in order
104+
const expectedItems = [
105+
'QA Fundamentals and Manual Testing - Exams'
106+
];
107+
108+
// Verify each menu item exists and is visible
109+
for (const itemText of expectedItems) {
110+
const item = await page.getByRole('link', { name: itemText });
111+
await expect(item).toBeVisible();
112+
}
113+
});
114+
115+
test('Verify Programming Fundamentals and Unit Testing dropdown contents', async ({ page }) => {
116+
// Navigate to the SoftUni Judge platform
117+
await page.goto('/');
118+
119+
// Wait for the dropdown to be visible and click it
120+
const dropdownQaulityAssurance = await page.getByText('Quality Assurance', { exact: true }).click();
121+
const dropdownQaFundamentals = await page.getByText('Programming for QA', { exact: true }).click();
122+
const dropdownSoftwareTechnologies = await page.getByText('Programming Fundamentals and Unit Testing', { exact: true }).click();
123+
// Define the expected menu items in order
124+
const expectedItems = [
125+
'Programming Fundamentals and Unit Testing - Exercises',
126+
'Programming Fundamentals and Unit Testing - Exams'
127+
];
128+
129+
// Verify each menu item exists and is visible
130+
for (const itemText of expectedItems) {
131+
const item = await page.getByRole('link', { name: itemText });
132+
await expect(item).toBeVisible();
133+
}
134+
});
135+
136+
test('Verify Programming Advanced for QA dropdown contents', async ({ page }) => {
137+
// Navigate to the SoftUni Judge platform
138+
await page.goto('/');
139+
140+
// Wait for the dropdown to be visible and click it
141+
const dropdownQaulityAssurance = await page.getByText('Quality Assurance', { exact: true }).click();
142+
const dropdownQaFundamentals = await page.getByText('Programming for QA', { exact: true }).click();
143+
const dropdownSoftwareTechnologies = await page.getByText('Programming Advanced for QA', { exact: true }).click();
144+
// Define the expected menu items in order
145+
const expectedItems = [
146+
'Programming Advanced for QA - Exercises',
147+
'Programming Advanced for QA - Exams'
148+
];
149+
150+
// Verify each menu item exists and is visible
151+
for (const itemText of expectedItems) {
152+
const item = await page.getByRole('link', { name: itemText });
153+
await expect(item).toBeVisible();
154+
}
155+
});
156+
157+
test('Verify Back-End Test Automation dropdown contents', async ({ page }) => {
158+
// Navigate to the SoftUni Judge platform
159+
await page.goto('/');
160+
161+
// Wait for the dropdown to be visible and click it
162+
const dropdownQaulityAssurance = await page.getByText('Quality Assurance', { exact: true }).click();
163+
const dropdownQaFundamentals = await page.getByText('Back-End Test Automation', { exact: true }).click();
164+
// Define the expected menu items in order
165+
const expectedItems = [
166+
'Back-End Technologies Basics - Exercises',
167+
'Back-End Technologies Basics - Exams'
168+
];
169+
170+
// Verify each menu item exists and is visible
171+
for (const itemText of expectedItems) {
172+
const item = await page.getByRole('link', { name: itemText });
173+
await expect(item).toBeVisible();
174+
}
175+
});
176+
177+
test('Contests page loads successfully', async ({ page }) => {
178+
await page.goto('/contests');
179+
await expect(page).toHaveTitle(/Contests/i);
180+
});
181+
182+
test('Contests list is visible', async ({ page }) => {
183+
await page.goto('/contests');
184+
const contestItems = await page.locator('._contestsListContainer_15pps_57');
185+
await expect(contestItems.first()).toBeVisible();
186+
});
187+
188+
test('Page works without JavaScript', async ({ browser }) => {
189+
const context = await browser.newContext({ javaScriptEnabled: false });
190+
const page = await context.newPage();
191+
await page.goto('/contests');
192+
const bodyText = await page.textContent('body');
193+
expect(bodyText).toContain('You need to enable JavaScript to run this app.');
194+
await context.close();
195+
});

Services/Administration/OJS.Services.Administration.Business/Contests/ContestsBusinessService.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace OJS.Services.Administration.Business.Contests;
2424
using OJS.Services.Common.Models.Contests;
2525
using OJS.Services.Common.Models.Contests.Results;
2626
using OJS.Services.Common.Models.Files;
27+
using OJS.Services.Infrastructure;
2728
using OJS.Services.Infrastructure.Constants;
2829
using OJS.Services.Infrastructure.Exceptions;
2930
using OJS.Services.Infrastructure.Extensions;
@@ -55,6 +56,7 @@ public class ContestsBusinessService : AdministrationOperationService<Contest, i
5556
private readonly IDataService<LecturerInContest> lecturerInContestDataService;
5657
private readonly ISettingsCacheService settingsCache;
5758
private readonly ILogger<ContestsBusinessService> logger;
59+
private readonly IDatesService datesService;
5860

5961
public ContestsBusinessService(
6062
IContestsDataService contestsData,
@@ -72,7 +74,8 @@ public ContestsBusinessService(
7274
IZipArchivesService zipArchivesService,
7375
IDataService<LecturerInContest> lecturerInContestDataService,
7476
ISettingsCacheService settingsCache,
75-
ILogger<ContestsBusinessService> logger)
77+
ILogger<ContestsBusinessService> logger,
78+
IDatesService datesService)
7679
{
7780
this.contestsData = contestsData;
7881
this.ipsData = ipsData;
@@ -90,6 +93,7 @@ public ContestsBusinessService(
9093
this.lecturerInContestDataService = lecturerInContestDataService;
9194
this.settingsCache = settingsCache;
9295
this.logger = logger;
96+
this.datesService = datesService;
9397
}
9498

9599
public async Task<IEnumerable<LecturerInContestActionsModel>> GetForLecturerInContest(string userId)
@@ -451,9 +455,14 @@ public async Task AdjustLimitBetweenSubmissions(WorkersBusyRatioServiceModel mod
451455
ratioFactor,
452456
queueFactor);
453457

458+
var now = this.datesService.GetUtcNow();
459+
var cutoff = now.AddHours(-settings.HoursToKeepAutoAdjustedLimitBetweenSubmissionsAfterContestEnd);
460+
461+
// Update all active contests and contests that have ended in the last N hours (to post-reduce possibly increased limit)
454462
await this.contestsData
455463
.GetAllVisible()
456-
.Where(c => c.AutoChangeLimitBetweenSubmissions)
464+
.Where(c => c.AutoChangeLimitBetweenSubmissions &&
465+
(c.EndTime >= now || (c.EndTime <= now && c.EndTime >= cutoff)))
457466
.UpdateFromQueryAsync(c => new Contest
458467
{
459468
// Will be clamped between 0 and max allowed limit

Services/Administration/OJS.Services.Administration.Data/Implementations/ContestsDataService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ namespace OJS.Services.Administration.Data.Implementations
1313
using OJS.Services.Common.Data;
1414
using OJS.Services.Common.Models.Contests;
1515
using OJS.Services.Common.Models.Users;
16+
using OJS.Services.Infrastructure;
1617
using OJS.Services.Infrastructure.Extensions;
1718

18-
public class ContestsDataService(OjsDbContext db, IContestsActivityService activityService)
19+
public class ContestsDataService(OjsDbContext db, IContestsActivityService activityService, IDatesService datesService)
1920
: AdministrationDataService<Contest>(db), IContestsDataService
2021
{
2122
public Task<Contest?> GetByIdWithProblems(int id)
@@ -40,7 +41,7 @@ public class ContestsDataService(OjsDbContext db, IContestsActivityService activ
4041
.FirstOrDefaultAsync();
4142

4243
public IQueryable<Contest> GetAllVisible()
43-
=> this.GetQuery(c => c.IsVisible);
44+
=> this.GetQuery(c => c.IsVisible || c.VisibleFrom <= datesService.GetUtcNow());
4445

4546
public IQueryable<Contest> GetAllByLecturer(string? lecturerId)
4647
=> this.GetQuery(c =>

Services/Administration/OJS.Services.Administration.Models/ContestLimitBetweenSubmissionsAdjustSettings.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ public class ContestLimitBetweenSubmissionsAdjustSettings
3737
/// e.g. for 3.0, if the queue is 3 times larger than the number of workers, it is considered to be critically full.
3838
/// </summary>
3939
public double QueueLenghtCriticalThresholdMultiplier { get; set; } = 3;
40+
41+
/// <summary>
42+
/// The number of hours after the contest end time in which the system will keep auto adjusting the limit between submissions.
43+
/// </summary>
44+
public int HoursToKeepAutoAdjustedLimitBetweenSubmissionsAfterContestEnd { get; set; } = 24;
4045
}

Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/BaseExecutionStrategy.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public async Task<IExecutionResult<TResult>> SafeExecute<TInput, TResult>(
4040
CancellationToken cancellationToken = default)
4141
where TResult : ISingleCodeRunResult, new()
4242
{
43+
this.SanitizeContent(executionContext);
4344
var submissionId = (int)submission.Id;
4445
this.WorkingDirectory = DirectoryHelpers.CreateTempDirectoryForExecutionStrategy();
4546
this.Logger.LogExecutionStrategyCreatedWorkingDirectory(this.Type.ToString(), this.WorkingDirectory, submissionId);
@@ -114,6 +115,11 @@ protected virtual async Task<IExecutionResult<TResult>> InternalExecute<TInput,
114115
protected virtual string PreprocessCode<TInput>(
115116
IExecutionContext<TInput> executionContext)
116117
=> executionContext.Code;
118+
119+
protected virtual void SanitizeContent<TInput>(
120+
IExecutionContext<TInput> executionContext)
121+
{
122+
}
117123
}
118124

119125
public abstract record BaseExecutionStrategySettings : IExecutionStrategySettings

Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/CPlusPlus/CPlusPlusCompileExecuteAndCheckExecutionStrategy.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using Microsoft.Extensions.Logging;
44
using OJS.Workers.Common;
55
using OJS.Workers.Compilers;
6-
using OJS.Workers.ExecutionStrategies.Extensions;
6+
using OJS.Workers.ExecutionStrategies.CodeSanitizers;
77
using OJS.Workers.ExecutionStrategies.Models;
88
using OJS.Workers.Executors;
99

@@ -25,8 +25,6 @@ protected override Task<IExecutionResult<TestResult>> ExecuteAgainstTestsInput(
2525
IExecutionResult<TestResult> result,
2626
CancellationToken cancellationToken = default)
2727
{
28-
executionContext.SanitizeContent();
29-
3028
return this.CompileExecuteAndCheck(
3129
executionContext,
3230
result,
@@ -35,6 +33,12 @@ protected override Task<IExecutionResult<TestResult>> ExecuteAgainstTestsInput(
3533
dependOnExitCodeForRunTimeError: true,
3634
cancellationToken: cancellationToken);
3735
}
36+
37+
protected override void SanitizeContent<TInput>(IExecutionContext<TInput> executionContext)
38+
{
39+
base.SanitizeContent(executionContext);
40+
new CPlusPlusSanitizer().Sanitize(executionContext);
41+
}
3842
}
3943

4044
public record CPlusPlusCompileExecuteAndCheckExecutionStrategySettings(int BaseTimeUsed, int BaseMemoryUsed)

Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/CPlusPlus/CPlusPlusZipFileExecutionStrategy.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
using OJS.Workers.Common;
1111
using OJS.Workers.Common.Helpers;
1212
using OJS.Workers.Compilers;
13-
using OJS.Workers.ExecutionStrategies.Extensions;
13+
using OJS.Workers.ExecutionStrategies.CodeSanitizers;
1414
using OJS.Workers.ExecutionStrategies.Models;
1515
using OJS.Workers.Executors;
1616

@@ -34,8 +34,6 @@ protected override async Task<IExecutionResult<TestResult>> ExecuteAgainstTestsI
3434
IExecutionResult<TestResult> result,
3535
CancellationToken cancellationToken = default)
3636
{
37-
executionContext.SanitizeContent();
38-
3937
var submissionDestination = Path.Combine(this.WorkingDirectory, ZippedSubmissionName);
4038
File.WriteAllBytes(submissionDestination, executionContext.FileContent);
4139
FileHelpers.RemoveFilesFromZip(submissionDestination, RemoveMacFolderPattern);
@@ -60,7 +58,7 @@ protected override async Task<IExecutionResult<TestResult>> ExecuteAgainstTestsI
6058
return result;
6159
}
6260

63-
var executor = this.CreateRestrictedExecutor();
61+
var executor = this.CreateRestrictedExecutor();
6462

6563
var checker = executionContext.Input.GetChecker();
6664

@@ -108,6 +106,12 @@ private IEnumerable<string> ExtractTaskSkeleton(string executionContextTaskSkele
108106

109107
return pathsToHeadersAndCppFiles;
110108
}
109+
110+
protected override void SanitizeContent<TInput>(IExecutionContext<TInput> executionContext)
111+
{
112+
base.SanitizeContent(executionContext);
113+
new CPlusPlusSanitizer().Sanitize(executionContext);
114+
}
111115
}
112116

113117
public record CPlusPlusZipFileExecutionStrategySettings(

0 commit comments

Comments
 (0)