Skip to content

Commit

Permalink
feat(vest): Add AbortSignal as a parameter to test (#1079)
Browse files Browse the repository at this point in the history
  • Loading branch information
ealush authored Sep 24, 2023
1 parent 4260f59 commit 2d28ea7
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 5 deletions.
2 changes: 2 additions & 0 deletions packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function IsolateTestBase() {
return {
severity: TestSeverity.Error,
status: TestStatus.UNTESTED,
abortController: new AbortController(),
};
}

Expand All @@ -57,6 +58,7 @@ export type IsolateTestPayload<
severity: TestSeverity;
status: TestStatus;
asyncTest?: AsyncTest;
abortController: AbortController;
};

type CommonTestFields<
Expand Down
1 change: 1 addition & 0 deletions packages/vest/src/core/isolate/IsolateTest/VestTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export class VestTest {

static cancel(test: TIsolateTest): void {
VestTest.setStatus(test, TestStatus.CANCELED);
VestTest.getData(test).abortController.abort(TestStatus.CANCELED);
}

static omit(test: TIsolateTest): void {
Expand Down
4 changes: 3 additions & 1 deletion packages/vest/src/core/test/TestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Maybe } from 'vest-utils';

import { TFieldName } from 'SuiteResultTypes';

export type TestFn = () => TestResult;
type TestFnPayload = { signal: AbortSignal };

export type TestFn = (payload: TestFnPayload) => TestResult;
export type AsyncTest = Promise<void>;
export type TestResult = Maybe<AsyncTest | boolean>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/vest/src/core/test/__tests__/test.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"}"`
);
});

Expand Down
83 changes: 83 additions & 0 deletions packages/vest/src/core/test/__tests__/testFunctionPayload.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as vest from 'vest';

describe('Test Function Payload', () => {
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(callPayload(testFnSync).signal).toBeInstanceOf(AbortSignal);
expect(callPayload(testFnAsync).signal).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(callPayload(testFn).signal.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(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', () => {
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(callPayload(testFn1).signal.aborted).toBe(true);
expect(callPayload(testFn2).signal.aborted).toBe(false);
});
});
});
});

function callPayload(
fn: jest.Mock<any, any, any>,
call: number = 0,
arg: number = 0
) {
return fn.mock.calls[call][arg];
}
4 changes: 2 additions & 2 deletions packages/vest/src/core/test/testLevelFlowControl/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({ signal: abortController.signal });
} catch (error) {
if (shouldUseErrorAsMessage(message, error)) {
VestTest.getData(testObject).message = error;
Expand Down
Original file line number Diff line number Diff line change
@@ -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":{}}}"`;
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
20 changes: 20 additions & 0 deletions website/docs/writing_tests/async_tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,23 @@ test('name', 'Already Taken', async () => {
return await doesUserExist(user);
});
```

## Using AbortSignal

> Since 5.1.0
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.

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.

[More on AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).

```js
test('name', 'Already Taken', async ({ signal }) => {
// ...
});
```

2 comments on commit 2d28ea7

@vercel
Copy link

@vercel vercel bot commented on 2d28ea7 Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

vest – ./website

vest.vercel.app
vest-ealush.vercel.app
vest-git-latest-ealush.vercel.app
vestjs.dev
www.vestjs.dev

@vercel
Copy link

@vercel vercel bot commented on 2d28ea7 Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

vest-next – ./website

vest-next-ealush.vercel.app
vest-next.vercel.app
vest-website.vercel.app
vest-next-git-latest-ealush.vercel.app

Please sign in to comment.