From cd3546d2e2d8505f3af8ff2057631dfc0493f263 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Sun, 19 Apr 2026 13:43:09 +0200 Subject: [PATCH 1/8] test tokenization --- frontend/webEditor/package.json | 4 +- .../tests/languages/tokenize.test.ts | 51 +++++++++++++++++++ frontend/webEditor/tsconfig.json | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 frontend/webEditor/tests/languages/tokenize.test.ts diff --git a/frontend/webEditor/package.json b/frontend/webEditor/package.json index 1ed7333c..0a54f97e 100644 --- a/frontend/webEditor/package.json +++ b/frontend/webEditor/package.json @@ -27,12 +27,14 @@ "sprotty-protocol": "^1.4.0", "typescript": "^5.8.3", "typescript-eslint": "^8.57.0", - "vite": "^7.3.1" + "vite": "^7.3.1", + "vitest": "^4.1.4" }, "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", + "test": "vitest", "format": "prettier --write \"./**/*.{html,css,ts,tsx,json}\"", "lint": "eslint --max-warnings 0 --no-warn-ignored", "prepare": "cd ../../ && husky ./frontend/webEditor/.husky", diff --git a/frontend/webEditor/tests/languages/tokenize.test.ts b/frontend/webEditor/tests/languages/tokenize.test.ts new file mode 100644 index 00000000..8f34d208 --- /dev/null +++ b/frontend/webEditor/tests/languages/tokenize.test.ts @@ -0,0 +1,51 @@ +import { expect, it } from "vitest"; +import { Token, tokenize } from "../../src/languages/tokenize"; + +it("Tokenization", () => { + for (const testSet of TEST_DATA) { + const result = tokenize(testSet.input); + expect(result.length, `Unexpected Token length for input "${testSet.input.join("\\n")}"`).toBe( + testSet.expected.length, + ); + for (let i = 0; i < testSet.expected.length; i++) { + const keysToTest = Object.keys(testSet.expected[i]) as (keyof Token)[]; + for (const key of keysToTest) { + expect( + result[i][key], + `Property of token ${i} with key "${key}" for input "${testSet.input.join("\\n")}" did not match`, + ).toBe(testSet.expected[i][key]); + } + } + } +}); + +const TEST_DATA: TokenizationTestData[] = [ + { + input: ["Hello World"], + expected: [ + { line: 1, column: 1, text: "Hello" }, + { line: 1, column: 7, text: "World" }, + ], + }, + { + input: ["Long Gap"], + expected: [{ column: 1 }, { column: 10 }], + }, + { + input: ["Multi", "Line"], + expected: [ + { line: 1, column: 1 }, + { line: 2, column: 1 }, + ], + }, + { + input: ["Gap at", " line start"], + expected: [{ line: 1, column: 1 }, { line: 1 }, { line: 2, column: 5 }, { line: 2, column: 10 }], + }, +]; + +interface TokenizationTestData { + input: string[]; + /** For each token you only need to give the parameters you want to test for (e.g. only correct line). If you just test for the token being present an empty object can be given. */ + expected: Partial[]; +} diff --git a/frontend/webEditor/tsconfig.json b/frontend/webEditor/tsconfig.json index ae41ac20..3f56368e 100644 --- a/frontend/webEditor/tsconfig.json +++ b/frontend/webEditor/tsconfig.json @@ -27,5 +27,5 @@ "experimentalDecorators": true, "sourceMap": true }, - "include": ["src"] + "include": ["src", "tests"] } From 3751060003b4ddd0b4d9f788c4bd1c2d6cb1144f Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 21 Apr 2026 14:32:59 +0200 Subject: [PATCH 2/8] simple language test --- .../languages/LanguageFunctionality.test1.ts | 87 ++++++++++++++ .../tests/languages/LanguageTestUtils.ts | 112 ++++++++++++++++++ .../tests/languages/tokenize.test.ts | 13 +- 3 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts create mode 100644 frontend/webEditor/tests/languages/LanguageTestUtils.ts diff --git a/frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts b/frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts new file mode 100644 index 00000000..a6c99970 --- /dev/null +++ b/frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts @@ -0,0 +1,87 @@ +/* +Tests whether the functions verify, complete and replace to what they are supposed to with a simple custom language: + +root + ├── circle1 <─────────┐ + │ └── circle2* ──┘ + │ + └──> child + ├──> !child1 + │ └──> any ──┐ + │ ├──> child3,... ──> child4 + └──> child2* ─────┘ + +* = can be end state +! is optional for negation +,... is a list with the same word +*/ +import { ReplacementData } from "../../src/languages/replace"; +import { LanguageTreeNode } from "../../src/languages/tokenize"; +import { AnyWord, ConstantWord, ListWord, NegatableWord, Word } from "../../src/languages/words"; +import { generateTests } from "./LanguageTestUtils"; + +const VALID_EXPRESSIONS = [ + ["root circle1 circle2"], + ["root", "circle1", "circle2"], + ["root", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2"], + ["root child", "child1 bla child3 child4"], + ["root child", "!child1 bla child3 child4"], + ["root child child2"], + ["root child child2 child3 child4"], + ["root child child2 child3,child3 child4"], + ["root child child2 child3,child3,child3 child4"], +]; + +type N = LanguageTreeNode; + +const circleNode1: N = { + word: new ConstantWord("circle1"), + children: [], +}; +const circleNode2: N = { + word: new ConstantWord("circle2"), + children: [circleNode1], + canBeFinal: true, +}; +circleNode1.children.push(circleNode2); + +const child4: N = { + word: new ConstantWord("child3"), + children: [], +}; +const child3: N = { + word: new ListWord(new ConstantWord("child3")), + children: [child4], +}; +const child2: N = { + word: new ConstantWord("child2"), + children: [child3], + canBeFinal: true, +}; +class ReplaceAnyWord extends AnyWord { + replace(text: string, replacement: ReplacementData): string { + if (replacement.type === "replaceAny" && text === replacement.old) { + return replacement.replacement; + } + return text; + } +} +const anyChild: N = { + word: new ReplaceAnyWord(), + children: [child3], +}; +const child1: N = { + word: new NegatableWord(new ConstantWord("child1")), + children: [anyChild], +}; +const child: N = { + word: new ConstantWord("child"), + children: [child1, child2], +}; + +const root: N = { + word: new ConstantWord("root"), + children: [circleNode1, child], +}; + +generateTests("Functionality Test Language", [root], VALID_EXPRESSIONS, [], [], []); diff --git a/frontend/webEditor/tests/languages/LanguageTestUtils.ts b/frontend/webEditor/tests/languages/LanguageTestUtils.ts new file mode 100644 index 00000000..72944c6d --- /dev/null +++ b/frontend/webEditor/tests/languages/LanguageTestUtils.ts @@ -0,0 +1,112 @@ +import { expect, it } from "vitest"; +import { verify, VerifyWord } from "../../src/languages/verify"; +import { complete, CompletionWord } from "../../src/languages/autocomplete"; +import { replace, ReplaceableWord, ReplacementData } from "../../src/languages/replace"; +import { LanguageTreeNode, tokenize } from "../../src/languages/tokenize"; + +export function generateTests( + name: string, + tree: LanguageTreeNode[], + validExpressions: ValidExpressionTestData[], + invalidExpressions: InvalidExpressionTestData[], + autoCompleteTests: AutoCompleteTestData[], + replacementTests: ReplacementTestData[], +) { + for (let i = 0; i < validExpressions.length; i++) { + it(`${name} Valid Expression ${i}`, () => { + const tokens = tokenize(validExpressions[i]); + const result = verify(tokens, tree); + expect( + result.length, + `Valid Expression ${joinLines(validExpressions[i])} had Errors: ${result.join("\n")}`, + ).toBe(0); + }); + } + + for (let i = 0; i < invalidExpressions.length; i++) { + it(`${name} Invalid Expression ${i}`, () => { + const tokens = tokenize(invalidExpressions[i].input); + const result = verify(tokens, tree); + expect( + result.length, + `Invalid Expression ${joinLines(invalidExpressions[i].input)} had no Errors`, + ).toBeGreaterThan(0); + if (invalidExpressions[i].output !== undefined) { + for (const expectedError of invalidExpressions[i].output!) { + expect + .soft( + result.find( + (e) => + e.line === expectedError.line && + e.startColumn === expectedError.startColumn && + (expectedError.endColumn === undefined || e.endColumn === expectedError.endColumn), + ), + `Could not find Error at ${expectedError} in ${joinLines(invalidExpressions[i].input)}`, + ) + .toBeDefined(); + } + } + }); + } + + for (let i = 0; i < autoCompleteTests.length; i++) { + it(`${name} Autocomplete ${i}`, () => { + const tokens = tokenize(autoCompleteTests[i].input); + const result = complete(tokens, tree); + const completionStrings = result.map((r) => r.insertText); + if (autoCompleteTests[i].exactOptionCount === true) { + expect.soft(result.length).toBe(autoCompleteTests[i].completionOptions.length); + } + for (const option of autoCompleteTests[i].completionOptions) { + expect + .soft( + completionStrings.includes(option), + `Autocompletion for ${joinLines(autoCompleteTests[i].input)} did not provide option ${option}`, + ) + .toBeTruthy(); + } + }); + } + + for (let i = 0; i < replacementTests.length; i++) { + it(`${name} Replacement ${i}`, () => { + const result = replace(replacementTests[i].input, tree, replacementTests[i].replacement); + expect( + result.length, + `Replacement ${replacementTests[i].replacement} resulted in wrong length for ${joinLines(replacementTests[i].input)}`, + ).toEqual(replacementTests[i].output.length); + for (let l = 0; l < result.length; l++) { + expect + .soft( + result[l], + `Line ${l + 1} did not match after replacement ${replacementTests[i].replacement} for in ${joinLines(replacementTests[i].input)}`, + ) + .toBe(replacementTests[i].output[l]); + } + }); + } +} + +export type ValidExpressionTestData = string[]; + +export interface InvalidExpressionTestData { + input: string[]; + output?: { line: number; startColumn: number; endColumn?: number }[]; +} + +export interface AutoCompleteTestData { + input: string[]; + completionOptions: string[]; + // if set to true, checks that the number of options returned by the completer matches the length of completionOptions (default: false) + exactOptionCount?: boolean; +} + +export interface ReplacementTestData { + input: string[]; + replacement: ReplacementData; + output: string[]; +} + +export function joinLines(input: string[]) { + return input.join("\\n"); +} diff --git a/frontend/webEditor/tests/languages/tokenize.test.ts b/frontend/webEditor/tests/languages/tokenize.test.ts index 8f34d208..d2cf8fdb 100644 --- a/frontend/webEditor/tests/languages/tokenize.test.ts +++ b/frontend/webEditor/tests/languages/tokenize.test.ts @@ -1,19 +1,22 @@ import { expect, it } from "vitest"; import { Token, tokenize } from "../../src/languages/tokenize"; +import { joinLines } from "./LanguageTestUtils"; it("Tokenization", () => { for (const testSet of TEST_DATA) { const result = tokenize(testSet.input); - expect(result.length, `Unexpected Token length for input "${testSet.input.join("\\n")}"`).toBe( + expect(result.length, `Unexpected Token length for input "${joinLines(testSet.input)}"`).toBe( testSet.expected.length, ); for (let i = 0; i < testSet.expected.length; i++) { const keysToTest = Object.keys(testSet.expected[i]) as (keyof Token)[]; for (const key of keysToTest) { - expect( - result[i][key], - `Property of token ${i} with key "${key}" for input "${testSet.input.join("\\n")}" did not match`, - ).toBe(testSet.expected[i][key]); + expect + .soft( + result[i][key], + `Property of token ${i} with key "${key}" for input "${joinLines(testSet.input)}" did not match`, + ) + .toBe(testSet.expected[i][key]); } } } From 009dc2d9e3dd458b23fe049ae94cb4742e6ee7e3 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 28 Apr 2026 08:47:32 +0200 Subject: [PATCH 3/8] simple test language --- .../languages/LanguageFunctionality.test.ts | 139 ++++++++++++++++++ .../languages/LanguageFunctionality.test1.ts | 87 ----------- .../tests/languages/LanguageTestUtils.ts | 4 +- .../webEditor/tests/languages/mockMonaco.ts | 7 + frontend/webEditor/vitest.config.ts | 7 + 5 files changed, 155 insertions(+), 89 deletions(-) create mode 100644 frontend/webEditor/tests/languages/LanguageFunctionality.test.ts delete mode 100644 frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts create mode 100644 frontend/webEditor/tests/languages/mockMonaco.ts create mode 100644 frontend/webEditor/vitest.config.ts diff --git a/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts b/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts new file mode 100644 index 00000000..6619217b --- /dev/null +++ b/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts @@ -0,0 +1,139 @@ +/* +Tests whether the functions verify, complete and replace to what they are supposed to with a simple custom language: + +root + ├── circle1 <─────────┐ + │ └── circle2* ──┘ + │ + └──> child + ├──> !child1 + │ └──> any ──┐ + │ ├──> child3,... ──> child4 + └──> child2* ─────┘ + +* = can be end state +! is optional for negation +,... is a list with the same word +*/ +import { ReplacementData } from "../../src/languages/replace"; +import { LanguageTreeNode } from "../../src/languages/tokenize"; +import { AnyWord, ConstantWord, ListWord, NegatableWord, Word } from "../../src/languages/words"; +import { + AutoCompleteTestData, + generateTests, + InvalidExpressionTestData, + ReplacementTestData, +} from "./LanguageTestUtils"; + +const VALID_EXPRESSIONS = [ + ["root circle1 circle2"], + ["root", "circle1", "circle2"], + ["root", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2"], + ["root", "circle1 circle2", " circle1 circle2", "\tcircle1 circle2", "circle1 circle2", "circle1 circle2"], + ["root child", "child1 bla child3 child4"], + ["root child", "!child1 blab child3 child4"], + ["root child child2"], + ["root child child2 child3 child4"], + ["root child child2 child3,child3 child4"], + ["root child child2 child3,child3,child3 child4"], +]; +const INVALID_EXPRESSIONS: InvalidExpressionTestData[] = [ + { input: ["root"] }, + { input: ["root circle1 circle2 circle1"] }, + { input: ["root WRONG child2"], output: [{ line: 1, startColumn: 6, endColumn: 11 }] }, + { + input: ["root", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2", "WRONG circle2"], + output: [{ line: 6, startColumn: 1, endColumn: 6 }], + }, + { input: ["root child child2 child3,child3, child4"] }, +]; +const AUTOCOMPLETE_TEST_DATA: AutoCompleteTestData[] = [ + { input: ["root "], completionOptions: ["circle1", "child"], exactOptionCount: true }, + { + input: ["root", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 "], + completionOptions: ["circle2"], + }, + { input: ["root child !"], completionOptions: ["child1"] }, + { input: ["root child child2 child3,child3,child3 child4 "], completionOptions: [], exactOptionCount: true }, +]; +const simpleInput1 = ["root child child1 bla child3 child4"]; +const simpleInput2 = ["root circle1 circle2 circle1 circle2"]; +const REPLACEMENT_TEST_DATA: ReplacementTestData[] = [ + { + input: ["root child child1 bla child3 child4"], + output: ["root child child1 blub child3 child4"], + replacement: { old: "bla", replacement: "blub", type: "replaceAny" }, + }, + { + input: ["root child child1 bla child3 chil"], + output: ["root child child1 blub child3 chil"], + replacement: { old: "bla", replacement: "blub", type: "replaceAny" }, + }, + // check that no wrong replacements happen + { input: simpleInput1, output: simpleInput1, replacement: { old: "blub", replacement: "bla", type: "replaceAny" } }, + { input: simpleInput1, output: simpleInput1, replacement: { old: "bla", replacement: "blub", type: "WRONG" } }, + { + input: simpleInput2, + output: simpleInput2, + replacement: { old: "circle1", replacement: "circle2", type: "replaceAny" }, + }, +]; + +const circleNode1: LanguageTreeNode = { + word: new ConstantWord("circle1"), + children: [], +}; +const circleNode2: LanguageTreeNode = { + word: new ConstantWord("circle2"), + children: [circleNode1], + canBeFinal: true, +}; +circleNode1.children.push(circleNode2); + +const child4: LanguageTreeNode = { + word: new ConstantWord("child4"), + children: [], +}; +const child3: LanguageTreeNode = { + word: new ListWord(new ConstantWord("child3")), + children: [child4], +}; +const child2: LanguageTreeNode = { + word: new ConstantWord("child2"), + children: [child3], + canBeFinal: true, +}; +class ReplaceAnyWord extends AnyWord { + replace(text: string, replacement: ReplacementData): string { + if (replacement.type === "replaceAny" && text === replacement.old) { + return replacement.replacement; + } + return text; + } +} +const anyChild: LanguageTreeNode = { + word: new ReplaceAnyWord(), + children: [child3], +}; +const child1: LanguageTreeNode = { + word: new NegatableWord(new ConstantWord("child1")), + children: [anyChild], +}; +const child: LanguageTreeNode = { + word: new ConstantWord("child"), + children: [child1, child2], +}; + +const root: LanguageTreeNode = { + word: new ConstantWord("root"), + children: [circleNode1, child], +}; + +generateTests( + "Functionality Test Language", + [root], + VALID_EXPRESSIONS, + INVALID_EXPRESSIONS, + AUTOCOMPLETE_TEST_DATA, + REPLACEMENT_TEST_DATA, +); diff --git a/frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts b/frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts deleted file mode 100644 index a6c99970..00000000 --- a/frontend/webEditor/tests/languages/LanguageFunctionality.test1.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* -Tests whether the functions verify, complete and replace to what they are supposed to with a simple custom language: - -root - ├── circle1 <─────────┐ - │ └── circle2* ──┘ - │ - └──> child - ├──> !child1 - │ └──> any ──┐ - │ ├──> child3,... ──> child4 - └──> child2* ─────┘ - -* = can be end state -! is optional for negation -,... is a list with the same word -*/ -import { ReplacementData } from "../../src/languages/replace"; -import { LanguageTreeNode } from "../../src/languages/tokenize"; -import { AnyWord, ConstantWord, ListWord, NegatableWord, Word } from "../../src/languages/words"; -import { generateTests } from "./LanguageTestUtils"; - -const VALID_EXPRESSIONS = [ - ["root circle1 circle2"], - ["root", "circle1", "circle2"], - ["root", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2"], - ["root child", "child1 bla child3 child4"], - ["root child", "!child1 bla child3 child4"], - ["root child child2"], - ["root child child2 child3 child4"], - ["root child child2 child3,child3 child4"], - ["root child child2 child3,child3,child3 child4"], -]; - -type N = LanguageTreeNode; - -const circleNode1: N = { - word: new ConstantWord("circle1"), - children: [], -}; -const circleNode2: N = { - word: new ConstantWord("circle2"), - children: [circleNode1], - canBeFinal: true, -}; -circleNode1.children.push(circleNode2); - -const child4: N = { - word: new ConstantWord("child3"), - children: [], -}; -const child3: N = { - word: new ListWord(new ConstantWord("child3")), - children: [child4], -}; -const child2: N = { - word: new ConstantWord("child2"), - children: [child3], - canBeFinal: true, -}; -class ReplaceAnyWord extends AnyWord { - replace(text: string, replacement: ReplacementData): string { - if (replacement.type === "replaceAny" && text === replacement.old) { - return replacement.replacement; - } - return text; - } -} -const anyChild: N = { - word: new ReplaceAnyWord(), - children: [child3], -}; -const child1: N = { - word: new NegatableWord(new ConstantWord("child1")), - children: [anyChild], -}; -const child: N = { - word: new ConstantWord("child"), - children: [child1, child2], -}; - -const root: N = { - word: new ConstantWord("root"), - children: [circleNode1, child], -}; - -generateTests("Functionality Test Language", [root], VALID_EXPRESSIONS, [], [], []); diff --git a/frontend/webEditor/tests/languages/LanguageTestUtils.ts b/frontend/webEditor/tests/languages/LanguageTestUtils.ts index 72944c6d..d6359358 100644 --- a/frontend/webEditor/tests/languages/LanguageTestUtils.ts +++ b/frontend/webEditor/tests/languages/LanguageTestUtils.ts @@ -18,7 +18,7 @@ export function generateTests( const result = verify(tokens, tree); expect( result.length, - `Valid Expression ${joinLines(validExpressions[i])} had Errors: ${result.join("\n")}`, + `Valid Expression ${joinLines(validExpressions[i])} had Errors: ${result.map((v) => JSON.stringify(v)).join("\n")}`, ).toBe(0); }); } @@ -41,7 +41,7 @@ export function generateTests( e.startColumn === expectedError.startColumn && (expectedError.endColumn === undefined || e.endColumn === expectedError.endColumn), ), - `Could not find Error at ${expectedError} in ${joinLines(invalidExpressions[i].input)}`, + `Could not find Error at ${JSON.stringify(expectedError)} in ${joinLines(invalidExpressions[i].input)}`, ) .toBeDefined(); } diff --git a/frontend/webEditor/tests/languages/mockMonaco.ts b/frontend/webEditor/tests/languages/mockMonaco.ts new file mode 100644 index 00000000..0a2dfea7 --- /dev/null +++ b/frontend/webEditor/tests/languages/mockMonaco.ts @@ -0,0 +1,7 @@ +import { vi } from "vitest"; + +vi.stubGlobal("window", { + location: { + href: "", + }, +}); diff --git a/frontend/webEditor/vitest.config.ts b/frontend/webEditor/vitest.config.ts new file mode 100644 index 00000000..9c2f0800 --- /dev/null +++ b/frontend/webEditor/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + setupFiles: ["./tests/languages/mockMonaco.ts"], + }, +}); From ab88ea0b35289f042f27b8cb0d6fe8d27e69796f Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 28 Apr 2026 09:52:55 +0200 Subject: [PATCH 4/8] mocks for assignment language tests --- .../languages/AssignmentLanguage.test.ts | 35 +++++++++++++++++++ .../webEditor/tests/languages/mockMonaco.ts | 11 ++++++ 2 files changed, 46 insertions(+) create mode 100644 frontend/webEditor/tests/languages/AssignmentLanguage.test.ts diff --git a/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts b/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts new file mode 100644 index 00000000..89d50846 --- /dev/null +++ b/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts @@ -0,0 +1,35 @@ +import { AssignmentLanguageTreeBuilder } from "../../src/assignment/language"; +import { DfdNodeImpl } from "../../src/diagram/nodes/common"; +import { DfdOutputPortImpl } from "../../src/diagram/ports/DfdOutputPort"; +import { LabelTypeRegistry } from "../../src/labels/LabelTypeRegistry"; +import { + ValidExpressionTestData, + AutoCompleteTestData, + generateTests, + InvalidExpressionTestData, + ReplacementTestData, +} from "./LanguageTestUtils"; + +const VALID_EXPRESSIONS: ValidExpressionTestData[] = [["forward flow"], ["forward hello|world"]]; + +const INVALID_EXPRESSIONS: InvalidExpressionTestData[] = []; + +const AUTOCOMPLETE_TEST_DATA: AutoCompleteTestData[] = []; + +const REPLACEMENT_TEST_DATA: ReplacementTestData[] = []; + +const labelTypeRegistry = new LabelTypeRegistry(); + +const mockPort = { + // @ts-expect-error We mock the abstract class into a concrete one for simplicity. TypeScript however still thinks this is an abstract class, so it would throw an error + parent: new DfdNodeImpl(), +} as unknown as DfdOutputPortImpl; + +generateTests( + "Functionality Test Language", + AssignmentLanguageTreeBuilder.buildTree(mockPort, labelTypeRegistry), + VALID_EXPRESSIONS, + INVALID_EXPRESSIONS, + AUTOCOMPLETE_TEST_DATA, + REPLACEMENT_TEST_DATA, +); diff --git a/frontend/webEditor/tests/languages/mockMonaco.ts b/frontend/webEditor/tests/languages/mockMonaco.ts index 0a2dfea7..631e7986 100644 --- a/frontend/webEditor/tests/languages/mockMonaco.ts +++ b/frontend/webEditor/tests/languages/mockMonaco.ts @@ -5,3 +5,14 @@ vi.stubGlobal("window", { href: "", }, }); + +vi.mock("../../src/diagram/ports/DfdOutputPort", () => ({})); + +vi.mock("../../src/diagram/nodes/common", () => { + class DfdNodeImpl { + // add any abstract methods the tested code calls + getAvailableInputs = vi.fn().mockReturnValue(["flow", "hello|world"]); + } + + return { DfdNodeImpl }; +}); From 592e588480a36ee877befd138244aec702ef6585 Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Tue, 28 Apr 2026 13:19:35 +0200 Subject: [PATCH 5/8] simple test for assignment language --- .../languages/AssignmentLanguage.test.ts | 53 +++++++++++++++++-- .../languages/LanguageFunctionality.test.ts | 5 +- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts b/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts index 89d50846..7e1f13b9 100644 --- a/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts +++ b/frontend/webEditor/tests/languages/AssignmentLanguage.test.ts @@ -10,15 +10,58 @@ import { ReplacementTestData, } from "./LanguageTestUtils"; -const VALID_EXPRESSIONS: ValidExpressionTestData[] = [["forward flow"], ["forward hello|world"]]; +const BASE_VALID_EXPRESSIONS: ValidExpressionTestData[] = [ + ["forward flow"], + ["forward hello|world"], + ["set Sensitivity.Public"], + ["unset Location.nonEU"], + ["assign Sensitivity.Public if TRUE from flow"], + ["assign Sensitivity.Public if TRUE"], + ["assign Sensitivity.Public if flow.Sensitivity.Public"], +]; -const INVALID_EXPRESSIONS: InvalidExpressionTestData[] = []; +const VALID_EXPRESSIONS = [...BASE_VALID_EXPRESSIONS, BASE_VALID_EXPRESSIONS.flat()]; -const AUTOCOMPLETE_TEST_DATA: AutoCompleteTestData[] = []; +const INVALID_EXPRESSIONS: InvalidExpressionTestData[] = [ + { input: ["forward Sensitivity.Public"] }, + { input: ["set flow"] }, + { input: ["forward hello"] }, + { input: ["set Sensitivity.public"] }, + { input: ["set sensitivity.Public"] }, +]; -const REPLACEMENT_TEST_DATA: ReplacementTestData[] = []; +const AUTOCOMPLETE_TEST_DATA: AutoCompleteTestData[] = [ + { input: [""], completionOptions: ["forward", "set", "unset", "assign"], exactOptionCount: true }, + { input: ["forward "], completionOptions: ["flow", "hello|world"], exactOptionCount: true }, + { input: ["set "], completionOptions: ["Sensitivity", "Location"], exactOptionCount: true }, + { input: ["set Sensitivity."], completionOptions: ["Personal", "Public"], exactOptionCount: true }, + { input: ["unset "], completionOptions: ["Sensitivity", "Location"], exactOptionCount: true }, + { input: ["assign "], completionOptions: ["Sensitivity", "Location"] }, + { input: ["assign Sensitivity.Public "], completionOptions: ["if"], exactOptionCount: true }, + { input: ["assign Sensitivity.Public if TRUE "], completionOptions: ["from"] }, +]; + +const REPLACEMENT_TEST_DATA: ReplacementTestData[] = [ + { + input: ["set Sensitivity.Public"], + replacement: { old: "Sensitivity.Public", replacement: "Sensitivity.New", type: "label" }, + output: ["set Sensitivity.New"], + }, +]; const labelTypeRegistry = new LabelTypeRegistry(); +const location = labelTypeRegistry.registerLabelType("Location"); +for (const defaultValue of location.values) { + labelTypeRegistry.unregisterLabelTypeValue(location.id, defaultValue.id); +} +labelTypeRegistry.registerLabelTypeValue(location.id, "EU"); +labelTypeRegistry.registerLabelTypeValue(location.id, "nonEU"); +const sensitivity = labelTypeRegistry.registerLabelType("Sensitivity"); +for (const defaultValue of sensitivity.values) { + labelTypeRegistry.unregisterLabelTypeValue(sensitivity.id, defaultValue.id); +} +labelTypeRegistry.registerLabelTypeValue(sensitivity.id, "Personal"); +labelTypeRegistry.registerLabelTypeValue(sensitivity.id, "Public"); const mockPort = { // @ts-expect-error We mock the abstract class into a concrete one for simplicity. TypeScript however still thinks this is an abstract class, so it would throw an error @@ -26,7 +69,7 @@ const mockPort = { } as unknown as DfdOutputPortImpl; generateTests( - "Functionality Test Language", + "Assignment Language", AssignmentLanguageTreeBuilder.buildTree(mockPort, labelTypeRegistry), VALID_EXPRESSIONS, INVALID_EXPRESSIONS, diff --git a/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts b/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts index 6619217b..27d81750 100644 --- a/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts +++ b/frontend/webEditor/tests/languages/LanguageFunctionality.test.ts @@ -2,8 +2,8 @@ Tests whether the functions verify, complete and replace to what they are supposed to with a simple custom language: root - ├── circle1 <─────────┐ - │ └── circle2* ──┘ + ├─> circle1 <─────────┐ + │ └─> circle2* ──┘ │ └──> child ├──> !child1 @@ -49,6 +49,7 @@ const INVALID_EXPRESSIONS: InvalidExpressionTestData[] = [ ]; const AUTOCOMPLETE_TEST_DATA: AutoCompleteTestData[] = [ { input: ["root "], completionOptions: ["circle1", "child"], exactOptionCount: true }, + { input: ["root c"], completionOptions: ["circle1", "child"], exactOptionCount: true }, { input: ["root", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 circle2", "circle1 "], completionOptions: ["circle2"], From 0b859b843d10a1482e8616b2c469975a3b0a897c Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Wed, 3 Jun 2026 08:50:45 +0200 Subject: [PATCH 6/8] simple tests for constraint dsl --- .../SimpleConstraintLanguage.test.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 frontend/webEditor/tests/languages/SimpleConstraintLanguage.test.ts diff --git a/frontend/webEditor/tests/languages/SimpleConstraintLanguage.test.ts b/frontend/webEditor/tests/languages/SimpleConstraintLanguage.test.ts new file mode 100644 index 00000000..85b7066b --- /dev/null +++ b/frontend/webEditor/tests/languages/SimpleConstraintLanguage.test.ts @@ -0,0 +1,63 @@ +import { LocalModelSource } from "sprotty"; +import { ConstraintDslTreeBuilder } from "../../src/constraint/language"; +import { LabelTypeRegistry } from "../../src/labels/LabelTypeRegistry"; +import { + ValidExpressionTestData, + AutoCompleteTestData, + generateTests, + InvalidExpressionTestData, + ReplacementTestData, +} from "./LanguageTestUtils"; + + +const VALID_EXPRESSIONS: ValidExpressionTestData[] = [ + ["- Test: data Sensitivity.Personal neverFlows vertex Location.nonEU"], + ["- Auth_request: data Sensitivity.Personal,Location.EU neverFlows vertex !Location.EU"], + ["- test: data Sensitivity.Personal neverFlows"], + ["- Test: data Sensitivity.Personal", " neverFlows", " vertex Location.nonEU"], +]; + +const INVALID_EXPRESSIONS: InvalidExpressionTestData[] = [ + { input: ["- data Sensitivity.Personal neverFlows vertex Location.nonEU"] }, + { input: ["- Test: data Sensitivity.Personal vertex Location.nonEU"] } +]; + +const AUTOCOMPLETE_TEST_DATA: AutoCompleteTestData[] = [ + { input: ["- Test: "], completionOptions: ["data", "vertex"], exactOptionCount: true }, + { input: ["- Test: data "], completionOptions: ["Sensitivity", "named", "Location", "type"] }, + { input: ["- Test: data Sensitivity.Personal "], completionOptions: ["neverFlows", "vertex"] }, + { input: ["- Test: data Sensitivity.Personal neverFlows "], completionOptions: ["where", "vertex"] }, + { input: ["- Test: data Sensitivity.Personal neverFlows vertex Location."], completionOptions: ["EU", "nonEU"] } +]; + +const REPLACEMENT_TEST_DATA: ReplacementTestData[] = [{ + input: ["- Test: data Sensitivity.Personal neverFlows vertex Location.nonEU"], + replacement: { old: "Sensitivity.Personal", replacement: "Sensitivity.New", type: "label" }, + output: ["- Test: data Sensitivity.New neverFlows vertex Location.nonEU"], + }]; + +const labelTypeRegistry = new LabelTypeRegistry(); +const location = labelTypeRegistry.registerLabelType("Location"); +for (const defaultValue of location.values) { + labelTypeRegistry.unregisterLabelTypeValue(location.id, defaultValue.id); +} +labelTypeRegistry.registerLabelTypeValue(location.id, "EU"); +labelTypeRegistry.registerLabelTypeValue(location.id, "nonEU"); +const sensitivity = labelTypeRegistry.registerLabelType("Sensitivity"); +for (const defaultValue of sensitivity.values) { + labelTypeRegistry.unregisterLabelTypeValue(sensitivity.id, defaultValue.id); +} +labelTypeRegistry.registerLabelTypeValue(sensitivity.id, "Personal"); +labelTypeRegistry.registerLabelTypeValue(sensitivity.id, "Public"); + +const mockModel = { +} as unknown as LocalModelSource; + +generateTests( + "Simple Constraint Language", + ConstraintDslTreeBuilder.buildTree(mockModel, labelTypeRegistry), + VALID_EXPRESSIONS, + INVALID_EXPRESSIONS, + AUTOCOMPLETE_TEST_DATA, + REPLACEMENT_TEST_DATA, +); From cab70c9f642cda94f253368638e2cbf2a67df37c Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Wed, 3 Jun 2026 08:52:15 +0200 Subject: [PATCH 7/8] fix package.lock --- frontend/webEditor/package-lock.json | 1177 +++++++++----------------- 1 file changed, 415 insertions(+), 762 deletions(-) diff --git a/frontend/webEditor/package-lock.json b/frontend/webEditor/package-lock.json index 08d11fc7..4cdfe7b7 100644 --- a/frontend/webEditor/package-lock.json +++ b/frontend/webEditor/package-lock.json @@ -31,7 +31,8 @@ "sprotty-protocol": "^1.4.0", "typescript": "^5.8.3", "typescript-eslint": "^8.57.0", - "vite": "^7.3.1" + "vite": "^7.3.1", + "vitest": "^4.1.4" } }, "node_modules/@babel/runtime": { @@ -43,431 +44,6 @@ "node": ">=6.9.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/win32-x64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", @@ -728,323 +304,50 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@inversifyjs/common": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@inversifyjs/common/-/common-1.4.0.tgz", - "integrity": "sha512-qfRJ/3iOlCL/VfJq8+4o5X4oA14cZSBbpAmHsYj8EsIit1xDndoOl0xKOyglKtQD4u4gdNVxMHx4RWARk/I4QA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inversifyjs/core": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@inversifyjs/core/-/core-1.3.5.tgz", - "integrity": "sha512-B4MFXabhNTAmrfgB+yeD6wd/GIvmvWC6IQ8Rh/j2C3Ix69kmqwz9pr8Jt3E+Nho9aEHOQCZaGmrALgtqRd+oEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inversifyjs/common": "1.4.0", - "@inversifyjs/reflect-metadata-utils": "0.2.4" - } - }, - "node_modules/@inversifyjs/reflect-metadata-utils": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@inversifyjs/reflect-metadata-utils/-/reflect-metadata-utils-0.2.4.tgz", - "integrity": "sha512-u95rV3lKfG+NT2Uy/5vNzoDujos8vN8O18SSA5UyhxsGYd4GLQn/eUsGXfOsfa7m34eKrDelTKRUX1m/BcNX5w==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "reflect-metadata": "0.2.2" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", - "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", - "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", - "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", - "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", - "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", - "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", - "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", - "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", - "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", - "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", - "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", - "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", - "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", - "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", - "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", - "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", - "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", - "cpu": [ - "x64" - ], + "node_modules/@inversifyjs/common": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@inversifyjs/common/-/common-1.4.0.tgz", + "integrity": "sha512-qfRJ/3iOlCL/VfJq8+4o5X4oA14cZSBbpAmHsYj8EsIit1xDndoOl0xKOyglKtQD4u4gdNVxMHx4RWARk/I4QA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", - "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", - "cpu": [ - "arm64" - ], + "node_modules/@inversifyjs/core": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@inversifyjs/core/-/core-1.3.5.tgz", + "integrity": "sha512-B4MFXabhNTAmrfgB+yeD6wd/GIvmvWC6IQ8Rh/j2C3Ix69kmqwz9pr8Jt3E+Nho9aEHOQCZaGmrALgtqRd+oEQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] + "dependencies": { + "@inversifyjs/common": "1.4.0", + "@inversifyjs/reflect-metadata-utils": "0.2.4" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", - "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", - "cpu": [ - "arm64" - ], + "node_modules/@inversifyjs/reflect-metadata-utils": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@inversifyjs/reflect-metadata-utils/-/reflect-metadata-utils-0.2.4.tgz", + "integrity": "sha512-u95rV3lKfG+NT2Uy/5vNzoDujos8vN8O18SSA5UyhxsGYd4GLQn/eUsGXfOsfa7m34eKrDelTKRUX1m/BcNX5w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "peerDependencies": { + "reflect-metadata": "0.2.2" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", - "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", - "cpu": [ - "ia32" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.52.4", @@ -1074,6 +377,31 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -1397,6 +725,119 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@vitest/expect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vscode/codicons": { "version": "0.0.44", "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.44.tgz", @@ -1493,6 +934,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/autocompleter": { "version": "9.3.2", "resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-9.3.2.tgz", @@ -1565,6 +1016,16 @@ "node": ">=10.0.0" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -1605,6 +1066,13 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/core-js": { "version": "3.48.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", @@ -1716,6 +1184,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", @@ -1999,6 +1474,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2016,6 +1501,16 @@ "dev": true, "license": "MIT" }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2118,21 +1613,6 @@ "integrity": "sha512-l/YiRdBSH/eWv6OF3sLGkwErL+n0MqCICi9mppTZBOCL5vixWGDqCYvRcuxB2h7RGCTzaTKOHT2caHvCXQPRlw==", "license": "MIT" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/get-east-asian-width": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", @@ -2381,16 +1861,16 @@ } }, "node_modules/lint-staged": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.5.tgz", - "integrity": "sha512-d12yC+/e8RhBjZtaxZn71FyrgU/P5e+uAPifhCLwdosQZP/zamSdKRWDC30ocVIbzDKiFG1McHc/LUgB92GIPw==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.8.tgz", + "integrity": "sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==", "dev": true, "license": "MIT", "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", - "tinyexec": "^1.1.2" + "tinyexec": "^1.2.4" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -2402,13 +1882,13 @@ "url": "https://opencollective.com/lint-staged" }, "optionalDependencies": { - "yaml": "^2.8.4" + "yaml": "^2.9.0" } }, "node_modules/listr2": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.1.tgz", - "integrity": "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.2.tgz", + "integrity": "sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==", "dev": true, "license": "MIT", "dependencies": { @@ -2546,6 +2026,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -2622,6 +2112,17 @@ "node": ">=0.10.0" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -2737,6 +2238,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -2975,6 +2483,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -3096,6 +2611,13 @@ "node": ">=8.3.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/stackblur-canvas": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", @@ -3106,6 +2628,13 @@ "node": ">=0.1.14" } }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -3117,9 +2646,9 @@ } }, "node_modules/string-width": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", - "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", "dev": true, "license": "MIT", "dependencies": { @@ -3206,10 +2735,17 @@ "utrie": "^1.0.2" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.2.tgz", - "integrity": "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -3258,6 +2794,16 @@ "dev": true, "license": "ISC" }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -3435,6 +2981,96 @@ } } }, + "node_modules/vitest": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3451,6 +3087,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", From 1bfc4c2d7d1e25139fcc1ffe4aa33044994be161 Mon Sep 17 00:00:00 2001 From: Alex | Kronox <39801116+Kr0nox@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:12:22 +0000 Subject: [PATCH 8/8] add workflow --- .github/workflows/testWebEditor.yml | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/testWebEditor.yml diff --git a/.github/workflows/testWebEditor.yml b/.github/workflows/testWebEditor.yml new file mode 100644 index 00000000..f6830cbb --- /dev/null +++ b/.github/workflows/testWebEditor.yml @@ -0,0 +1,37 @@ +name: Web Editor Unit Test + +on: + workflow_dispatch: + push: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + pre_job: + runs-on: ubuntu-latest + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@v5 + with: + concurrent_skipping: 'same_content_newer' + skip_after_successful_duplicate: 'true' + + test: + needs: pre_job + if: ${{ needs.pre_job.outputs.should_skip != 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version: "lts/*" + + - name: Install and Run Tests + working-directory: frontend/webEditor + run: | + npm install + npm run test \ No newline at end of file