An open API service indexing awesome lists of open source software.

https://github.com/smartive/xstate-test-toolbox

This package contains the helper createTestPlans which can be used with `xstate` and `@xstate/test`.
https://github.com/smartive/xstate-test-toolbox

guards testing tests xstate

Last synced: about 1 year ago
JSON representation

This package contains the helper createTestPlans which can be used with `xstate` and `@xstate/test`.

Awesome Lists containing this project

README

          

# @smartive/xstate-test-toolbox

This package contains the helper `createTestPlans` which can be used with xstate and @xstate/test.

## `createTestPlans`

This function adds the `meta`-property to every state and a test if it is defined within `tests`. (see in example) Beside that it generates all possible test simple path plans for all possible combinations of your guards.

⚠️ Attention: Your statechart must consist of only string references to guards, actions and services otherwise the testing will break.

### Example

```Typescript
// The following snippet does not include all needed imports and code it is intended
// to give you a starting point and an idea how the `createTestPlans`-function can be used.

import { createTestPlans, StatesTestFunctions } from '@smartive/xstate-test-toolbox';
import { FetchInterceptor, mockHeaders, mockResponse, RequestCallCountMock } from '@smartive/testcafe-utils';
import { TestEventsConfig } from '@xstate/test/lib/types';
import { RequestMock } from 'testcafe';
import { Context, machine } from './machine-under-test';
// ...

type TestContext = {
t: TestController,
plan: string,
path: string
};

const fetchInterceptor = new FetchInterceptor({
fetchPeople: /.+swapi\.dev.+\/people\/$/,
fetchMore: /.+swapi\.dev.+\/people\/\?page=.+/,
searchPeople: /.+swapi\.dev.+\/people\/\?search=.+/,
});

const getRequestMocks = (plan: string, path: string): object[] => {
const peopleUrl = /.+swapi\.dev.+\/people\/.*/
if (
plan.includes('NoResults') ||
(plan.includes('Searching') && path.includes('NoResults'))
) {
return [
RequestMock()
.onRequestTo(peopleUrl)
.respond(empty, 200, mockHeaders),
];
}

if (plan.includes('Error')) {
switch (path) {
case 'Pending → error.platform.fetchPeople':
return [
RequestMock().onRequestTo(peopleUrl).respond({}, 400, mockHeaders),
];
case 'Pending → done.invoke.fetchPeople → Idle → END_REACHED → LoadingMore → error.platform.fetchMore':
return [
new RequestCallCountMock(peopleUrl, [
{ body: mockResponse(peoples) },
{ body: mockResponse({}, 400) },
]),
];
case 'Pending → done.invoke.fetchPeople → NoResults → QUERY_DISPATCHED → Searching → error.platform.searchPeople':
return [
RequestMock()
.onRequestTo(fetchInterceptor.interceptUrls.searchPeople)
.respond({}, 400, mockHeaders),
RequestMock()
.onRequestTo(peopleUrl)
.respond(empty, 200, mockHeaders),
];
case 'Pending → done.invoke.fetchPeople → Idle → QUERY_DISPATCHED → Searching → error.platform.searchPeople':
return [
RequestMock()
.onRequestTo(fetchInterceptor.interceptUrls.searchPeople)
.respond({}, 400, mockHeaders),
];
}
}

return [];
};

const tests: StatesTestFunctions = {
Pending: ({ t }) => t.expect(page.spinner.exists).ok(),
Idle: ({ t }) => t.expect(page.listItem.count).gt(0),
LoadingMore: ({ t }) => t.expect(page.listItem.count).gt(1).expect(page.spinner.exists).ok(),
Error: ({ t }) => t.expect(page.error.exists).ok(),
NoResults: ({ t }) => t.expect(page.notify.exists).ok(),
Searching: ({ t }) => t.expect(page.search.value).contains('luke').expect(page.spinner.exists).ok(),
};

const testEvents: TestEventsConfig = {
END_REACHED: ({ t }) => t.hover(page.listItem.nth(9), { speed: 0.8 }),
QUERY_DISPATCHED: ({ t }) => t.typeText(page.search, 'luke', { speed: 0.8 }),
'done.invoke.fetchPeople': fetchInterceptor.resolve('fetchPeople'),
'error.platform.fetchPeople': fetchInterceptor.resolve('fetchPeople'),
'error.platform.fetchMore': fetchInterceptor.resolve('fetchMore'),
'error.platform.searchPeople': fetchInterceptor.resolve('searchPeople'),
};

createTestPlans({
machine,
tests,
testEvents,
// add logLevel: LogLevel.INFO for some output which plans/paths are generated
}).forEach(
({ description: plan, paths }) => {
fixture(plan).page(`http://localhost:3000/peoples`);

paths.forEach(({ test: run, description: path }) => {
test
.clientScripts([fetchInterceptor.clientScript()])
.requestHooks(getRequestMocks(plan, path))(`via ${path} ⬏`, (t) => run({ plan, path, t }));
});
}
);
```