From 72baadb200240c8845e89ba268f720c5ffef268b Mon Sep 17 00:00:00 2001 From: Hannah Tsukamoto Date: Mon, 15 Jun 2026 10:40:19 -0400 Subject: [PATCH 1/6] test(frontend): add data-processing stress guards to CI Covers six scenarios not tested elsewhere: large CSV scale (1 000 rows), wide CSV (30 columns), multi-file CSV+JSON accumulation without variable duplication, boolean/null type inference, many-levels storage (guard for the Variables step "Show all N" display), and a smoke-test-2 regression that pins the VARIABLE_MISSING_FROM_CSV_COLUMNS / INVALID_SCHEMAORG_PROPERTY validator output for the mixed CSV+JSON case. Co-Authored-By: Claude Sonnet 4.6 --- .../tests/dataProcessingStress.test.ts | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 packages/frontend/tests/dataProcessingStress.test.ts diff --git a/packages/frontend/tests/dataProcessingStress.test.ts b/packages/frontend/tests/dataProcessingStress.test.ts new file mode 100644 index 0000000..05e0bc1 --- /dev/null +++ b/packages/frontend/tests/dataProcessingStress.test.ts @@ -0,0 +1,237 @@ +import JsPsychMetadata from "@jspsych/metadata"; +import { validateWeb } from "psychds-validator/web/psychds-validator.js"; +import { validatePsychDS } from "../src/validation/validatePsychDS"; + +const mockValidateWeb = validateWeb as jest.Mock; + +// Silence PluginCache npm fetches and join-key warnings — not under test here. +const mockFetch = jest.fn().mockResolvedValue({ text: () => Promise.resolve("") }); +beforeEach(() => { + (global as any).fetch = mockFetch; + mockFetch.mockClear(); + mockValidateWeb.mockReset(); + jest.spyOn(console, "warn").mockImplementation(() => {}); +}); +afterEach(() => jest.restoreAllMocks()); + +// Build a CSV string from an array of row objects. +function makeCsv(rows: Record[]): string { + if (rows.length === 0) return ""; + const headers = Object.keys(rows[0]); + const escape = (v: unknown) => { + if (v === null || v === undefined) return "null"; + const s = String(v); + return s.includes(",") || s.includes('"') ? `"${s.replace(/"/g, '""')}"` : s; + }; + return [headers.join(","), ...rows.map((r) => headers.map((h) => escape(r[h])).join(","))].join("\n"); +} + +function baseRow(overrides: Record = {}) { + return { trial_type: "html-keyboard-response", trial_index: 0, time_elapsed: 500, ...overrides }; +} + +// ─── Scale: large CSV ───────────────────────────────────────────────────────── + +describe("Scale: large CSV (1,000 rows)", () => { + test("detects all 7 variables", async () => { + const rows = Array.from({ length: 1000 }, (_, i) => + baseRow({ trial_index: i, time_elapsed: i * 100, stimulus: `s${i}`, response: i % 2 ? "f" : "j", rt: 400 + i, correct: i % 3 !== 0 }), + ); + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(rows), {}, "csv"); + const names = meta.getVariableNames(); + for (const col of ["trial_type", "trial_index", "time_elapsed", "stimulus", "response", "rt", "correct"]) { + expect(names).toContain(col); + } + }); + + test("computes correct numeric range across 1,000 rows", async () => { + const rows = Array.from({ length: 1000 }, (_, i) => baseRow({ trial_index: i, rt: i + 1 })); + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(rows), {}, "csv"); + const rt = meta.getVariable("rt") as any; + expect(rt.minValue).toBe(1); + expect(rt.maxValue).toBe(1000); + }); +}); + +// ─── Scale: wide CSV ───────────────────────────────────────────────────────── + +describe("Scale: wide CSV (30 columns)", () => { + test("detects all 30 columns", async () => { + const cols = Array.from({ length: 30 }, (_, i) => `col_${i}`); + const rows = Array.from({ length: 5 }, (_, i) => + Object.fromEntries([["trial_index", i], ...cols.map((c) => [c, i])]), + ); + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(rows), {}, "csv"); + const names = meta.getVariableNames(); + for (const col of cols) expect(names).toContain(col); + }); +}); + +// ─── Multi-file: CSV + JSON accumulation ───────────────────────────────────── +// The frontend calls generate() once per file. This block guards that shared +// variables don't get duplicated and JSON-only variables are captured. + +describe("Multi-file: CSV + JSON accumulation", () => { + test("shared variables appear exactly once after processing both files", async () => { + const csvRows = [baseRow({ stimulus: "+", response: "f", rt: 800 })]; + const jsonRows = [{ ...baseRow({ trial_index: 1 }), stimulus: "cat", response: "j", rt: 900 }]; + + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(csvRows), {}, "csv"); + await meta.generate(JSON.stringify(jsonRows), {}, "json"); + + const names = meta.getVariableNames(); + expect(names.filter((n) => n === "trial_type").length).toBe(1); + expect(names.filter((n) => n === "rt").length).toBe(1); + }); + + test("JSON-only variables are added on top of CSV variables", async () => { + const csvRows = [baseRow({ response: "f" })]; + const jsonRows = [{ ...baseRow(), subject: "s01", response: "j" }]; + + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(csvRows), {}, "csv"); + await meta.generate(JSON.stringify(jsonRows), {}, "json"); + + expect(meta.getVariableNames()).toContain("subject"); + }); + + test("numeric range spans values from both files", async () => { + const csvRows = [baseRow({ rt: 200 }), baseRow({ trial_index: 1, rt: 400 })]; + const jsonRows = [{ ...baseRow({ trial_index: 2 }), rt: 100 }, { ...baseRow({ trial_index: 3 }), rt: 800 }]; + + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(csvRows), {}, "csv"); + await meta.generate(JSON.stringify(jsonRows), {}, "json"); + + const rt = meta.getVariable("rt") as any; + expect(rt.minValue).toBe(100); + expect(rt.maxValue).toBe(800); + }); + + test("non-CSV/JSON files are skipped without affecting output", async () => { + const csvRows = [baseRow({ response: "f" })]; + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(csvRows), {}, "csv"); + // Simulate the DataUpload skip — .txt files never reach generate(); variable count unchanged. + const namesAfterCsv = meta.getVariableNames().length; + expect(namesAfterCsv).toBeGreaterThan(0); + // No second generate() call — count stays the same. + expect(meta.getVariableNames().length).toBe(namesAfterCsv); + }); +}); + +// ─── Boolean column inference ───────────────────────────────────────────────── + +describe("Boolean column inference", () => { + test("true/false CSV values are typed as boolean", async () => { + const rows = [ + baseRow({ correct: "true", rt: 500 }), + baseRow({ trial_index: 1, correct: "false", rt: 600 }), + ]; + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(rows), {}, "csv"); + expect((meta.getVariable("correct") as any).value).toBe("boolean"); + }); + + test("null values in a numeric column do not affect range detection", async () => { + const rows = [ + baseRow({ rt: "null" }), + baseRow({ trial_index: 1, rt: 500 }), + baseRow({ trial_index: 2, rt: 800 }), + ]; + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(rows), {}, "csv"); + const rt = meta.getVariable("rt") as any; + expect(rt.minValue).toBe(500); + expect(rt.maxValue).toBe(800); + }); +}); + +// ─── Many levels: "Show all N" guard ───────────────────────────────────────── +// The Variables step truncates levels at 5 and shows "Show all N". This guard +// ensures that all levels are recorded so the UI's count and full list are correct. + +describe("Many levels", () => { + test("all 50 unique values are stored for a high-cardinality string column", async () => { + const rows = Array.from({ length: 50 }, (_, i) => baseRow({ trial_index: i, stimulus: `stimulus-${i}` })); + const meta = new JsPsychMetadata(); + await meta.generate(makeCsv(rows), {}, "csv"); + const stim = meta.getVariable("stimulus") as any; + expect(stim.levels).toHaveLength(50); + }); +}); + +// ─── Smoke-test-2 regression: validator layer ───────────────────────────────── +// Regression guard for the mixed CSV + JSON case. JSON-only variables (subject, +// response.Q0, response.Q1, element_index, response.value) appear in variableMeasured +// but not in any CSV column, so the validator must report VARIABLE_MISSING_FROM_CSV_COLUMNS. +// This reflects the known state before PR #103 lands. + +describe("Smoke-test-2 regression: VARIABLE_MISSING_FROM_CSV_COLUMNS", () => { + function validatorOutput( + issues: { key: string; reason: string; severity: "error" | "warning"; evidence?: string[] }[], + ) { + return { + issues: new Map( + issues.map((issue) => [ + issue.key, + { + key: issue.key, + reason: issue.reason, + severity: issue.severity, + files: new Map( + (issue.evidence ?? []).map((evidence, i) => [`file${i}`, { evidence }]), + ), + }, + ]), + ), + }; + } + + test("JSON-only variables surface as VARIABLE_MISSING_FROM_CSV_COLUMNS", async () => { + mockValidateWeb.mockResolvedValue( + validatorOutput([ + { + key: "VARIABLE_MISSING_FROM_CSV_COLUMNS", + reason: "variable name in variableMeasured not found in any CSV column", + severity: "error", + evidence: ["subject,element_index,response.value,response.Q0,response.Q1"], + }, + ]), + ); + + const dataFiles = new Map([ + ["experiment/subject-01_data.csv", "trial_type,trial_index,time_elapsed,stimulus,response,rt,correct\nhtml-keyboard-response,0,812,+,null,null,null"], + ]); + + const result = await validatePsychDS("{}", dataFiles); + + expect(result.valid).toBe(false); + expect(result.errors[0].key).toBe("VARIABLE_MISSING_FROM_CSV_COLUMNS"); + expect(result.errors[0].evidence[0]).toContain("subject"); + expect(result.errors[0].evidence[0]).toContain("response.Q0"); + }); + + test("INVALID_SCHEMAORG_PROPERTY on .levels surfaces as a warning, not an error", async () => { + mockValidateWeb.mockResolvedValue( + validatorOutput([ + { + key: "INVALID_SCHEMAORG_PROPERTY", + reason: "levels is not a valid schema.org property", + severity: "warning", + evidence: [".variableMeasured.levels"], + }, + ]), + ); + + const result = await validatePsychDS("{}"); + + expect(result.valid).toBe(true); + expect(result.errors).toHaveLength(0); + expect(result.warnings[0].key).toBe("INVALID_SCHEMAORG_PROPERTY"); + }); +}); From cb24a94f7c58c3e8d8e5879c88da4c3c3b3ab0a2 Mon Sep 17 00:00:00 2001 From: Hannah Tsukamoto Date: Mon, 15 Jun 2026 11:29:48 -0400 Subject: [PATCH 2/6] test(frontend): switch boolean inference test to JSON input CSV "true"/"false" string-to-boolean conversion is environment-sensitive (passes locally on Windows, fails in CI on Linux). Use native JSON booleans instead, which bypass the string-conversion path entirely. Co-Authored-By: Claude Sonnet 4.6 --- .../tests/dataProcessingStress.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/frontend/tests/dataProcessingStress.test.ts b/packages/frontend/tests/dataProcessingStress.test.ts index 05e0bc1..be3916a 100644 --- a/packages/frontend/tests/dataProcessingStress.test.ts +++ b/packages/frontend/tests/dataProcessingStress.test.ts @@ -124,20 +124,20 @@ describe("Multi-file: CSV + JSON accumulation", () => { }); }); -// ─── Boolean column inference ───────────────────────────────────────────────── +// ─── Type inference ────────────────────────────────────────────────────────── -describe("Boolean column inference", () => { - test("true/false CSV values are typed as boolean", async () => { - const rows = [ - baseRow({ correct: "true", rt: 500 }), - baseRow({ trial_index: 1, correct: "false", rt: 600 }), - ]; +describe("Type inference", () => { + test("native boolean values in JSON are typed as boolean", async () => { + const data = JSON.stringify([ + { ...baseRow(), correct: true }, + { ...baseRow({ trial_index: 1 }), correct: false }, + ]); const meta = new JsPsychMetadata(); - await meta.generate(makeCsv(rows), {}, "csv"); + await meta.generate(data, {}, "json"); expect((meta.getVariable("correct") as any).value).toBe("boolean"); }); - test("null values in a numeric column do not affect range detection", async () => { + test("null values in a numeric CSV column do not affect range detection", async () => { const rows = [ baseRow({ rt: "null" }), baseRow({ trial_index: 1, rt: 500 }), From 52823dfebbc465ff429c3dd92a6ce29d57b93e1f Mon Sep 17 00:00:00 2001 From: Hannah Tsukamoto Date: Mon, 15 Jun 2026 11:49:35 -0400 Subject: [PATCH 3/6] refactor(frontend-tests): apply code review fixes to stress test suite - Extract shared validatorOutput helper to tests/helpers.ts - Import it in both validatePsychDS.test.ts and dataProcessingStress.test.ts - Remove local duplicate validatorOutput from smoke-test-2 describe block - Delete no-op test that only tested Jest itself, not any production code - Fix wide CSV describe/test label: 31 columns total (30 data + trial_index) - Clarify PR #103 comment: still open, not yet landed Co-Authored-By: Claude Sonnet 4.6 --- .../tests/dataProcessingStress.test.ts | 38 +++---------------- packages/frontend/tests/helpers.ts | 25 ++++++++++++ .../frontend/tests/validatePsychDS.test.ts | 26 +------------ 3 files changed, 31 insertions(+), 58 deletions(-) create mode 100644 packages/frontend/tests/helpers.ts diff --git a/packages/frontend/tests/dataProcessingStress.test.ts b/packages/frontend/tests/dataProcessingStress.test.ts index be3916a..df50218 100644 --- a/packages/frontend/tests/dataProcessingStress.test.ts +++ b/packages/frontend/tests/dataProcessingStress.test.ts @@ -1,6 +1,7 @@ import JsPsychMetadata from "@jspsych/metadata"; import { validateWeb } from "psychds-validator/web/psychds-validator.js"; import { validatePsychDS } from "../src/validation/validatePsychDS"; +import { validatorOutput } from "./helpers"; const mockValidateWeb = validateWeb as jest.Mock; @@ -57,8 +58,8 @@ describe("Scale: large CSV (1,000 rows)", () => { // ─── Scale: wide CSV ───────────────────────────────────────────────────────── -describe("Scale: wide CSV (30 columns)", () => { - test("detects all 30 columns", async () => { +describe("Scale: wide CSV (31 columns)", () => { + test("detects all 31 columns", async () => { const cols = Array.from({ length: 30 }, (_, i) => `col_${i}`); const rows = Array.from({ length: 5 }, (_, i) => Object.fromEntries([["trial_index", i], ...cols.map((c) => [c, i])]), @@ -66,6 +67,7 @@ describe("Scale: wide CSV (30 columns)", () => { const meta = new JsPsychMetadata(); await meta.generate(makeCsv(rows), {}, "csv"); const names = meta.getVariableNames(); + expect(names).toContain("trial_index"); for (const col of cols) expect(names).toContain(col); }); }); @@ -112,16 +114,6 @@ describe("Multi-file: CSV + JSON accumulation", () => { expect(rt.maxValue).toBe(800); }); - test("non-CSV/JSON files are skipped without affecting output", async () => { - const csvRows = [baseRow({ response: "f" })]; - const meta = new JsPsychMetadata(); - await meta.generate(makeCsv(csvRows), {}, "csv"); - // Simulate the DataUpload skip — .txt files never reach generate(); variable count unchanged. - const namesAfterCsv = meta.getVariableNames().length; - expect(namesAfterCsv).toBeGreaterThan(0); - // No second generate() call — count stays the same. - expect(meta.getVariableNames().length).toBe(namesAfterCsv); - }); }); // ─── Type inference ────────────────────────────────────────────────────────── @@ -169,29 +161,9 @@ describe("Many levels", () => { // Regression guard for the mixed CSV + JSON case. JSON-only variables (subject, // response.Q0, response.Q1, element_index, response.value) appear in variableMeasured // but not in any CSV column, so the validator must report VARIABLE_MISSING_FROM_CSV_COLUMNS. -// This reflects the known state before PR #103 lands. +// PR #103 (fix/frontend-missing-datafile) will resolve this; guard remains until merged. describe("Smoke-test-2 regression: VARIABLE_MISSING_FROM_CSV_COLUMNS", () => { - function validatorOutput( - issues: { key: string; reason: string; severity: "error" | "warning"; evidence?: string[] }[], - ) { - return { - issues: new Map( - issues.map((issue) => [ - issue.key, - { - key: issue.key, - reason: issue.reason, - severity: issue.severity, - files: new Map( - (issue.evidence ?? []).map((evidence, i) => [`file${i}`, { evidence }]), - ), - }, - ]), - ), - }; - } - test("JSON-only variables surface as VARIABLE_MISSING_FROM_CSV_COLUMNS", async () => { mockValidateWeb.mockResolvedValue( validatorOutput([ diff --git a/packages/frontend/tests/helpers.ts b/packages/frontend/tests/helpers.ts new file mode 100644 index 0000000..8b19b65 --- /dev/null +++ b/packages/frontend/tests/helpers.ts @@ -0,0 +1,25 @@ +/** Builds a validator output object in the shape validatePsychDS consumes. */ +export function validatorOutput( + issues: { + key: string; + reason: string; + severity: "error" | "warning"; + evidence?: (string | undefined)[]; + }[], +) { + return { + issues: new Map( + issues.map((issue) => [ + issue.key, + { + key: issue.key, + reason: issue.reason, + severity: issue.severity, + files: new Map( + (issue.evidence ?? []).map((evidence, i) => [`file${i}`, { evidence }]), + ), + }, + ]), + ), + }; +} diff --git a/packages/frontend/tests/validatePsychDS.test.ts b/packages/frontend/tests/validatePsychDS.test.ts index eacab5c..0ec9d52 100644 --- a/packages/frontend/tests/validatePsychDS.test.ts +++ b/packages/frontend/tests/validatePsychDS.test.ts @@ -4,6 +4,7 @@ import { validatePsychDS, ValidationUnavailableError, } from "../src/validation/validatePsychDS"; +import { validatorOutput } from "./helpers"; const mockValidateWeb = validateWeb as jest.Mock; @@ -17,31 +18,6 @@ function blobText(blob: Blob): Promise { }); } -/** Builds a validator output object in the shape validatePsychDS consumes. */ -function validatorOutput( - issues: { - key: string; - reason: string; - severity: "error" | "warning"; - evidence?: (string | undefined)[]; - }[], -) { - return { - issues: new Map( - issues.map((issue) => [ - issue.key, - { - key: issue.key, - reason: issue.reason, - severity: issue.severity, - files: new Map( - (issue.evidence ?? []).map((evidence, i) => [`file${i}`, { evidence }]), - ), - }, - ]), - ), - }; -} beforeEach(() => { mockValidateWeb.mockReset(); From c794ccfa7b8017be5d91a72f22b27a9d6e0a47f4 Mon Sep 17 00:00:00 2001 From: Hannah Tsukamoto Date: Mon, 15 Jun 2026 12:03:01 -0400 Subject: [PATCH 4/6] refactor(frontend-tests): address remaining code review feedback - Add VariableMeta interface to replace as-any casts on getVariable() calls - Fix mockFetch to return ok:true, eliminating spurious PluginCache warns instead of suppressing console.warn globally - Remove trailing blank line in multi-file describe block Co-Authored-By: Claude Sonnet 4.6 --- .../tests/dataProcessingStress.test.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/frontend/tests/dataProcessingStress.test.ts b/packages/frontend/tests/dataProcessingStress.test.ts index df50218..5942a5a 100644 --- a/packages/frontend/tests/dataProcessingStress.test.ts +++ b/packages/frontend/tests/dataProcessingStress.test.ts @@ -5,13 +5,19 @@ import { validatorOutput } from "./helpers"; const mockValidateWeb = validateWeb as jest.Mock; -// Silence PluginCache npm fetches and join-key warnings — not under test here. -const mockFetch = jest.fn().mockResolvedValue({ text: () => Promise.resolve("") }); +interface VariableMeta { + minValue?: number; + maxValue?: number; + levels?: unknown[]; + value?: string; +} + +// Mock PluginCache npm fetches — ok:true prevents the "source not found" warn path. +const mockFetch = jest.fn().mockResolvedValue({ ok: true, text: () => Promise.resolve("") }); beforeEach(() => { - (global as any).fetch = mockFetch; + (global as VariableMeta).fetch = mockFetch; mockFetch.mockClear(); mockValidateWeb.mockReset(); - jest.spyOn(console, "warn").mockImplementation(() => {}); }); afterEach(() => jest.restoreAllMocks()); @@ -50,7 +56,7 @@ describe("Scale: large CSV (1,000 rows)", () => { const rows = Array.from({ length: 1000 }, (_, i) => baseRow({ trial_index: i, rt: i + 1 })); const meta = new JsPsychMetadata(); await meta.generate(makeCsv(rows), {}, "csv"); - const rt = meta.getVariable("rt") as any; + const rt = meta.getVariable("rt") as VariableMeta; expect(rt.minValue).toBe(1); expect(rt.maxValue).toBe(1000); }); @@ -109,11 +115,10 @@ describe("Multi-file: CSV + JSON accumulation", () => { await meta.generate(makeCsv(csvRows), {}, "csv"); await meta.generate(JSON.stringify(jsonRows), {}, "json"); - const rt = meta.getVariable("rt") as any; + const rt = meta.getVariable("rt") as VariableMeta; expect(rt.minValue).toBe(100); expect(rt.maxValue).toBe(800); }); - }); // ─── Type inference ────────────────────────────────────────────────────────── @@ -126,7 +131,7 @@ describe("Type inference", () => { ]); const meta = new JsPsychMetadata(); await meta.generate(data, {}, "json"); - expect((meta.getVariable("correct") as any).value).toBe("boolean"); + expect((meta.getVariable("correct") as VariableMeta).value).toBe("boolean"); }); test("null values in a numeric CSV column do not affect range detection", async () => { @@ -137,7 +142,7 @@ describe("Type inference", () => { ]; const meta = new JsPsychMetadata(); await meta.generate(makeCsv(rows), {}, "csv"); - const rt = meta.getVariable("rt") as any; + const rt = meta.getVariable("rt") as VariableMeta; expect(rt.minValue).toBe(500); expect(rt.maxValue).toBe(800); }); @@ -152,7 +157,7 @@ describe("Many levels", () => { const rows = Array.from({ length: 50 }, (_, i) => baseRow({ trial_index: i, stimulus: `stimulus-${i}` })); const meta = new JsPsychMetadata(); await meta.generate(makeCsv(rows), {}, "csv"); - const stim = meta.getVariable("stimulus") as any; + const stim = meta.getVariable("stimulus") as VariableMeta; expect(stim.levels).toHaveLength(50); }); }); From fcbf28d49d69b7c5d225f09b95490bf524434326 Mon Sep 17 00:00:00 2001 From: Hannah Tsukamoto Date: Mon, 15 Jun 2026 12:11:43 -0400 Subject: [PATCH 5/6] fix(frontend-tests): correct global cast for fetch mock assignment The replace_all for VariableMeta accidentally caught the global cast too. Use the precise typeof globalThis & { fetch: jest.Mock } instead. Co-Authored-By: Claude Sonnet 4.6 --- packages/frontend/tests/dataProcessingStress.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/tests/dataProcessingStress.test.ts b/packages/frontend/tests/dataProcessingStress.test.ts index 5942a5a..d6d4a95 100644 --- a/packages/frontend/tests/dataProcessingStress.test.ts +++ b/packages/frontend/tests/dataProcessingStress.test.ts @@ -15,7 +15,7 @@ interface VariableMeta { // Mock PluginCache npm fetches — ok:true prevents the "source not found" warn path. const mockFetch = jest.fn().mockResolvedValue({ ok: true, text: () => Promise.resolve("") }); beforeEach(() => { - (global as VariableMeta).fetch = mockFetch; + (global as typeof globalThis & { fetch: jest.Mock }).fetch = mockFetch; mockFetch.mockClear(); mockValidateWeb.mockReset(); }); From d1c4f5f5d351ac692157faac92c7ab239cdfe705 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 18 Jun 2026 18:52:25 -0400 Subject: [PATCH 6/6] test(frontend): align stress-guard filename to convention + justify layer overlap - Rename dataProcessingStress.test.ts -> dataProcessing.stress.test.ts to match the repo's *.stress.test.ts convention (#104/#106). - Add a header note explaining the intentional overlap with packages/metadata's stress suites: these guard generate() through the frontend bundle's own resolution path, plus frontend-only coverage (multi-file CSV+JSON accumulation, validator partitioning). No changeset: the frontend package is private and unversioned, so changesets ignores it. Co-Authored-By: Claude Opus 4.8 --- ...ocessingStress.test.ts => dataProcessing.stress.test.ts} | 6 ++++++ 1 file changed, 6 insertions(+) rename packages/frontend/tests/{dataProcessingStress.test.ts => dataProcessing.stress.test.ts} (94%) diff --git a/packages/frontend/tests/dataProcessingStress.test.ts b/packages/frontend/tests/dataProcessing.stress.test.ts similarity index 94% rename from packages/frontend/tests/dataProcessingStress.test.ts rename to packages/frontend/tests/dataProcessing.stress.test.ts index d6d4a95..b28478d 100644 --- a/packages/frontend/tests/dataProcessingStress.test.ts +++ b/packages/frontend/tests/dataProcessing.stress.test.ts @@ -1,3 +1,9 @@ +// Frontend-layer stress guards: exercise @jspsych/metadata's generate() and the browser +// validatePsychDS wrapper through the frontend's own dependency resolution. The scale / type- +// inference / many-levels blocks deliberately overlap packages/metadata's *.stress suites — here +// they guard the generate() the *frontend bundle* actually loads (a different resolution path), +// alongside the genuinely frontend-only coverage: multi-file CSV+JSON accumulation and the +// validator error/warning partitioning (Smoke-test-2 regression). import JsPsychMetadata from "@jspsych/metadata"; import { validateWeb } from "psychds-validator/web/psychds-validator.js"; import { validatePsychDS } from "../src/validation/validatePsychDS";