dojo dragon main logo

Assertion templates

Assertion templates provide a reusable base to assert against a widget's entire render output, but allow portions to be modified as needed between several test executions. This means common elements that do not change across multiple tests can be abstracted and defined once and reused in multiple locations.

To use assertion templates first import the module:

import assertionTemplate from '@dojo/framework/testing/assertionTemplate';

A base assertion should be created which defines the widget's default render state. Given the following widget:

src/widgets/Profile.tsx

import { create, tsx } from '@dojo/framework/core/vdom';

import * as css from './styles/Profile.m.css';

export interface ProfileProperties {
    username?: string;
}

const factory = create().properties<ProfileProperties>();

const Profile = factory(function Profile({ properties }) {
    const { username } = properties();
    return <h1 classes={[css.root]}>{`Welcome ${username || 'Stranger'}!`}</h1>;
});

export default Profile;

The base assertion might look like:

tests/unit/widgets/Profile.tsx

const { describe, it } = intern.getInterface('bdd');
import harness from '@dojo/framework/testing/harness';
import assertionTemplate from '@dojo/framework/testing/assertionTemplate';
import { tsx } from '@dojo/framework/core/vdom';

import Profile from '../../../src/widgets/Profile';
import * as css from '../../../src/widgets/Profile.m.css';

const profileAssertion = assertionTemplate(() => (
    <h1 classes={[css.root]} assertion-key="welcome">
        Welcome Stranger!
    </h1>
));

and in a test would look like:

tests/unit/widgets/Profile.tsx

const profileAssertion = assertionTemplate(() => (
    <h1 classes={[css.root]} assertion-key="welcome">
        Welcome Stranger!
    </h1>
));

describe('Profile', () => {
    it('default renders correctly', () => {
        const h = harness(() => <Profile />);
        h.expect(profileAssertion);
    });
});

To test the scenario of a username property being passed to the Profile, the assertion template can be parameterized such as:

tests/unit/widgets/Profile.tsx

describe('Profile', () => {
    ...

    it('renders given username correctly', () => {
        // update the expected result with a given username
        const namedAssertion = profileAssertion.setChildren('~welcome', () => [
            'Welcome Kel Varnsen!'
        ]);
        const h = harness(() => <Profile username="Kel Varnsen" />);
        h.expect(namedAssertion);
    });
});

Here the setChildren() api is used on the baseAssertion, and the special ~ selector allows finding a node with a key of ~welcome. The assertion-key property (or when using w() or v()functions, ~key) is a special property on assertion templates that will be erased at assertion time so it doesn't show up when matching the renders. This allows the assertion templates to easily select nodes without having to augment the actual widget render function. Once the welcome node is found, its children are overridden to a new value of ['Welcome Kel Varnsen!'], and the resulting template is then used in h.expect. It's important to note that assertion templates always return a new assertion template when setting a value. This ensures that an existing template is not accidentally mutated, which would cause other tests to potentially fail, and allows construction of layered templates that incrementally build on each other.

Assertion template has the following API:

insertBefore(selector: string, children: () => DNode[]): AssertionTemplateResult;
insertAfter(selector: string, children: () => DNode[]): AssertionTemplateResult;
insertSiblings(selector: string, children: () => DNode[], type?: 'before' | 'after'): AssertionTemplateResult;
append(selector: string, children: () => DNode[]): AssertionTemplateResult;
prepend(selector: string, children: () => DNode[]): AssertionTemplateResult;
replaceChildren(selector: string, children: () => DNode[]): AssertionTemplateResult;
setChildren(selector: string, children: () => DNode[], type?: 'prepend' | 'replace' | 'append'): AssertionTemplateResult;
setProperty(selector: string, property: string, value: any): AssertionTemplateResult;
setProperties(selector: string, value: any | PropertiesComparatorFunction): AssertionTemplateResult;
getChildren(selector: string): DNode[];
getProperty(selector: string, property: string): any;
getProperties(selector: string): any;
replace(selector: string, node: DNode): AssertionTemplateResult;
remove(selector: string): AssertionTemplateResult;