From 670d04adcaaa2124c20d29ac2d1f50aa062582af Mon Sep 17 00:00:00 2001 From: Nicolas Besnard Date: Mon, 8 Dec 2025 18:38:43 +0100 Subject: [PATCH] feat: only --- .changeset/warm-results-design.md | 5 +++ .../fixtures/each-only/each-only.eval.ts | 24 +++++++++++ .../test-modifiers.eval.ts | 35 ++++++++++++++++ .../test-modifiers.eval.ts | 24 +++++++++++ .../tests/test-modifiers.test.ts | 40 +++++++++++++++++++ packages/evalite/src/evalite.ts | 21 +++++++++- packages/evalite/src/run-evalite.ts | 1 + 7 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 .changeset/warm-results-design.md create mode 100644 packages/evalite-tests/tests/fixtures/each-only/each-only.eval.ts create mode 100644 packages/evalite-tests/tests/fixtures/test-modifiers-multiple-only/test-modifiers.eval.ts create mode 100644 packages/evalite-tests/tests/fixtures/test-modifiers-only/test-modifiers.eval.ts diff --git a/.changeset/warm-results-design.md b/.changeset/warm-results-design.md new file mode 100644 index 00000000..d68c20f2 --- /dev/null +++ b/.changeset/warm-results-design.md @@ -0,0 +1,5 @@ +--- +"evalite": minor +--- + +Add only modifier diff --git a/packages/evalite-tests/tests/fixtures/each-only/each-only.eval.ts b/packages/evalite-tests/tests/fixtures/each-only/each-only.eval.ts new file mode 100644 index 00000000..a3cbda3c --- /dev/null +++ b/packages/evalite-tests/tests/fixtures/each-only/each-only.eval.ts @@ -0,0 +1,24 @@ +import { evalite } from "evalite"; + +evalite.each([{ name: "variant-1", input: "v1" }]).only("Only Each Test", { + data: () => { + return [{ input: "only-each", expected: "only-each" }]; + }, + task: function getTask(input: string) { + console.log("task() called in Only Each Test"); + return input; + }, + scorers: [], +}); + +evalite.each([{ name: "variant-1", input: "v1" }])("Regular Each Test", { + data: () => { + return [{ input: "regular-each", expected: "regular-each" }]; + }, + task: function getTask(input: string) { + // This should not be called because another test has .only() + console.log("task() called in Regular Each Test"); + return input; + }, + scorers: [], +}); diff --git a/packages/evalite-tests/tests/fixtures/test-modifiers-multiple-only/test-modifiers.eval.ts b/packages/evalite-tests/tests/fixtures/test-modifiers-multiple-only/test-modifiers.eval.ts new file mode 100644 index 00000000..994add85 --- /dev/null +++ b/packages/evalite-tests/tests/fixtures/test-modifiers-multiple-only/test-modifiers.eval.ts @@ -0,0 +1,35 @@ +import { evalite } from "evalite"; + +evalite.only("Only Test 1", { + data: () => { + return [{ input: "only1", expected: "only1" }]; + }, + task: function getTask(input: string) { + console.log("task() called in Only Test 1"); + return input; + }, + scorers: [], +}); + +evalite.only("Only Test 2", { + data: () => { + return [{ input: "only2", expected: "only2" }]; + }, + task: function getTask(input: string) { + console.log("task() called in Only Test 2"); + return input; + }, + scorers: [], +}); + +evalite("Regular Test", { + data: () => { + return [{ input: "regular", expected: "regular" }]; + }, + task: function getTask(input: string) { + // This should not be called because other tests have .only() + console.log("task() called in Regular Test"); + return input; + }, + scorers: [], +}); diff --git a/packages/evalite-tests/tests/fixtures/test-modifiers-only/test-modifiers.eval.ts b/packages/evalite-tests/tests/fixtures/test-modifiers-only/test-modifiers.eval.ts new file mode 100644 index 00000000..b7c31794 --- /dev/null +++ b/packages/evalite-tests/tests/fixtures/test-modifiers-only/test-modifiers.eval.ts @@ -0,0 +1,24 @@ +import { evalite } from "evalite"; + +evalite.only("Only Test", { + data: () => { + return [{ input: "only", expected: "only" }]; + }, + task: function getTask(input: string) { + console.log("task() called in Only Test"); + return input; + }, + scorers: [], +}); + +evalite("Regular Test", { + data: () => { + return [{ input: "regular", expected: "regular" }]; + }, + task: function getTask(input: string) { + // This should not be called because another test has .only() + console.log("task() called in Regular Test"); + return input; + }, + scorers: [], +}); diff --git a/packages/evalite-tests/tests/test-modifiers.test.ts b/packages/evalite-tests/tests/test-modifiers.test.ts index 85673bf9..1230c49a 100644 --- a/packages/evalite-tests/tests/test-modifiers.test.ts +++ b/packages/evalite-tests/tests/test-modifiers.test.ts @@ -50,3 +50,43 @@ it("should not call opts.data() for a skipped test with .each().skip()", async ( expect(output).toContain("opts.data() called in Regular Each Test"); expect(output).not.toContain("opts.data() called in Skipped Each Test"); }); + +it("should only run tests marked with .only()", async () => { + await using fixture = await loadFixture("test-modifiers-only"); + + await fixture.run({ + mode: "run-once-and-exit", + }); + + const output = fixture.getOutput(); + + expect(output).toContain("task() called in Only Test"); + expect(output).not.toContain("task() called in Regular Test"); +}); + +it("should run all tests marked with .only()", async () => { + await using fixture = await loadFixture("test-modifiers-multiple-only"); + + await fixture.run({ + mode: "run-once-and-exit", + }); + + const output = fixture.getOutput(); + + expect(output).toContain("task() called in Only Test 1"); + expect(output).toContain("task() called in Only Test 2"); + expect(output).not.toContain("task() called in Regular Test"); +}); + +it("should only run tests marked with .each().only()", async () => { + await using fixture = await loadFixture("each-only"); + + await fixture.run({ + mode: "run-once-and-exit", + }); + + const output = fixture.getOutput(); + + expect(output).toContain("task() called in Only Each Test"); + expect(output).not.toContain("task() called in Regular Each Test"); +}); diff --git a/packages/evalite/src/evalite.ts b/packages/evalite/src/evalite.ts index 4329f402..8006c395 100644 --- a/packages/evalite/src/evalite.ts +++ b/packages/evalite/src/evalite.ts @@ -146,13 +146,18 @@ evalite.skip = ( opts: Evalite.RunnerOpts ) => registerEvalite(evalName, opts, { modifier: "skip" }); +evalite.only = ( + evalName: string, + opts: Evalite.RunnerOpts +) => registerEvalite(evalName, opts, { modifier: "only" }); + evalite.each = ( variants: Array<{ name: string; input: TVariant; only?: boolean }> ) => { function createEvals( evalName: string, opts: Evalite.RunnerOpts, - modifier?: "skip" + modifier?: "skip" | "only" ) { const hasOnlyFlag = variants.some((v) => v.only === true); const filteredVariants = hasOnlyFlag @@ -185,6 +190,13 @@ evalite.each = ( return createEvals(evalName, opts, "skip"); }; + eachFn.only = ( + evalName: string, + opts: Evalite.RunnerOpts + ) => { + return createEvals(evalName, opts, "only"); + }; + return eachFn; }; @@ -223,7 +235,12 @@ function registerEvalite( variantGroup?: string; } = {} ) { - const describeFn = vitestOpts.modifier === "skip" ? describe.skip : describe; + const describeFn = + vitestOpts.modifier === "skip" + ? describe.skip + : vitestOpts.modifier === "only" + ? describe.only + : describe; const datasetPromise: Promise> = vitestOpts.modifier === "skip" ? Promise.resolve({ success: true, data: [] }) diff --git a/packages/evalite/src/run-evalite.ts b/packages/evalite/src/run-evalite.ts index 37138462..0d19f27b 100644 --- a/packages/evalite/src/run-evalite.ts +++ b/packages/evalite/src/run-evalite.ts @@ -350,6 +350,7 @@ export const runEvalite = async (opts: { mode: "test", browser: undefined, config: false, + allowOnly: true, }, { ...mergedViteConfig,