ember-statecharts
ember-statecharts copied to clipboard
[Testing Helpers] Pull in helpers from qunit-xstate-test, providing better type definitions for ember projects
@sukima's project here: https://github.com/sukima/qunit-xstate-test is great in that it eliminates testing boilerplate from the most comment way of testing using statecharts.
However, it's framework-agnostic (which is great!), but that means that the type definitions don't quite match for what we need in an Ember context (things like assigning the correct 'this', etc).
I've made the following changes here to support Ember w/ TypeScript:
import { module, test } from 'qunit';
/* global QUnit */
import { TestModel } from '@xstate/test';
import { TestPlan } from '@xstate/test/lib/types';
import { TestContext as EmberTestContext } from 'ember-test-helpers';
type TestCallbackFn<TestContext, TContext, TReturn> = (
this: EmberTestContext,
assert: Assert,
path: TestPlan<TestContext, TContext>['paths'][0]
) => TReturn;
export const testShortestPaths = <TestContext, TContext, TReturn>(
testModel: TestModel<TestContext, TContext>,
testCallback: TestCallbackFn<TestContext, TContext, TReturn>
) => {
testModel.getShortestPathPlans().forEach((plan) => {
module(plan.description, function () {
plan.paths.forEach((path) => {
test(path.description, function (assert) {
return testCallback.bind(this)(assert, path);
});
});
});
});
};
Only 2 notable changes for testShortestPaths
(originally here):
- the
TestCallbackFn
has a 'this' type, which matches our ember test type. - the callback has the correct
this
bound to it.
So what does this change get us?
Auto-completion of all our helpers:
For this to work well, what does the testModel look like?
interface TestMachineContext {
assert: Assert;
owner: ApplicationInstance;
}
const testModel = createModel<TestMachineContext, {}>(
Machine({
id: 'login-test',
initial: 'begin',
states: {
begin: {
on: {
SCAN_LOGIN_VALID: 'scannedValidLoginQR',
SCAN_LOGIN_INVALID: 'begin',
SCAN_INVALID: 'begin',
},
meta: {
async test({ assert }) {
// assertions
},
},
},
scannedValidLoginQR: {},
},
})
).withEvents({
SCAN_LOGIN_VALID: {
async exec({ owner }) => {
scanQR(owner, ['login', { pub: 'abcdef123', name: 'General Kenobi' }]);
},
},
// ...
});
Note, I think that maybe the args to meta.test
are invalid (the reason why type inference doesn't work here).
This'd be upstream in XState though.
cc @davidkpiano re: meta.test types
This is a planned addition (re: adding types for meta
), and I might make it so that @xstate/test
version 1.0 would look more like:
const testModel = createModel({
// ... test machine definition goes directly in here
states: {
loading: {
// test is a direct property of the state
async test() {
// assertion
}
}
}
})