Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Testing Helpers] Pull in helpers from qunit-xstate-test, providing better type definitions for ember projects #239

Open
NullVoxPopuli opened this issue May 2, 2020 · 1 comment

Comments

@NullVoxPopuli
Copy link
Contributor

NullVoxPopuli commented May 2, 2020

@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:
image

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

@davidkpiano
Copy link

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
      }
    }
  }
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants