diff --git a/src/testing/README.md b/src/testing/README.md index 12b2a102d..dc19c66af 100644 --- a/src/testing/README.md +++ b/src/testing/README.md @@ -320,13 +320,13 @@ Here we're using the `setChildren()` api on the baseAssertion, and we're using t Assertion Template has the following api's: ``` -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; +insertBefore(selector: string, children: DNode[] | (() => DNode[])): AssertionTemplateResult; +insertAfter(selector: string, children: DNode[] | (() => DNode[])): AssertionTemplateResult; +insertSiblings(selector: string, children: DNode[] | (() => DNode[]), type?: 'before' | 'after'): AssertionTemplateResult; +append(selector: string, children: DNode[] | (() => DNode[])): AssertionTemplateResult; +prepend(selector: string, children: DNode[] | (() => DNode[])): AssertionTemplateResult; +replaceChildren(selector: string, children: DNode[] | (() => DNode[])): AssertionTemplateResult; +setChildren(selector: string, children: DNode[] | (() => 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[]; diff --git a/src/testing/assertionTemplate.ts b/src/testing/assertionTemplate.ts index fb6042b90..ed36e31e5 100644 --- a/src/testing/assertionTemplate.ts +++ b/src/testing/assertionTemplate.ts @@ -6,15 +6,21 @@ import WidgetBase from '../core/WidgetBase'; export type PropertiesComparatorFunction = (actualProperties: any) => any; +export type TemplateChildren = DNode[] | (() => DNode[]); + export interface AssertionTemplateResult { (): DNode | DNode[]; - append(selector: string, children: DNode[]): AssertionTemplateResult; - prepend(selector: string, children: DNode[]): AssertionTemplateResult; - replaceChildren(selector: string, children: DNode[]): AssertionTemplateResult; - insertBefore(selector: string, children: DNode[]): AssertionTemplateResult; - insertAfter(selector: string, children: DNode[]): AssertionTemplateResult; - insertSiblings(selector: string, children: DNode[], type?: 'before' | 'after'): AssertionTemplateResult; - setChildren(selector: string, children: DNode[], type?: 'prepend' | 'replace' | 'append'): AssertionTemplateResult; + append(selector: string, children: TemplateChildren): AssertionTemplateResult; + prepend(selector: string, children: TemplateChildren): AssertionTemplateResult; + replaceChildren(selector: string, children: TemplateChildren): AssertionTemplateResult; + insertBefore(selector: string, children: TemplateChildren): AssertionTemplateResult; + insertAfter(selector: string, children: TemplateChildren): AssertionTemplateResult; + insertSiblings(selector: string, children: TemplateChildren, type?: 'before' | 'after'): AssertionTemplateResult; + setChildren( + selector: string, + children: TemplateChildren, + type?: 'prepend' | 'replace' | 'append' + ): AssertionTemplateResult; setProperty(selector: string, property: string, value: any): AssertionTemplateResult; setProperties(selector: string, value: any | PropertiesComparatorFunction): AssertionTemplateResult; getChildren(selector: string): DNode[]; @@ -74,24 +80,32 @@ export function assertionTemplate(renderFunc: () => DNode | DNode[]) { return render; }); }; - assertionTemplateResult.append = (selector: string, children: DNode[]) => { + assertionTemplateResult.append = (selector: string, children: TemplateChildren) => { return assertionTemplateResult.setChildren(selector, children, 'append'); }; - assertionTemplateResult.prepend = (selector: string, children: DNode[]) => { + assertionTemplateResult.prepend = (selector: string, children: TemplateChildren) => { return assertionTemplateResult.setChildren(selector, children, 'prepend'); }; - assertionTemplateResult.replaceChildren = (selector: string, children: DNode[]) => { + assertionTemplateResult.replaceChildren = (selector: string, children: TemplateChildren) => { return assertionTemplateResult.setChildren(selector, children, 'replace'); }; assertionTemplateResult.setChildren = ( selector: string, - children: DNode[], + children: TemplateChildren, type: 'prepend' | 'replace' | 'append' = 'replace' ) => { + if (Array.isArray(children)) { + console.warn( + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + } return assertionTemplate(() => { const render = renderFunc(); const node = findOne(render, selector); node.children = node.children || []; + if (typeof children === 'function') { + children = children(); + } switch (type) { case 'prepend': node.children = [...children, ...node.children]; @@ -106,23 +120,31 @@ export function assertionTemplate(renderFunc: () => DNode | DNode[]) { return render; }); }; - assertionTemplateResult.insertBefore = (selector: string, children: DNode[]) => { + assertionTemplateResult.insertBefore = (selector: string, children: TemplateChildren) => { return assertionTemplateResult.insertSiblings(selector, children, 'before'); }; - assertionTemplateResult.insertAfter = (selector: string, children: DNode[]) => { + assertionTemplateResult.insertAfter = (selector: string, children: TemplateChildren) => { return assertionTemplateResult.insertSiblings(selector, children, 'after'); }; assertionTemplateResult.insertSiblings = ( selector: string, - children: DNode[], + children: TemplateChildren, type: 'before' | 'after' = 'after' ) => { + if (Array.isArray(children)) { + console.warn( + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + } return assertionTemplate(() => { const render = renderFunc(); const node = findOne(render, selector); const parent = (node as any).parent; const index = parent.children.indexOf(node); let newChildren = [...parent.children]; + if (typeof children === 'function') { + children = children(); + } switch (type) { case 'before': newChildren.splice(index, 0, ...children); diff --git a/tests/testing/unit/assertionTemplate.tsx b/tests/testing/unit/assertionTemplate.tsx index edfe1c762..435b72a9d 100644 --- a/tests/testing/unit/assertionTemplate.tsx +++ b/tests/testing/unit/assertionTemplate.tsx @@ -1,5 +1,6 @@ -const { describe, it } = intern.getInterface('bdd'); +const { describe, it, after, afterEach } = intern.getInterface('bdd'); const { assert } = intern.getPlugin('chai'); +import { stub } from 'sinon'; import { harness } from '../../../src/testing/harness'; import { WidgetBase } from '../../../src/core/WidgetBase'; @@ -78,7 +79,17 @@ const tsxAssertion = assertionTemplate(() => ( )); +let consoleStub = stub(console, 'warn'); + describe('assertionTemplate', () => { + afterEach(() => { + consoleStub.resetHistory(); + }); + + after(() => { + consoleStub.restore(); + }); + it('can get a property', () => { const classes = baseAssertion.getProperty('~root', 'classes'); assert.deepEqual(classes, ['root']); @@ -134,36 +145,102 @@ describe('assertionTemplate', () => { it('can set a child', () => { const h = harness(() => w(MyWidget, { replaceChild: true })); const childAssertion = baseAssertion.setChildren('~header', ['replace']); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + h.expect(childAssertion); + }); + + it('can set a child with a factory function', () => { + const h = harness(() => w(MyWidget, { replaceChild: true })); + const childAssertion = baseAssertion.setChildren('~header', () => ['replace']); h.expect(childAssertion); }); it('can set a child with replace', () => { const h = harness(() => w(MyWidget, { replaceChild: true })); const childAssertion = baseAssertion.replaceChildren('~header', ['replace']); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + h.expect(childAssertion); + }); + + it('can set a child with replace with a factory function', () => { + const h = harness(() => w(MyWidget, { replaceChild: true })); + const childAssertion = baseAssertion.replaceChildren('~header', () => ['replace']); h.expect(childAssertion); }); it('can set a child with prepend', () => { const h = harness(() => w(MyWidget, { prependChild: true })); const childAssertion = baseAssertion.prepend('~header', ['prepend']); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + h.expect(childAssertion); + }); + + it('can set a child with prepend with a factory function', () => { + const h = harness(() => w(MyWidget, { prependChild: true })); + const childAssertion = baseAssertion.prepend('~header', () => ['prepend']); h.expect(childAssertion); }); it('can set a child with append', () => { const h = harness(() => w(MyWidget, { appendChild: true })); const childAssertion = baseAssertion.append('~header', ['append']); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + h.expect(childAssertion); + }); + + it('can set a child with append with a factory function', () => { + const h = harness(() => w(MyWidget, { appendChild: true })); + const childAssertion = baseAssertion.append('~header', () => ['append']); h.expect(childAssertion); }); it('can set children after with insert', () => { const h = harness(() => w(MyWidget, { after: true })); const childAssertion = baseAssertion.insertAfter('ul', [v('span', ['after'])]); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + h.expect(childAssertion); + }); + + it('can set children after with insert with a factory function', () => { + const h = harness(() => w(MyWidget, { after: true })); + const childAssertion = baseAssertion.insertAfter('ul', () => [v('span', ['after'])]); h.expect(childAssertion); }); it('can set children before with insert', () => { const h = harness(() => w(MyWidget, { before: true })); const childAssertion = baseAssertion.insertBefore('ul', [v('span', ['before'])]); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' + ); + h.expect(childAssertion); + }); + + it('can set children before with insert with a factory function', () => { + const h = harness(() => w(MyWidget, { before: true })); + const childAssertion = baseAssertion.insertBefore('ul', () => [v('span', ['before'])]); h.expect(childAssertion); });