From bbb823355b6e35ebe4f2abc8dfb92c306b3491dc Mon Sep 17 00:00:00 2001 From: Georgi Georgiev Date: Mon, 22 Dec 2025 10:30:47 +0200 Subject: [PATCH 1/2] 1748 support typescript exam projects with bundling step before running mochasinon tests (#1671) 1748 support typescript exam projects with bundling step before running mochasinon tests (#1671) closes https://github.com/SoftUni-Internal/exam-systems-issues/issues/1748 --- Docker/worker_base/js/v20/package.json | 3 +- .../OJS.Servers.Worker/appsettings.json | 1 + .../Models/ExecutionStrategyType.cs | 1 + ...tProjectMochaUnitTestsExecutionStrategy.cs | 68 +++++++++++++++++++ .../ExecutionStrategyFactory.cs | 7 ++ .../ExecutionStrategySettingsProvider.cs | 13 ++++ .../Configuration/OjsWorkersConfig.cs | 3 + 7 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/Typescript/TypeScriptProjectMochaUnitTestsExecutionStrategy.cs diff --git a/Docker/worker_base/js/v20/package.json b/Docker/worker_base/js/v20/package.json index fdd34dc6dd..8170d1fd70 100644 --- a/Docker/worker_base/js/v20/package.json +++ b/Docker/worker_base/js/v20/package.json @@ -23,6 +23,7 @@ "sinon": "^17.0.1", "sinon-chai": "^3.7.0", "underscore": "^1.13.6", - "http-server": "14.1.1" + "http-server": "14.1.1", + "esbuild": "^0.27.1" } } diff --git a/Servers/Worker/OJS.Servers.Worker/appsettings.json b/Servers/Worker/OJS.Servers.Worker/appsettings.json index 9a05afdf64..d8b77d1272 100644 --- a/Servers/Worker/OJS.Servers.Worker/appsettings.json +++ b/Servers/Worker/OJS.Servers.Worker/appsettings.json @@ -66,6 +66,7 @@ "UnderscoreModulePath": "{nodeResourcesPath}/node_modules/underscore", "MochaModulePath": "{nodeResourcesPath}/node_modules/mocha/bin/_mocha", "ChaiModulePath": "{nodeResourcesPath}/node_modules/chai", + "ESBuildModulePath": "{nodeResourcesPath}/node_modules/esbuild/bin/esbuild", "PlaywrightModulePath": "{nodeResourcesPath}/node_modules/playwright", "PlaywrightChromiumModulePath": "{nodeResourcesPath}/node_modules/playwright-chromium", "JsDomModulePath": "{nodeResourcesPath}/node_modules/jsdom", diff --git a/Services/Common/OJS.Workers/OJS.Workers.Common/Models/ExecutionStrategyType.cs b/Services/Common/OJS.Workers/OJS.Workers.Common/Models/ExecutionStrategyType.cs index 5b153834c0..5d87fe9bc5 100644 --- a/Services/Common/OJS.Workers/OJS.Workers.Common/Models/ExecutionStrategyType.cs +++ b/Services/Common/OJS.Workers/OJS.Workers.Common/Models/ExecutionStrategyType.cs @@ -76,5 +76,6 @@ public enum ExecutionStrategyType DotNetCore8UnitTestsExecutionStrategy = 79, PythonDjangoOrmParallelExecutionStrategy = 80, NodeJsV20PreprocessExecuteAndRunAllUnitTestsWithMocha = 81, + TypeScriptV20ProjectMochaUnitTestsExecutionStrategy = 82, } } 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 new file mode 100644 index 0000000000..83d040e68d --- /dev/null +++ b/Services/Common/OJS.Workers/OJS.Workers.ExecutionStrategies/NodeJs/Typescript/TypeScriptProjectMochaUnitTestsExecutionStrategy.cs @@ -0,0 +1,68 @@ +namespace OJS.Workers.ExecutionStrategies.NodeJs.Typescript; + +using Common; +using Common.Helpers; +using Executors; +using Microsoft.Extensions.Logging; +using Models; + +public class TypeScriptProjectMochaUnitTestsExecutionStrategy( + IOjsSubmission submission, + IProcessExecutorFactory processExecutorFactory, + IExecutionStrategySettingsProvider settingsProvider, + ILogger> logger) + : NodeJsPreprocessExecuteAndRunAllUnitTestsWithMochaExecutionStrategy( + submission, + processExecutorFactory, + settingsProvider, + logger) + where TSettings : TypeScriptProjectMochaUnitTestsExecutionStrategySettings +{ + protected override async Task> ExecuteAgainstTestsInput(IExecutionContext executionContext, IExecutionResult result, + CancellationToken cancellationToken = default) + { + SaveZipSubmission(executionContext.FileContent, this.WorkingDirectory); + + 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"], + workingDirectory: this.WorkingDirectory, + cancellationToken: cancellationToken); + + if (bundleResult.ExitCode != 0) + { + result.IsCompiledSuccessfully = false; + result.CompilerComment = bundleResult.ErrorOutput; + return result; + } + + var bundlePath = FileHelpers.BuildPath(this.WorkingDirectory, "dist", "app.bundle.js"); + + executionContext.Code = FileHelpers.ReadFile(bundlePath); + + return await base.ExecuteAgainstTestsInput(executionContext, result, cancellationToken); + } +} + +public record TypeScriptProjectMochaUnitTestsExecutionStrategySettings( + int BaseTimeUsed, + int BaseMemoryUsed, + string NodeJsExecutablePath, + string UnderscoreModulePath, + string MochaModulePath, + string ChaiModulePath, + string SinonModulePath, + string SinonChaiModulePath, + string EsBuildModulePath) + : NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategySettings( + BaseTimeUsed, + BaseMemoryUsed, + NodeJsExecutablePath, + UnderscoreModulePath, + MochaModulePath, + ChaiModulePath, + SinonModulePath, + SinonChaiModulePath); \ No newline at end of file diff --git a/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategyFactory.cs b/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategyFactory.cs index fcc44f43c2..274d5fae57 100644 --- a/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategyFactory.cs +++ b/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategyFactory.cs @@ -190,6 +190,13 @@ public IExecutionStrategy CreateExecutionStrategy(IOjsSubmission submission) loggerFactory.CreateStrategyLogger>(submissionId, verbosely, logFileMaxBytes), compilerFactory); break; + case ExecutionStrategyType.TypeScriptV20ProjectMochaUnitTestsExecutionStrategy: + executionStrategy = new TypeScriptProjectMochaUnitTestsExecutionStrategy( + submission, + processExecutorFactory, + executionStrategySettingsProvider, + loggerFactory.CreateStrategyLogger>(submissionId, verbosely, logFileMaxBytes)); + break; case ExecutionStrategyType.NodeJsPreprocessExecuteAndRunUnitTestsWithMocha: case ExecutionStrategyType.NodeJsV20PreprocessExecuteAndRunUnitTestsWithMocha: executionStrategy = new NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategy( diff --git a/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategySettingsProvider.cs b/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategySettingsProvider.cs index 2f0298d8cd..30441332ac 100644 --- a/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategySettingsProvider.cs +++ b/Services/Worker/OJS.Services.Worker.Business/Implementations/ExecutionStrategySettingsProvider.cs @@ -70,6 +70,19 @@ ExecutionStrategyType.NodeJsPreprocessExecuteAndCheck or this.GetNodeResourcePath(executionStrategyType, this.settings.SinonModulePath), this.GetNodeResourcePath(executionStrategyType, this.settings.SinonChaiModulePath)) + as TSettings, + ExecutionStrategyType.TypeScriptV20ProjectMochaUnitTestsExecutionStrategy => new + TypeScriptProjectMochaUnitTestsExecutionStrategySettings( + GetBaseTimeUsed(submission, this.settings.NodeJsBaseTimeUsedInMilliseconds * 2), + GetBaseMemoryUsed(submission, this.settings.NodeJsBaseMemoryUsedInBytes), + this.GetNodeJsExecutablePath(executionStrategyType), + this.GetNodeResourcePath(executionStrategyType, this.settings.UnderscoreModulePath), + this.GetNodeResourcePath(executionStrategyType, this.settings.MochaModulePath), + this.GetNodeResourcePath(executionStrategyType, this.settings.ChaiModulePath), + this.GetNodeResourcePath(executionStrategyType, this.settings.SinonModulePath), + this.GetNodeResourcePath(executionStrategyType, this.settings.SinonChaiModulePath), + this.GetNodeResourcePath(executionStrategyType, this.settings.EsBuildModulePath)) + as TSettings, ExecutionStrategyType.JavaPreprocessCompileExecuteAndCheck or ExecutionStrategyType.Java21PreprocessCompileExecuteAndCheck => new diff --git a/Services/Worker/OJS.Services.Worker.Models/Configuration/OjsWorkersConfig.cs b/Services/Worker/OJS.Services.Worker.Models/Configuration/OjsWorkersConfig.cs index 9abf86e1e9..4c6d85784e 100644 --- a/Services/Worker/OJS.Services.Worker.Models/Configuration/OjsWorkersConfig.cs +++ b/Services/Worker/OJS.Services.Worker.Models/Configuration/OjsWorkersConfig.cs @@ -127,6 +127,9 @@ public class OjsWorkersConfig : BaseConfig [Required] public string SinonChaiModulePath { get; set; } = string.Empty; + [Required] + public string EsBuildModulePath { get; set; } = string.Empty; + [Required] public string UnderscoreModulePath { get; set; } = string.Empty; From 8616a0877e92bdf75a3dd1cf6870016c16edecfc Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0LU3CP\\User1" Date: Tue, 24 Mar 2026 16:13:19 +0200 Subject: [PATCH 2/2] Fixed value toString throws for text in columns https://github.com/SoftUni-Internal/exam-systems-issues/issues/1750 --- .../contest-categories/contestCategoriesGridColumns.tsx | 4 ++-- .../pages/administration-new/contests/contestsGridColumns.tsx | 4 ++-- .../administration-new/exam-groups/examGroupsGridColumns.tsx | 2 +- .../participants/participantsGridColumns.tsx | 2 +- .../pages/administration-new/problems/problemGridColumns.tsx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) 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()} /> , },