Compare commits

...
Author SHA1 Message Date
Ludy87 1d27cd26a6 Update usePolicyAutoRun.batch.test.tsx 2026-08-25 15:50:06 +02:00
Ludy be24bcc759 Merge branch 'main' into test_fixtures_cross_platform_20260804 2026-08-25 15:42:33 +02:00
Anthony Stirling 6e39e25905 Merge main into test_fixtures_cross_platform_20260804 2026-08-21 07:17:38 +01:00
Ludy c5f7ebc2f1 Merge branch 'main' into test_fixtures_cross_platform_20260804 2026-08-16 11:42:48 +02:00
Ludy e50f18ca7c Merge branch 'main' into test_fixtures_cross_platform_20260804 2026-08-13 00:20:41 +02:00
Ludy87 31827c4405 Update frontend.yml 2026-08-11 16:33:12 +02:00
Ludy 9a8ee5a84d Merge branch 'main' into test_fixtures_cross_platform_20260804 2026-08-11 16:30:43 +02:00
Ludy b4ef003689 Merge branch 'main' into test_fixtures_cross_platform_20260804 2026-08-11 11:06:39 +02:00
Ludy87 c01d402323 Replace proprietary setupTests with portal in vitest config
Update frontend/editor/vitest.config.ts to exclude src/portal/setupTests.ts instead of src/proprietary/setupTests.ts. Aligns the Vitest ignore list with the portal layer's setup file so the test runner skips the correct setup file.
2026-08-04 14:51:01 +02:00
Ludy87 ec19f0c8ac Update vitest.config.ts 2026-08-04 14:48:24 +02:00
Ludy87 92889fce92 Update vitest.config.ts 2026-08-04 13:20:15 +02:00
Ludy87 61ed03477b Update .gitignore 2026-08-04 13:06:13 +02:00
Ludy87 345a9d37b1 fix(frontend): make coverage timeouts and test fixtures cross-platform 2026-08-04 13:03:26 +02:00
7 changed files with 68 additions and 35 deletions
+11 -2
View File
@@ -523,6 +523,7 @@ tasks:
deps: [prepare]
vars:
COVERAGE: '{{.COVERAGE | default .CI | default "false"}}'
TEST_TIMEOUT: '{{.TEST_TIMEOUT | default "5000"}}'
cmds:
- >
npx vitest run --root editor
@@ -531,7 +532,9 @@ tasks:
--coverage.reporter=text-summary
--coverage.reporter=json-summary
--coverage.reporter=html
--coverage.reportsDirectory=./coverage{{end}}
--coverage.reportsDirectory=./coverage
{{if .TEST_TIMEOUT}}--testTimeout={{.TEST_TIMEOUT}}
--hookTimeout={{.TEST_TIMEOUT}}{{end}}{{end}}
test:watch:
desc: "Run tests in watch mode"
@@ -541,9 +544,15 @@ tasks:
test:coverage:
desc: "Run tests with coverage (one-shot; CI-friendly)."
vars:
TEST_TIMEOUT: '{{.TEST_TIMEOUT | default "5000"}}'
env:
TEST_TIMEOUT: '{{.TEST_TIMEOUT}}'
cmds:
- task: test:editor
vars: { COVERAGE: "true" }
vars:
COVERAGE: "true"
TEST_TIMEOUT: '{{.TEST_TIMEOUT}}'
# ============================================================
# Code Generation
+2
View File
@@ -6,6 +6,7 @@
.pnp.js
# testing
/editor/coverage
/coverage
# production
@@ -50,3 +51,4 @@ test-results
/editor/src-tauri/tauri.conf.dev-update.json
.a11y-scan/
.a11y-acc/
/editor/src-tauri/*
-3
View File
@@ -212,6 +212,3 @@ if (typeof globalThis.DOMMatrix === "undefined") {
configurable: true,
});
}
// Set global test timeout to prevent hangs
vi.setConfig({ testTimeout: 5000, hookTimeout: 5000 });
@@ -33,6 +33,8 @@ export function formatMinor(
minor: number,
currency: string | null | undefined,
): string {
// Money strings use a stable decimal separator because the compact symbol is
// already supplied separately and backend/test contracts expect dot decimals.
const num = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 3,
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { renderHook, waitFor } from "@testing-library/react";
import type { ClassificationConfidence } from "@app/types/fileContext";
/**
@@ -8,7 +8,7 @@ import type { ClassificationConfidence } from "@app/types/fileContext";
*/
const FILE_COUNT = 61;
const TEST_TIMEOUT_MS = Number(process.env.TEST_TIMEOUT ?? 10000);
// A tiny mutable "workspace" the mocks share: the list of file stubs currently in
// the workbench, mirrored into useAllFiles. consumeFiles mutates it in place
// (input id → output id) exactly as the real silent reducer would.
@@ -240,15 +240,13 @@ beforeEach(() => {
/** Drive the hook until the store shows the expected number of imported runs. */
async function runUntilSettled(expectedRuns: number) {
renderHook(() => Harness());
await act(async () => {
await vi.waitFor(
() => {
const imported = latestRuns.filter((r) => r.imported).length;
expect(imported).toBe(expectedRuns);
},
{ timeout: 8000, interval: 20 },
);
});
await waitFor(
() => {
const imported = latestRuns.filter((r) => r.imported).length;
expect(imported).toBe(expectedRuns);
},
{ timeout: TEST_TIMEOUT_MS, interval: 20 },
);
}
describe("policy auto-run — 61-file batch through a Security → Classification chain", () => {
@@ -296,19 +294,15 @@ describe("policy auto-run — 61-file batch through a Security → Classificatio
id: "storage",
versionNumber: 1,
});
act(() => {
mocks.workspace = [];
});
mocks.workspace = [];
await act(async () => {
await vi.waitFor(
() => {
const imported = latestRuns.filter((r) => r.imported).length;
expect(imported).toBe(FILE_COUNT * 2);
},
{ timeout: 8000, interval: 20 },
);
});
await waitFor(
() => {
const imported = latestRuns.filter((r) => r.imported).length;
expect(imported).toBe(FILE_COUNT * 2);
},
{ timeout: TEST_TIMEOUT_MS, interval: 20 },
);
// Still fully processed (chain intact), but Security's versions went to
// STORAGE, never re-added to the workbench — the workspace stays empty.
@@ -416,6 +416,7 @@ describe("Login", () => {
options: { redirectTo: "/auth/callback" },
});
});
await waitFor(() => expect(oauthButton).not.toBeDisabled());
});
it("should use actual provider ID for OAuth login (custom provider)", async () => {
@@ -463,6 +464,10 @@ describe("Login", () => {
options: { redirectTo: "/auth/callback" },
});
});
// The async handler clears isSubmitting after the OAuth mock resolves. Wait
// for that final render so Mantine's transition cannot update after teardown.
await waitFor(() => expect(oauthButton).not.toBeDisabled());
});
it("should use oidc provider ID when explicitly configured", async () => {
@@ -509,6 +514,7 @@ describe("Login", () => {
options: { redirectTo: "/auth/callback" },
});
});
await waitFor(() => expect(oauthButton).not.toBeDisabled());
});
it("should show error on failed login", async () => {
@@ -845,6 +851,9 @@ describe("Login", () => {
await waitFor(() => {
expect(springAuth.signInWithOAuth).toHaveBeenCalled();
});
await waitFor(() =>
expect(screen.getByText(/Authentik/)).not.toBeDisabled(),
);
// Must be stashed before the cross-origin SSO redirect wipes location.state.
expect(sessionStorage.getItem("stirling_post_login_path")).toBe(
+27 -7
View File
@@ -1,12 +1,18 @@
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const editorDir = dirname(fileURLToPath(import.meta.url));
const tsconfig = (name: string) => resolve(editorDir, name);
// Projects do NOT inherit the root test.testTimeout, so every project silently
// ran at vitest's 5s default. Spread this into each one instead.
const TIMEOUTS = { testTimeout: 10000, hookTimeout: 10000 };
export default defineConfig({
root: editorDir,
test: {
globals: true,
environment: "jsdom",
@@ -22,13 +28,21 @@ export default defineConfig({
reporter: ["text", "json", "html"],
exclude: [
"node_modules/",
"dist/**",
"coverage/**",
"src/core/setupTests.ts",
"src/proprietary/setupTests.ts",
"src/portal/setupTests.ts",
"src/saas/setupTests.ts",
"**/*.d.ts",
"src/tests/test-fixtures/**",
"src/**/*.spec.ts",
],
thresholds: {
lines: 13,
functions: 40,
branches: 63,
statements: 13,
},
},
projects: [
{
@@ -43,7 +57,8 @@ export default defineConfig({
plugins: [
react(),
tsconfigPaths({
projects: ["./tsconfig.core.vite.json"],
root: editorDir,
projects: [tsconfig("tsconfig.core.vite.json")],
}),
],
esbuild: {
@@ -62,9 +77,10 @@ export default defineConfig({
plugins: [
react(),
tsconfigPaths({
root: editorDir,
// Broad project so @app/@portal resolve in every editor file the
// portal tests pull in (core/ui, core, ...).
projects: ["./tsconfig.portal.vite.json"],
projects: [tsconfig("tsconfig.portal.vite.json")],
}),
],
esbuild: {
@@ -83,7 +99,8 @@ export default defineConfig({
plugins: [
react(),
tsconfigPaths({
projects: ["./tsconfig.proprietary.vite.json"],
root: editorDir,
projects: [tsconfig("tsconfig.proprietary.vite.json")],
}),
],
esbuild: {
@@ -102,7 +119,8 @@ export default defineConfig({
plugins: [
react(),
tsconfigPaths({
projects: ["./tsconfig.desktop.vite.json"],
root: editorDir,
projects: [tsconfig("tsconfig.desktop.vite.json")],
}),
],
esbuild: {
@@ -127,7 +145,8 @@ export default defineConfig({
plugins: [
react(),
tsconfigPaths({
projects: ["./tsconfig.saas.vite.json"],
root: editorDir,
projects: [tsconfig("tsconfig.saas.vite.json")],
}),
],
esbuild: {
@@ -146,7 +165,8 @@ export default defineConfig({
plugins: [
react(),
tsconfigPaths({
projects: ["./tsconfig.prototypes.vite.json"],
root: editorDir,
projects: [tsconfig("tsconfig.prototypes.vite.json")],
}),
],
esbuild: {