diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contest-categories/contestCategoriesGridColumns.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contest-categories/contestCategoriesGridColumns.tsx index 988e8c0a8e..1f42d6fa78 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contest-categories/contestCategoriesGridColumns.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contest-categories/contestCategoriesGridColumns.tsx @@ -70,7 +70,7 @@ const categoriesFilterableColumns: AdministrationGridColDef[] = [ categoryId: params.row.id, categoryName: params.row.name, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, @@ -100,7 +100,7 @@ const categoriesFilterableColumns: AdministrationGridColDef[] = [ categoryId: params.row.parentId, categoryName: params.row.parent, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contests/contestsGridColumns.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contests/contestsGridColumns.tsx index 1ce9a6a8fa..3cb4283fc0 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contests/contestsGridColumns.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/contests/contestsGridColumns.tsx @@ -53,7 +53,7 @@ const contestFilterableColumns: AdministrationGridColDef[] = [ contestId: params.row.id, contestName: params.row.name, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, @@ -73,7 +73,7 @@ const contestFilterableColumns: AdministrationGridColDef[] = [ contestId: params.row.id, contestName: params.row.name, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/exam-groups/examGroupsGridColumns.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/exam-groups/examGroupsGridColumns.tsx index 3853035f98..964df2eb53 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/exam-groups/examGroupsGridColumns.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/exam-groups/examGroupsGridColumns.tsx @@ -50,7 +50,7 @@ const examGroupsFilterableColumns: AdministrationGridColDef[] = [ contestId: params.row.contestId, contestName: params.row.contestName, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/participants/participantsGridColumns.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/participants/participantsGridColumns.tsx index af93a9bf08..b221d488a6 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/participants/participantsGridColumns.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/participants/participantsGridColumns.tsx @@ -61,7 +61,7 @@ const participantsFilteringColumns: AdministrationGridColDef[] = [ contestId: params.row.contestId, contestName: params.row.contestName, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, diff --git a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/problems/problemGridColumns.tsx b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/problems/problemGridColumns.tsx index 0228407278..feaddf0d95 100644 --- a/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/problems/problemGridColumns.tsx +++ b/Servers/UI/OJS.Servers.Ui/ClientApp/src/pages/administration-new/problems/problemGridColumns.tsx @@ -79,7 +79,7 @@ const problemFilterableColumns: AdministrationGridColDef[] = [ contestId: params.row.contestId, contestName: params.row.contest, })} - text={params.value.toString()} + text={params.value?.toString()} /> , }, diff --git a/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/Models/JsonExecutionResult.cs b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/Models/JsonExecutionResult.cs index 36ca92e12a..089819400d 100644 --- a/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/Models/JsonExecutionResult.cs +++ b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/Models/JsonExecutionResult.cs @@ -10,6 +10,9 @@ namespace OJS.Workers.ExecutionStrategies.Models public class JsonExecutionResult { private const string InvalidJsonReplace = "]}[},!^@,Invalid,!^@,{]{["; + private const string MissingJsonStructureError = "Invalid console output! Please make sure there are no console.log statements in the solution. The system expects JSON output from Mocha test results."; + private const string MissingPassingFieldError = "Invalid console output! Please make sure there are no console.log statements in the solution. Missing or invalid 'passing' array in test results."; + private const string MissingFailuresFieldError = "Invalid console output! Please make sure there are no console.log statements in the solution. Missing or invalid 'failures' array with 'err.message' fields."; public IList TestErrors { get; set; } @@ -68,7 +71,7 @@ public static JsonExecutionResult Parse(string result, bool forceErrorExtracting } catch { - error = "Invalid console output!"; + error = MissingJsonStructureError; } var testsIndexes = new List(); @@ -80,7 +83,7 @@ public static JsonExecutionResult Parse(string result, bool forceErrorExtracting } catch { - error = "Invalid console output!"; + error = MissingPassingFieldError; } } @@ -97,7 +100,7 @@ public static JsonExecutionResult Parse(string result, bool forceErrorExtracting } catch { - error = "Invalid console output!"; + error = MissingFailuresFieldError; } } diff --git a/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/NodeJsPreprocessExecuteAndRunAllUnitTestsWithMochaExecutionStrategy.cs b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/NodeJsPreprocessExecuteAndRunAllUnitTestsWithMochaExecutionStrategy.cs index feb3b1f205..8b3917c493 100644 --- a/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/NodeJsPreprocessExecuteAndRunAllUnitTestsWithMochaExecutionStrategy.cs +++ b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/NodeJsPreprocessExecuteAndRunAllUnitTestsWithMochaExecutionStrategy.cs @@ -107,23 +107,18 @@ protected override async Task> ExecuteAgainstTestsI protected static string FormatTests(IEnumerable tests, bool isTypeScript) { var formattedTests = new List(); - var testCounter = 1; foreach (var test in tests) { // Use simple sequential test names - var testName = $"Test{testCounter}"; var testContent = test.Input.Trim(); // Format the test with proper it() wrapper var formattedTest = $@" {(isTypeScript ? "// @ts-ignore" : "")} - it('{testName}', function () {{ - {testContent} - }})"; + {testContent}"; formattedTests.Add(formattedTest); - testCounter++; } // Join all formatted tests diff --git a/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/Typescript/TypeScriptProjectMochaUnitTestsExecutionStrategy.cs b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/Typescript/TypeScriptProjectMochaUnitTestsExecutionStrategy.cs index 83d040e68d..7f1b76e64b 100644 --- a/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/Typescript/TypeScriptProjectMochaUnitTestsExecutionStrategy.cs +++ b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/Typescript/TypeScriptProjectMochaUnitTestsExecutionStrategy.cs @@ -23,12 +23,24 @@ protected override async Task> ExecuteAgainstTestsI { SaveZipSubmission(executionContext.FileContent, this.WorkingDirectory); + var esBuildExecutionArguments = new[] + { + "src/index.ts", + "--bundle", + "--platform=node", + "--format=cjs", + "--target=node21", + "--packages=external", + "--drop:console", + "--outfile=dist/app.bundle.js" + }; + var executor = this.CreateStandardExecutor(); var bundleResult = await executor.Execute( this.Settings.EsBuildModulePath, executionContext.TimeLimit, executionContext.MemoryLimit, - executionArguments: ["src/index.ts", "--bundle", "--platform=node", "--format=cjs", "--target=node21", "--packages=external", "--outfile=dist/app.bundle.js"], + executionArguments: esBuildExecutionArguments, workingDirectory: this.WorkingDirectory, cancellationToken: cancellationToken); diff --git a/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs b/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs index 31be3e37a0..219400c721 100644 --- a/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs +++ b/Services/UI/OJS.Services.Ui.Business/Implementations/ContestsBusinessService.cs @@ -13,6 +13,7 @@ namespace OJS.Services.Ui.Business.Implementations; using OJS.Services.Infrastructure.Constants; using OJS.Services.Infrastructure.Extensions; using OJS.Services.Infrastructure.Models; +using OJS.Services.Infrastructure; using OJS.Services.Ui.Business.Cache; using OJS.Services.Ui.Business.Validations.Implementations.Contests; using OJS.Services.Ui.Data; @@ -34,7 +35,8 @@ public class ContestsBusinessService( IContestParticipantsCacheService contestParticipantsCacheService, IContestsCacheService contestsCacheService, ILecturersInContestsCacheService lecturersInContestsCache, - IContestDetailsValidationService contestDetailsValidationService) + IContestDetailsValidationService contestDetailsValidationService, + IDatesService datesService) : IContestsBusinessService { public async Task> GetContestDetails(int id) @@ -328,6 +330,8 @@ public async Task GetSearchContestsByName( .MapCollection() .ToPagedListAsync(model.PageNumber, model.ItemsPerPage); + this.SetContestVisibility(searchContests); + modelResult.Contests = searchContests; modelResult.TotalContestsCount = allContestsQueryable.Count(); @@ -344,6 +348,8 @@ public async Task> GetAllByFiltersAnd var pagedContests = await contestsData.GetAllAsPageByFiltersAndSorting(model, includeHidden); + this.SetContestVisibility(pagedContests.Items); + var participantResultsByContest = new Dictionary>(); if (user.IsAuthenticated) { @@ -390,6 +396,8 @@ public async Task> GetParticipatedByU participatedContests, sortAndFilterModel); + this.SetContestVisibility(participatedContestsInPage.Items); + var participantResultsByContest = new Dictionary>(); var loggedInUser = userProviderService.GetCurrentUser(); @@ -499,10 +507,16 @@ await pagedContests.Items.ForEachAsync(c => .ToListAsync(); public async Task> GetAllParticipatedContests(string username) - => await contestsData - .GetLatestForParticipantByUsername(username) - .MapCollection() - .ToListAsync(); + { + var contests = await contestsData + .GetLatestForParticipantByUsername(username) + .MapCollection() + .ToListAsync(); + + this.SetContestVisibility(contests); + + return contests; + } private static async Task>> MapParticipationResultsToContestsInPage( IQueryable participants) @@ -565,4 +579,13 @@ private async Task GetNestedFilterCategoriesIfAny(Co official, isUserAdminOrLecturerInContest); } + + private void SetContestVisibility(IEnumerable contests) + { + var now = datesService.GetUtcNow(); + foreach (var contest in contests) + { + contest.IsVisible = contest.IsVisible || contest.VisibleFrom <= now; + } + } } \ No newline at end of file diff --git a/Services/UI/OJS.Services.Ui.Models/Contests/ContestForListingServiceModel.cs b/Services/UI/OJS.Services.Ui.Models/Contests/ContestForListingServiceModel.cs index ed630a9ecf..c82be95875 100644 --- a/Services/UI/OJS.Services.Ui.Models/Contests/ContestForListingServiceModel.cs +++ b/Services/UI/OJS.Services.Ui.Models/Contests/ContestForListingServiceModel.cs @@ -84,9 +84,6 @@ public void RegisterMappings(IProfileExpression configuration) .SelectMany(pg => pg.Problems) .Where(x => !x.IsDeleted) .Sum(pr => pr.MaximumPoints))) - .ForMember( - c => c.IsVisible, - opt => opt.MapFrom(s => s.IsVisible || s.VisibleFrom <= DateTime.UtcNow)) // For online contests: // In a problem group with multiple problems, compete points are derived from a single problem, // unlike practice mode where points can be accumulated from all problems across groups.