From 92558407a7ef83128362e2efc1019c533c77840e Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 25 Jun 2019 14:57:20 +0100 Subject: [PATCH 1/4] Extend children API to support factory functions, warn on using Arrays --- src/testing/assertionTemplate.ts | 48 +++++++++----- tests/testing/unit/assertionTemplate.tsx | 79 +++++++++++++++++++++++- 2 files changed, 112 insertions(+), 15 deletions(-) diff --git a/src/testing/assertionTemplate.ts b/src/testing/assertionTemplate.ts index fb6042b90..23b74fff9 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,31 @@ 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' ) => { return assertionTemplate(() => { const render = renderFunc(); const node = findOne(render, selector); node.children = node.children || []; + if (typeof children === 'function') { + children = children(); + } else { + console.warn( + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + } switch (type) { case 'prepend': node.children = [...children, ...node.children]; @@ -106,15 +119,15 @@ 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' ) => { return assertionTemplate(() => { @@ -123,6 +136,13 @@ export function assertionTemplate(renderFunc: () => DNode | DNode[]) { const parent = (node as any).parent; const index = parent.children.indexOf(node); let newChildren = [...parent.children]; + if (typeof children === 'function') { + children = children(); + } else { + console.warn( + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + } 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..29f48a235 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']); @@ -135,36 +146,102 @@ describe('assertionTemplate', () => { const h = harness(() => w(MyWidget, { replaceChild: true })); const childAssertion = baseAssertion.setChildren('~header', ['replace']); h.expect(childAssertion); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + }); + + 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']); h.expect(childAssertion); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + }); + + 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']); h.expect(childAssertion); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + }); + + 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']); h.expect(childAssertion); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + }); + + 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'])]); h.expect(childAssertion); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + }); + + 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'])]); h.expect(childAssertion); + assert.isTrue(consoleStub.calledOnce); + assert.strictEqual( + consoleStub.firstCall.args[0], + 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + ); + }); + + 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); }); it('can be used with tsx', () => { From 671112711cb238d38181fa4db670dc798ebc1f84 Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 25 Jun 2019 14:58:20 +0100 Subject: [PATCH 2/4] Update readme --- src/testing/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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[]; From f571411e6f30e12f43314cd6838187924f16f43e Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 25 Jun 2019 15:07:27 +0100 Subject: [PATCH 3/4] change warning --- src/testing/assertionTemplate.ts | 4 ++-- tests/testing/unit/assertionTemplate.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/testing/assertionTemplate.ts b/src/testing/assertionTemplate.ts index 23b74fff9..51525bf53 100644 --- a/src/testing/assertionTemplate.ts +++ b/src/testing/assertionTemplate.ts @@ -102,7 +102,7 @@ export function assertionTemplate(renderFunc: () => DNode | DNode[]) { children = children(); } else { console.warn( - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); } switch (type) { @@ -140,7 +140,7 @@ export function assertionTemplate(renderFunc: () => DNode | DNode[]) { children = children(); } else { console.warn( - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); } switch (type) { diff --git a/tests/testing/unit/assertionTemplate.tsx b/tests/testing/unit/assertionTemplate.tsx index 29f48a235..795e563d5 100644 --- a/tests/testing/unit/assertionTemplate.tsx +++ b/tests/testing/unit/assertionTemplate.tsx @@ -149,7 +149,7 @@ describe('assertionTemplate', () => { assert.isTrue(consoleStub.calledOnce); assert.strictEqual( consoleStub.firstCall.args[0], - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); }); @@ -166,7 +166,7 @@ describe('assertionTemplate', () => { assert.isTrue(consoleStub.calledOnce); assert.strictEqual( consoleStub.firstCall.args[0], - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); }); @@ -183,7 +183,7 @@ describe('assertionTemplate', () => { assert.isTrue(consoleStub.calledOnce); assert.strictEqual( consoleStub.firstCall.args[0], - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); }); @@ -200,7 +200,7 @@ describe('assertionTemplate', () => { assert.isTrue(consoleStub.calledOnce); assert.strictEqual( consoleStub.firstCall.args[0], - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); }); @@ -217,7 +217,7 @@ describe('assertionTemplate', () => { assert.isTrue(consoleStub.calledOnce); assert.strictEqual( consoleStub.firstCall.args[0], - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); }); @@ -234,7 +234,7 @@ describe('assertionTemplate', () => { assert.isTrue(consoleStub.calledOnce); assert.strictEqual( consoleStub.firstCall.args[0], - 'The array API (`children: DNode[]`) has been deprecated. Replacing children should use a factory to avoid issues with mutation.' + 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' ); }); From e6e24b2fccce388ed443da82feab48c87d8fa71a Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 25 Jun 2019 16:05:01 +0100 Subject: [PATCH 4/4] Immediately warn for using array API --- src/testing/assertionTemplate.ts | 18 ++++++++++-------- tests/testing/unit/assertionTemplate.tsx | 12 ++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/testing/assertionTemplate.ts b/src/testing/assertionTemplate.ts index 51525bf53..ed36e31e5 100644 --- a/src/testing/assertionTemplate.ts +++ b/src/testing/assertionTemplate.ts @@ -94,16 +94,17 @@ export function assertionTemplate(renderFunc: () => DNode | 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(); - } else { - console.warn( - 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' - ); } switch (type) { case 'prepend': @@ -130,6 +131,11 @@ export function assertionTemplate(renderFunc: () => DNode | 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); @@ -138,10 +144,6 @@ export function assertionTemplate(renderFunc: () => DNode | DNode[]) { let newChildren = [...parent.children]; if (typeof children === 'function') { children = children(); - } else { - console.warn( - 'The array API (`children: DNode[]`) has been deprecated. Working with children should use a factory to avoid issues with mutation.' - ); } switch (type) { case 'before': diff --git a/tests/testing/unit/assertionTemplate.tsx b/tests/testing/unit/assertionTemplate.tsx index 795e563d5..435b72a9d 100644 --- a/tests/testing/unit/assertionTemplate.tsx +++ b/tests/testing/unit/assertionTemplate.tsx @@ -145,12 +145,12 @@ describe('assertionTemplate', () => { it('can set a child', () => { const h = harness(() => w(MyWidget, { replaceChild: true })); const childAssertion = baseAssertion.setChildren('~header', ['replace']); - h.expect(childAssertion); 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', () => { @@ -162,12 +162,12 @@ describe('assertionTemplate', () => { it('can set a child with replace', () => { const h = harness(() => w(MyWidget, { replaceChild: true })); const childAssertion = baseAssertion.replaceChildren('~header', ['replace']); - h.expect(childAssertion); 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', () => { @@ -179,12 +179,12 @@ describe('assertionTemplate', () => { it('can set a child with prepend', () => { const h = harness(() => w(MyWidget, { prependChild: true })); const childAssertion = baseAssertion.prepend('~header', ['prepend']); - h.expect(childAssertion); 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', () => { @@ -196,12 +196,12 @@ describe('assertionTemplate', () => { it('can set a child with append', () => { const h = harness(() => w(MyWidget, { appendChild: true })); const childAssertion = baseAssertion.append('~header', ['append']); - h.expect(childAssertion); 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', () => { @@ -213,12 +213,12 @@ describe('assertionTemplate', () => { it('can set children after with insert', () => { const h = harness(() => w(MyWidget, { after: true })); const childAssertion = baseAssertion.insertAfter('ul', [v('span', ['after'])]); - h.expect(childAssertion); 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', () => { @@ -230,12 +230,12 @@ describe('assertionTemplate', () => { it('can set children before with insert', () => { const h = harness(() => w(MyWidget, { before: true })); const childAssertion = baseAssertion.insertBefore('ul', [v('span', ['before'])]); - h.expect(childAssertion); 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', () => {