From 8458d5ccda8dea0f8877c08cb5542129d417d8ca Mon Sep 17 00:00:00 2001 From: Weiller Carvalho Date: Sat, 15 Nov 2025 02:44:49 -0300 Subject: [PATCH] fix: surface glob errors and enforce t.try() guard rails issue #3407 --- lib/api.js | 2 +- lib/test.js | 4 ++-- test-tap/api.js | 21 +++++++++++++++++++ .../fixtures/try-guards.js | 19 +++++++++++++++++ test/multiple-implementations/test.js | 6 ++++++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 test/multiple-implementations/fixtures/try-guards.js diff --git a/lib/api.js b/lib/api.js index 92eb7bb4f..741a59fff 100644 --- a/lib/api.js +++ b/lib/api.js @@ -146,7 +146,7 @@ export default class Api extends Emittery { const {providers = []} = this.options; - let testFiles; + let testFiles = []; try { testFiles = await globs.findTests({cwd: this.options.projectDir, ...apiOptions.globs}); if (typeof testFileSelector === 'function') { diff --git a/lib/test.js b/lib/test.js index a14a7c505..d76ede4d7 100644 --- a/lib/test.js +++ b/lib/test.js @@ -135,7 +135,7 @@ class ExecutionContext extends Assertions { if (discarded) { test.saveFirstError(new Error('Can’t commit a result that was previously discarded')); - throw this.testFailure; + throw test.testFailure; } committed = true; @@ -154,7 +154,7 @@ class ExecutionContext extends Assertions { discard({retainLogs = false} = {}) { if (committed) { test.saveFirstError(new Error('Can’t discard a result that was previously committed')); - throw this.testFailure; + throw test.testFailure; } if (discarded) { diff --git a/test-tap/api.js b/test-tap/api.js index 6820efd2d..3cd70a5ae 100644 --- a/test-tap/api.js +++ b/test-tap/api.js @@ -33,6 +33,27 @@ const options = [ ]; for (const opt of options) { + test(`propagates glob errors - workerThreads: ${opt.workerThreads}`, async t => { + const invalidProjectDir = path.join(__dirname, 'fixture', 'fail-fast', 'single-file', 'test.cjs'); + const api = await apiCreator({ + ...opt, + projectDir: invalidProjectDir, + }); + + const errors = []; + api.on('run', plan => { + plan.status.on('stateChange', event => { + if (event.type === 'internal-error') { + errors.push(event.err); + } + }); + }); + + const runStatus = await api.run(); + t.equal(runStatus.stats.internalErrors, 1); + t.equal(errors.length, 1); + t.match(errors[0].message, /cwd.*directory/); + }); test(`fail-fast mode - workerThreads: ${opt.workerThreads} - single file & serial`, async t => { const api = await apiCreator({ ...opt, diff --git a/test/multiple-implementations/fixtures/try-guards.js b/test/multiple-implementations/fixtures/try-guards.js new file mode 100644 index 000000000..033310a98 --- /dev/null +++ b/test/multiple-implementations/fixtures/try-guards.js @@ -0,0 +1,19 @@ +import test from 'ava'; + +test('commit after discard throws', async t => { + const attempt = await t.try(tt => { + tt.pass(); + }); + + attempt.discard(); + attempt.commit(); +}); + +test('discard after commit throws', async t => { + const attempt = await t.try(tt => { + tt.pass(); + }); + + attempt.commit(); + attempt.discard(); +}); diff --git a/test/multiple-implementations/test.js b/test/multiple-implementations/test.js index 1bbeeecbe..1c63668ad 100644 --- a/test/multiple-implementations/test.js +++ b/test/multiple-implementations/test.js @@ -11,3 +11,9 @@ test('t.try()', async t => { const result = await t.throwsAsync(fixture(['try.js'])); t.regex(result.stdout, /Expected an implementation/); }); + +test('t.try() guardrails report errors', async t => { + const result = await t.throwsAsync(fixture(['try-guards.js'])); + t.regex(result.stdout, /Can’t commit a result that was previously discarded/); + t.regex(result.stdout, /Can’t discard a result that was previously committed/); +});