From 53504468f22f3d9ac0b24c34786df0ad9bd55f92 Mon Sep 17 00:00:00 2001 From: Evyatar Date: Sun, 24 Sep 2023 11:07:29 +0200 Subject: [PATCH 1/5] add abort signal --- .../core/isolate/IsolateTest/IsolateTest.ts | 2 ++ .../src/core/isolate/IsolateTest/VestTest.ts | 1 + packages/vest/src/core/test/TestTypes.ts | 2 +- .../__snapshots__/IsolateTest.test.ts.snap | 18 ++++++++++ .../__tests__/__snapshots__/test.test.ts.snap | 27 ++++++++++++++ .../vest/src/core/test/__tests__/test.test.ts | 2 +- .../core/test/testLevelFlowControl/runTest.ts | 4 +-- .../SuiteSerializer.test.ts.snap | 2 +- .../__snapshots__/staticSuite.test.ts.snap | 36 +++++++++++++++++++ 9 files changed, 89 insertions(+), 5 deletions(-) diff --git a/packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts b/packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts index 84fea2062..afb4fa2ab 100644 --- a/packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts +++ b/packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts @@ -47,6 +47,7 @@ export function IsolateTestBase() { return { severity: TestSeverity.Error, status: TestStatus.UNTESTED, + abortController: new AbortController(), }; } @@ -57,6 +58,7 @@ export type IsolateTestPayload< severity: TestSeverity; status: TestStatus; asyncTest?: AsyncTest; + abortController: AbortController; }; type CommonTestFields< diff --git a/packages/vest/src/core/isolate/IsolateTest/VestTest.ts b/packages/vest/src/core/isolate/IsolateTest/VestTest.ts index a87725d86..0dbae7305 100644 --- a/packages/vest/src/core/isolate/IsolateTest/VestTest.ts +++ b/packages/vest/src/core/isolate/IsolateTest/VestTest.ts @@ -142,6 +142,7 @@ export class VestTest { static cancel(test: TIsolateTest): void { VestTest.setStatus(test, TestStatus.CANCELED); + VestTest.getData(test).abortController.abort(); } static omit(test: TIsolateTest): void { diff --git a/packages/vest/src/core/test/TestTypes.ts b/packages/vest/src/core/test/TestTypes.ts index becacb5f4..3c9c4c197 100644 --- a/packages/vest/src/core/test/TestTypes.ts +++ b/packages/vest/src/core/test/TestTypes.ts @@ -2,7 +2,7 @@ import { Maybe } from 'vest-utils'; import { TFieldName } from 'SuiteResultTypes'; -export type TestFn = () => TestResult; +export type TestFn = (abortSignal?: AbortSignal) => TestResult; export type AsyncTest = Promise; export type TestResult = Maybe; diff --git a/packages/vest/src/core/test/__tests__/__snapshots__/IsolateTest.test.ts.snap b/packages/vest/src/core/test/__tests__/__snapshots__/IsolateTest.test.ts.snap index f6af009a1..908366a58 100644 --- a/packages/vest/src/core/test/__tests__/__snapshots__/IsolateTest.test.ts.snap +++ b/packages/vest/src/core/test/__tests__/__snapshots__/IsolateTest.test.ts.snap @@ -5,6 +5,15 @@ exports[`IsolateTest TestObject constructor 1`] = ` "$type": "UnitTest", "children": [], "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "unicycle", "message": "I am Root.", "severity": "error", @@ -24,6 +33,15 @@ exports[`IsolateTest testObject.warn Should mark the test as warning 1`] = ` "$type": "UnitTest", "children": [], "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "unicycle", "message": "I am Root.", "severity": "warning", diff --git a/packages/vest/src/core/test/__tests__/__snapshots__/test.test.ts.snap b/packages/vest/src/core/test/__tests__/__snapshots__/test.test.ts.snap index 9fda6e6da..07db22bb4 100644 --- a/packages/vest/src/core/test/__tests__/__snapshots__/test.test.ts.snap +++ b/packages/vest/src/core/test/__tests__/__snapshots__/test.test.ts.snap @@ -6,6 +6,15 @@ exports[`Test Vest's \`test\` function test params creates a test without a key "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "field_name", "message": "failure message", "severity": "error", @@ -73,6 +82,15 @@ exports[`Test Vest's \`test\` function test params creates a test without a mess "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "field_name", "severity": "error", "status": "PASSING", @@ -141,6 +159,15 @@ exports[`Test Vest's \`test\` function test params creates a test without a mess "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "field_name", "severity": "error", "status": "PASSING", diff --git a/packages/vest/src/core/test/__tests__/test.test.ts b/packages/vest/src/core/test/__tests__/test.test.ts index e75401367..a60229813 100644 --- a/packages/vest/src/core/test/__tests__/test.test.ts +++ b/packages/vest/src/core/test/__tests__/test.test.ts @@ -193,7 +193,7 @@ describe("Test Vest's `test` function", () => { expect(testObject.key).toBe('keyboardcat'); expect(testObject.data.message).toBe('failure message'); expect(IsolateSerializer.serialize(testObject)).toMatchInlineSnapshot( - `"{"$":"Test","D":{"severity":"error","status":"PASSING","fieldName":"field_name","message":"failure message"},"k":"keyboardcat"}"` + `"{"$":"Test","D":{"severity":"error","status":"PASSING","abortController":{},"fieldName":"field_name","message":"failure message"},"k":"keyboardcat"}"` ); }); diff --git a/packages/vest/src/core/test/testLevelFlowControl/runTest.ts b/packages/vest/src/core/test/testLevelFlowControl/runTest.ts index f9ea2870c..9a1f450e8 100644 --- a/packages/vest/src/core/test/testLevelFlowControl/runTest.ts +++ b/packages/vest/src/core/test/testLevelFlowControl/runTest.ts @@ -40,10 +40,10 @@ function runSyncTest(testObject: TIsolateTest): TestResult { return SuiteContext.run({ currentTest: testObject }, () => { let result: TestResult; - const { message, testFn } = VestTest.getData(testObject); + const { message, testFn, abortController } = VestTest.getData(testObject); try { - result = testFn(); + result = testFn(abortController.signal); } catch (error) { if (shouldUseErrorAsMessage(message, error)) { VestTest.getData(testObject).message = error; diff --git a/packages/vest/src/exports/__tests__/__snapshots__/SuiteSerializer.test.ts.snap b/packages/vest/src/exports/__tests__/__snapshots__/SuiteSerializer.test.ts.snap index 7b418ded9..fe12a2c5b 100644 --- a/packages/vest/src/exports/__tests__/__snapshots__/SuiteSerializer.test.ts.snap +++ b/packages/vest/src/exports/__tests__/__snapshots__/SuiteSerializer.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SuiteSerializer Should produce a valid serialized dump 1`] = `"{"children":[{"$":"Focused","D":{"focusMode":0,"match":["field_1"],"matchAll":false}},{"$":"Test","D":{"severity":"error","status":"FAILED","fieldName":"field_1","message":"field_1_message"}},{"$":"Test","D":{"severity":"error","status":"SKIPPED","fieldName":"field_2","message":"field_2_message"}},{"children":[{"$":"Test","D":{"severity":"error","status":"SKIPPED","fieldName":"field_3","groupName":"group_1","message":"field_3_message_1"}},{"$":"Test","D":{"severity":"error","status":"SKIPPED","fieldName":"field_3","groupName":"group_1","message":"field_3_message_2"}},{"$":"Test","D":{"severity":"error","status":"SKIPPED","fieldName":"field_4","groupName":"group_1","message":"field_4_message"}}],"$":"Group","D":{}},{"children":[{"$":"Test","D":{"severity":"error","status":"SKIPPED","fieldName":"field_5","message":"field_5_message"}}],"$":"SkipWhen","D":{}}],"$":"Suite","D":{"optional":{}}}"`; +exports[`SuiteSerializer Should produce a valid serialized dump 1`] = `"{"children":[{"$":"Focused","D":{"focusMode":0,"match":["field_1"],"matchAll":false}},{"$":"Test","D":{"severity":"error","status":"FAILED","abortController":{},"fieldName":"field_1","message":"field_1_message"}},{"$":"Test","D":{"severity":"error","status":"SKIPPED","abortController":{},"fieldName":"field_2","message":"field_2_message"}},{"children":[{"$":"Test","D":{"severity":"error","status":"SKIPPED","abortController":{},"fieldName":"field_3","groupName":"group_1","message":"field_3_message_1"}},{"$":"Test","D":{"severity":"error","status":"SKIPPED","abortController":{},"fieldName":"field_3","groupName":"group_1","message":"field_3_message_2"}},{"$":"Test","D":{"severity":"error","status":"SKIPPED","abortController":{},"fieldName":"field_4","groupName":"group_1","message":"field_4_message"}}],"$":"Group","D":{}},{"children":[{"$":"Test","D":{"severity":"error","status":"SKIPPED","abortController":{},"fieldName":"field_5","message":"field_5_message"}}],"$":"SkipWhen","D":{}}],"$":"Suite","D":{"optional":{}}}"`; diff --git a/packages/vest/src/suite/__tests__/__snapshots__/staticSuite.test.ts.snap b/packages/vest/src/suite/__tests__/__snapshots__/staticSuite.test.ts.snap index 5e2ada75e..c81bc15b1 100644 --- a/packages/vest/src/suite/__tests__/__snapshots__/staticSuite.test.ts.snap +++ b/packages/vest/src/suite/__tests__/__snapshots__/staticSuite.test.ts.snap @@ -10,6 +10,15 @@ exports[`staticSuite dump should output a dump of the suite 1`] = ` "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "t1", "severity": "error", "status": "FAILED", @@ -25,6 +34,15 @@ exports[`staticSuite dump should output a dump of the suite 1`] = ` "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "t2", "severity": "error", "status": "FAILED", @@ -44,6 +62,15 @@ exports[`staticSuite dump should output a dump of the suite 1`] = ` "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "t1", "groupName": "g1", "severity": "error", @@ -60,6 +87,15 @@ exports[`staticSuite dump should output a dump of the suite 1`] = ` "allowReorder": undefined, "children": null, "data": { + "abortController": AbortController { + Symbol(signal): AbortSignal { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kAborted): false, + Symbol(kReason): undefined, + }, + }, "fieldName": "t3", "groupName": "g1", "severity": "error", From 21c11ab575aa23d8f0aa0603f56b6d544e7663d0 Mon Sep 17 00:00:00 2001 From: Evyatar Date: Sun, 24 Sep 2023 13:16:41 +0200 Subject: [PATCH 2/5] Add AbortSignalTests --- .../core/test/__tests__/asyncTests.test.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 packages/vest/src/core/test/__tests__/asyncTests.test.ts diff --git a/packages/vest/src/core/test/__tests__/asyncTests.test.ts b/packages/vest/src/core/test/__tests__/asyncTests.test.ts new file mode 100644 index 000000000..01d9ebb09 --- /dev/null +++ b/packages/vest/src/core/test/__tests__/asyncTests.test.ts @@ -0,0 +1,64 @@ +import * as vest from 'vest'; + +describe('AsyncTests', () => { + describe('AbortSignal', () => { + it('Should pass abort signal to test functions', () => { + const testFnSync = jest.fn(); + const testFnAsync = jest.fn().mockResolvedValue(undefined); + const suite = vest.create(() => { + vest.test('field_1', testFnSync); + vest.test('field_2', testFnAsync); + }); + suite(); + + expect(testFnSync.mock.calls[0][0]).toBeInstanceOf(AbortSignal); + expect(testFnAsync.mock.calls[0][0]).toBeInstanceOf(AbortSignal); + }); + + describe('When test is not canceled', () => { + it('Should proceed without aborting the test', async () => { + const testFn = jest.fn().mockResolvedValue(undefined); + const suite = vest.create(() => { + vest.test('field_1', testFn); + }); + suite(); + + await expect(testFn.mock.calls[0][0].aborted).toBe(false); + }); + }); + + describe('When test is canceled', () => { + it('Should abort the test', async () => { + const testFn = jest.fn().mockResolvedValue(undefined); + const suite = vest.create(() => { + vest.test('field_1', testFn); + }); + suite(); + suite(); + + await expect(testFn.mock.calls[0][0].aborted).toBe(true); + await expect(testFn.mock.calls[1][0].aborted).toBe(false); + }); + }); + + describe('Multiple async tests', () => { + it('Should abort only the canceled test', async () => { + const testFn1 = jest.fn().mockResolvedValue(undefined); + const testFn2 = jest.fn().mockResolvedValue(undefined); + + const suite = vest.create((only?: string) => { + vest.only(only); + + vest.test('field_1', testFn1); + vest.test('field_2', testFn2); + }); + + suite(); + suite('field_1'); + + await expect(testFn1.mock.calls[0][0].aborted).toBe(true); + expect(testFn2.mock.calls[0][0].aborted).toBe(false); + }); + }); + }); +}); From 54e85852295aada88c1565955bf8c61cea56bdf4 Mon Sep 17 00:00:00 2001 From: Evyatar Date: Sun, 24 Sep 2023 13:23:03 +0200 Subject: [PATCH 3/5] Add docs --- website/docs/writing_tests/async_tests.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/website/docs/writing_tests/async_tests.md b/website/docs/writing_tests/async_tests.md index e1f980922..4a16e745e 100644 --- a/website/docs/writing_tests/async_tests.md +++ b/website/docs/writing_tests/async_tests.md @@ -20,3 +20,21 @@ test('name', 'Already Taken', async () => { return await doesUserExist(user); }); ``` + +## Using AbortSignal + +> Since 5.1.0 + +Each Vest test is passed as an argument an `AbortSignal` object. Vest internally sets the AbortSignal `aborted` property to true when the test is canceled. + +A test is canceled when running the same test again before its previous run has completed. + +You can use the AbortSignal to stop the execution of your async test, or pass it to your fetch request. + +[Read more on AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). + +```js +test('name', 'Already Taken', async signal => { + // ... +}); +``` From 5f9e3a822ba2799ef9eed67d8ee38f01c4e35522 Mon Sep 17 00:00:00 2001 From: Evyatar Date: Sun, 24 Sep 2023 20:39:30 +0200 Subject: [PATCH 4/5] Modify to use an object --- packages/vest/src/core/test/TestTypes.ts | 4 +++- ...ncTests.test.ts => testFunctionPayload.ts} | 24 ++++++++++++------- .../core/test/testLevelFlowControl/runTest.ts | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) rename packages/vest/src/core/test/__tests__/{asyncTests.test.ts => testFunctionPayload.ts} (68%) diff --git a/packages/vest/src/core/test/TestTypes.ts b/packages/vest/src/core/test/TestTypes.ts index 3c9c4c197..3b966e1bf 100644 --- a/packages/vest/src/core/test/TestTypes.ts +++ b/packages/vest/src/core/test/TestTypes.ts @@ -2,7 +2,9 @@ import { Maybe } from 'vest-utils'; import { TFieldName } from 'SuiteResultTypes'; -export type TestFn = (abortSignal?: AbortSignal) => TestResult; +type TestFnPayload = { signal: AbortSignal }; + +export type TestFn = (payload: TestFnPayload) => TestResult; export type AsyncTest = Promise; export type TestResult = Maybe; diff --git a/packages/vest/src/core/test/__tests__/asyncTests.test.ts b/packages/vest/src/core/test/__tests__/testFunctionPayload.ts similarity index 68% rename from packages/vest/src/core/test/__tests__/asyncTests.test.ts rename to packages/vest/src/core/test/__tests__/testFunctionPayload.ts index 01d9ebb09..14ad168d1 100644 --- a/packages/vest/src/core/test/__tests__/asyncTests.test.ts +++ b/packages/vest/src/core/test/__tests__/testFunctionPayload.ts @@ -1,6 +1,6 @@ import * as vest from 'vest'; -describe('AsyncTests', () => { +describe('Test Function Payload', () => { describe('AbortSignal', () => { it('Should pass abort signal to test functions', () => { const testFnSync = jest.fn(); @@ -11,8 +11,8 @@ describe('AsyncTests', () => { }); suite(); - expect(testFnSync.mock.calls[0][0]).toBeInstanceOf(AbortSignal); - expect(testFnAsync.mock.calls[0][0]).toBeInstanceOf(AbortSignal); + expect(callPayload(testFnSync).signal).toBeInstanceOf(AbortSignal); + expect(callPayload(testFnAsync).signal).toBeInstanceOf(AbortSignal); }); describe('When test is not canceled', () => { @@ -23,7 +23,7 @@ describe('AsyncTests', () => { }); suite(); - await expect(testFn.mock.calls[0][0].aborted).toBe(false); + await expect(callPayload(testFn).signal.aborted).toBe(false); }); }); @@ -36,8 +36,8 @@ describe('AsyncTests', () => { suite(); suite(); - await expect(testFn.mock.calls[0][0].aborted).toBe(true); - await expect(testFn.mock.calls[1][0].aborted).toBe(false); + await expect(callPayload(testFn).signal.aborted).toBe(true); + await expect(callPayload(testFn, 1, 0).signal.aborted).toBe(false); }); }); @@ -56,9 +56,17 @@ describe('AsyncTests', () => { suite(); suite('field_1'); - await expect(testFn1.mock.calls[0][0].aborted).toBe(true); - expect(testFn2.mock.calls[0][0].aborted).toBe(false); + await expect(callPayload(testFn1).signal.aborted).toBe(true); + expect(callPayload(testFn2).signal.aborted).toBe(false); }); }); }); }); + +function callPayload( + fn: jest.Mock, + call: number = 0, + arg: number = 0 +) { + return fn.mock.calls[call][arg]; +} diff --git a/packages/vest/src/core/test/testLevelFlowControl/runTest.ts b/packages/vest/src/core/test/testLevelFlowControl/runTest.ts index 9a1f450e8..75020789e 100644 --- a/packages/vest/src/core/test/testLevelFlowControl/runTest.ts +++ b/packages/vest/src/core/test/testLevelFlowControl/runTest.ts @@ -43,7 +43,7 @@ function runSyncTest(testObject: TIsolateTest): TestResult { const { message, testFn, abortController } = VestTest.getData(testObject); try { - result = testFn(abortController.signal); + result = testFn({ signal: abortController.signal }); } catch (error) { if (shouldUseErrorAsMessage(message, error)) { VestTest.getData(testObject).message = error; From 608822813d2b003e51a2e2adcedc1f21ec13a1cb Mon Sep 17 00:00:00 2001 From: Evyatar Date: Sun, 24 Sep 2023 20:51:50 +0200 Subject: [PATCH 5/5] Add abort reason and update docs --- .../vest/src/core/isolate/IsolateTest/VestTest.ts | 2 +- ...FunctionPayload.ts => testFunctionPayload.test.ts} | 11 +++++++++++ website/docs/writing_tests/async_tests.md | 10 ++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) rename packages/vest/src/core/test/__tests__/{testFunctionPayload.ts => testFunctionPayload.test.ts} (86%) diff --git a/packages/vest/src/core/isolate/IsolateTest/VestTest.ts b/packages/vest/src/core/isolate/IsolateTest/VestTest.ts index 0dbae7305..ee65bac1e 100644 --- a/packages/vest/src/core/isolate/IsolateTest/VestTest.ts +++ b/packages/vest/src/core/isolate/IsolateTest/VestTest.ts @@ -142,7 +142,7 @@ export class VestTest { static cancel(test: TIsolateTest): void { VestTest.setStatus(test, TestStatus.CANCELED); - VestTest.getData(test).abortController.abort(); + VestTest.getData(test).abortController.abort(TestStatus.CANCELED); } static omit(test: TIsolateTest): void { diff --git a/packages/vest/src/core/test/__tests__/testFunctionPayload.ts b/packages/vest/src/core/test/__tests__/testFunctionPayload.test.ts similarity index 86% rename from packages/vest/src/core/test/__tests__/testFunctionPayload.ts rename to packages/vest/src/core/test/__tests__/testFunctionPayload.test.ts index 14ad168d1..e34248f8a 100644 --- a/packages/vest/src/core/test/__tests__/testFunctionPayload.ts +++ b/packages/vest/src/core/test/__tests__/testFunctionPayload.test.ts @@ -39,6 +39,17 @@ describe('Test Function Payload', () => { await expect(callPayload(testFn).signal.aborted).toBe(true); await expect(callPayload(testFn, 1, 0).signal.aborted).toBe(false); }); + + it('Should set the reason to `canceled`', async () => { + const testFn = jest.fn().mockResolvedValue(undefined); + const suite = vest.create(() => { + vest.test('field_1', testFn); + }); + suite(); + suite(); + + await expect(callPayload(testFn).signal.reason).toBe('CANCELED'); + }); }); describe('Multiple async tests', () => { diff --git a/website/docs/writing_tests/async_tests.md b/website/docs/writing_tests/async_tests.md index 4a16e745e..527c2e268 100644 --- a/website/docs/writing_tests/async_tests.md +++ b/website/docs/writing_tests/async_tests.md @@ -25,16 +25,18 @@ test('name', 'Already Taken', async () => { > Since 5.1.0 -Each Vest test is passed as an argument an `AbortSignal` object. Vest internally sets the AbortSignal `aborted` property to true when the test is canceled. +Each test function is passed an object with a `signal` property. This signal is an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) which can be used to terminate your async operations once a test is canceled. -A test is canceled when running the same test again before its previous run has completed. +The AbortSignal has a boolean `aborted` property, by which you can determine whether the test was canceled or not. + +A test gets canceled when running the same test again before its previous run has completed. You can use the AbortSignal to stop the execution of your async test, or pass it to your fetch request. -[Read more on AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). +[More on AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). ```js -test('name', 'Already Taken', async signal => { +test('name', 'Already Taken', async ({ signal }) => { // ... }); ```