Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Fix Edge failures #680

Merged
merged 7 commits into from
Sep 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 22 additions & 32 deletions src/WidgetBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ export class WidgetBase<P = WidgetProperties, C extends DNode = DNode> extends E

private _numRootNodes = 0;

private _rootNodeKeys: object[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a root node key really an object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got a typing error when i set it to string[]. The typedoc popup said that properties.key is an object.
screenshot 2017-09-15 09 44 15


/**
* @constructor
*/
Expand Down Expand Up @@ -239,16 +241,6 @@ export class WidgetBase<P = WidgetProperties, C extends DNode = DNode> extends E
projectionOptions: ProjectionOptions,
vnodeSelector: string,
properties: VNodeProperties
): void {
this._nodeHandler.add(element, properties);
this.onElementCreated(element, String(properties.key));
}

private _afterRootCreateCallback(
element: HTMLElement,
projectionOptions: ProjectionOptions,
vnodeSelector: string,
properties: VNodeProperties
): void {
this._addElementToNodeHandler(element, projectionOptions, properties);
this.onElementCreated(element, String(properties.key));
Expand All @@ -262,36 +254,32 @@ export class WidgetBase<P = WidgetProperties, C extends DNode = DNode> extends E
projectionOptions: ProjectionOptions,
vnodeSelector: string,
properties: VNodeProperties
): void {
this._nodeHandler.add(element, properties);
this.onElementUpdated(element, String(properties.key));
}

private _afterRootUpdateCallback(
element: HTMLElement,
projectionOptions: ProjectionOptions,
vnodeSelector: string,
properties: VNodeProperties
): void {
this._addElementToNodeHandler(element, projectionOptions, properties);
this.onElementUpdated(element, String(properties.key));
}

private _addElementToNodeHandler(element: HTMLElement, projectionOptions: ProjectionOptions, properties: VNodeProperties) {
this._currentRootNode++;
const isLastRootNode = (this._currentRootNode === this._numRootNodes);

if (this._projectorAttachEvent === undefined) {
this._projectorAttachEvent = projectionOptions.nodeEvent.on('rendered', () => {
this._nodeHandler.addProjector();
});
this.own(this._projectorAttachEvent);
const isRootNode = !properties.key || this._rootNodeKeys.indexOf(properties.key) > -1;
const hasKey = !!properties.key;
let isLastRootNode = false;

if (isRootNode) {
this._currentRootNode++;
isLastRootNode = (this._currentRootNode === this._numRootNodes);

if (this._projectorAttachEvent === undefined) {
this._projectorAttachEvent = projectionOptions.nodeEvent.on('rendered', () => {
this._nodeHandler.addProjector();
});
this.own(this._projectorAttachEvent);
}
}

if (isLastRootNode) {
this._nodeHandler.addRoot(element, properties);
}
else {
else if (hasKey) {
this._nodeHandler.add(element, properties);
}
}
Expand Down Expand Up @@ -466,13 +454,15 @@ export class WidgetBase<P = WidgetProperties, C extends DNode = DNode> extends E
this._numRootNodes = nodes.length;
this._currentRootNode = 0;
const rootNodes: DNode[] = [];
this._rootNodeKeys = [];

nodes.forEach(node => {
if (isHNode(node)) {
rootNodes.push(node);
node.properties = node.properties || {};
node.properties.afterCreate = this._afterRootCreateCallback;
node.properties.afterUpdate = this._afterRootUpdateCallback;
if (node.properties.key) {
this._rootNodeKeys.push(node.properties.key);
}
}
});

Expand All @@ -481,7 +471,7 @@ export class WidgetBase<P = WidgetProperties, C extends DNode = DNode> extends E
if (isHNode(node) || isWNode(node)) {
node.properties = node.properties || {};
if (isHNode(node)) {
if (rootNodes.indexOf(node) === -1 && node.properties.key) {
if (rootNodes.indexOf(node) === -1 || node.properties.key) {
node.properties.afterCreate = this._afterCreateCallback;
node.properties.afterUpdate = this._afterUpdateCallback;
}
Expand Down
11 changes: 7 additions & 4 deletions tests/unit/WidgetBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,19 +572,22 @@ registerSuite({
assert.lengthOf(qux.children, 1);
const bar = qux.children[0];
assert.equal(bar.vnodeSelector, 'bar');
assert.deepEqual(bar.properties, { bind: widget, foo: 'bar' });
assert.deepEqual(bar.properties.bind, widget);
assert.deepEqual(bar.properties.foo, 'bar');
assert.lengthOf(bar.children, 2);
const foo = bar.children[0];
assert.equal(foo.vnodeSelector, 'foo');
assert.deepEqual(foo.properties, { bind: widget });
assert.deepEqual(foo.properties.bind, widget);
assert.lengthOf(foo.children, 1);
const baz1 = foo.children[0];
assert.equal(baz1.vnodeSelector, 'baz');
assert.deepEqual(baz1.properties, { bind: widget, baz: 'qux' });
assert.deepEqual(baz1.properties.bind, widget);
assert.deepEqual(baz1.properties.baz, 'qux');
assert.lengthOf(baz1.children, 0);
const baz2 = bar.children[1];
assert.equal(baz2.vnodeSelector, 'baz');
assert.deepEqual(baz2.properties, { bind: widget, baz: 'qux' });
assert.deepEqual(baz2.properties.bind, widget);
assert.deepEqual(baz2.properties.baz, 'qux');
assert.lengthOf(baz2.children, 0);
},
'class level decorator'() {
Expand Down
16 changes: 8 additions & 8 deletions tests/unit/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ registerSuite({
const Projector = ProjectorMixin(WidgetA);
const projector = new Projector();
const handle = projector.append(root);
assert.strictEqual(projector.lifeCycleCreated.length, 2);
assert.strictEqual(projector.lifeCycleCreated.length, 3);
assert.strictEqual(projector.lifeCycleUpdated.length, 0);
handle.destroy();
},
Expand All @@ -162,18 +162,18 @@ registerSuite({
const projector = new Projector();
const handle = projector.append(root);
assert.strictEqual(projector.lifeCycleCreated[0].key, 'div2');
assert.strictEqual(projector.lifeCycleCreated[1].key, 'div1');
assert.strictEqual(projector.lifeCycleCreated[2].key, 'div1');
handle.destroy();
},

'on create with afterCreate'() {
const Projector = ProjectorMixin(WidgetB);
const projector = new Projector();
const handle = projector.append(root);
assert.strictEqual(projector.lifeCycleCreated.length, 2);
// afterCreateCounter will be 1 because the other two afterCreate callbacks will be replaced in
assert.strictEqual(projector.lifeCycleCreated.length, 3);
// afterCreateCounter will be 0 because all afterCreate callbacks will be replaced in
// WidgetBase.
assert.strictEqual(afterCreateCounter, 1);
assert.strictEqual(afterCreateCounter, 0);
assert.strictEqual(afterUpdateCounter, 0);
assert.strictEqual(projector.lifeCycleUpdated.length, 0);
handle.destroy();
Expand All @@ -195,7 +195,7 @@ registerSuite({
}, 'DOM update did not occur', 10);

assert.strictEqual(projector.lifeCycleCreated.length, 1, 'Unexpected number of created nodes.');
assert.strictEqual(projector.lifeCycleUpdated.length, 2, 'Unexpected number of updated nodes.');
assert.strictEqual(projector.lifeCycleUpdated.length, 3, 'Unexpected number of updated nodes.');
},

async 'on update with afterUpdate'() {
Expand All @@ -215,9 +215,9 @@ registerSuite({
}, 'DOM update did not occur', 10);

assert.strictEqual(projector.lifeCycleCreated.length, 1, 'Unexpected number of created nodes.');
assert.strictEqual(projector.lifeCycleUpdated.length, 2, 'Unexpected number of updated nodes.');
assert.strictEqual(projector.lifeCycleUpdated.length, 3, 'Unexpected number of updated nodes.');
assert.strictEqual(afterCreateCounter, 0); // afterCreate callback is replaced in WidgetBase.
assert.strictEqual(afterUpdateCounter, 1); // afterUpdate callback is replaced in WidgetBase.
assert.strictEqual(afterUpdateCounter, 0); // afterUpdate callback is replaced in WidgetBase.
},

async 'basic widget that always re-renders'() {
Expand Down
82 changes: 32 additions & 50 deletions tests/unit/mixins/Projector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import '@dojo/shim/Promise';
import { VNode } from '@dojo/interfaces/vdom';
import * as registerSuite from 'intern!object';
import * as assert from 'intern/chai!assert';
import { spy } from 'sinon';
import { spy, stub, SinonStub } from 'sinon';
import { v } from '../../../src/d';
import { ProjectorMixin, ProjectorAttachState } from '../../../src/mixins/Projector';
import { beforeRender, WidgetBase } from '../../../src/WidgetBase';
import { waitFor } from '../waitFor';

const Event = global.window.Event;

Expand Down Expand Up @@ -44,26 +43,27 @@ function sendAnimationEndEvents(element: Element) {
dispatchEvent(element, 'animationend');
}

let rafSpy: any;
let cancelRafSpy: any;
let rafStub: SinonStub;
let cancelRafStub: SinonStub;
let projector: BaseTestWidget | MyWidget;

registerSuite({
name: 'mixins/projectorMixin',

beforeEach() {
result = null;
rafSpy = spy(global, 'requestAnimationFrame');
cancelRafSpy = spy(global, 'cancelAnimationFrame');
rafStub = stub(global, 'requestAnimationFrame').returns(1);
rafStub.yields();
cancelRafStub = stub(global, 'cancelAnimationFrame');
},

afterEach() {
if (projector) {
projector.destroy();
projector = <any> undefined;
}
rafSpy.restore();
cancelRafSpy.restore();
rafStub.restore();
cancelRafStub.restore();
},
'attach to projector': {
'append': {
Expand Down Expand Up @@ -636,7 +636,7 @@ registerSuite({

projector.pause();
projector.scheduleRender();
assert.isFalse(rafSpy.called);
assert.isFalse(rafStub.called);
},
'pause cancels animation frame if scheduled'() {
const projector = new BaseTestWidget();
Expand All @@ -645,7 +645,7 @@ registerSuite({

projector.scheduleRender();
projector.pause();
assert.isTrue(cancelRafSpy.called);
assert.isTrue(cancelRafStub.called);
},
'resume'() {
const projector = new BaseTestWidget();
Expand Down Expand Up @@ -786,14 +786,14 @@ registerSuite({

projector.invalidate();

assert.isFalse(rafSpy.called);
assert.isFalse(rafStub.called);
},
'invalidate after attached'() {
const projector: any = new BaseTestWidget();

projector.append();
projector.invalidate();
assert.isTrue(rafSpy.called);
assert.isTrue(rafStub.called);
},
'reattach'() {
const root = document.createElement('div');
Expand Down Expand Up @@ -869,8 +869,8 @@ registerSuite({
}

const projector = new TestProjector();

await projector.append();
projector.async = false;
projector.append();

children.push(v('div', {
id: 'test-element',
Expand All @@ -880,25 +880,19 @@ registerSuite({

projector.callInvalidate();

await waitFor(() => {
return document.getElementById('test-element') !== null;
}, 'Element was never added');

const domNode = document.getElementById('test-element')!;

await waitFor(() => {
return domNode.classList.contains('fade-in') && domNode.classList.contains('fade-in-active');
}, 'fade-in classes never got added to element');
assert.isNotNull(domNode);
assert.isTrue(domNode.classList.contains('fade-in'));
assert.isTrue(domNode.classList.contains('fade-in-active'));

// manually fire the transition end events
sendAnimationEndEvents(domNode);
sendAnimationEndEvents(domNode!);

children = [];
projector.callInvalidate();

await waitFor(() => {
return domNode.classList.contains('fade-out') && domNode.classList.contains('fade-out-active');
}, 'fade-out classes never got added to element');
assert.isTrue(domNode.classList.contains('fade-out'));
assert.isTrue(domNode.classList.contains('fade-out-active'));

domNode.parentElement!.removeChild(domNode);
},
Expand All @@ -918,8 +912,8 @@ registerSuite({
}

const projector = new TestProjector();

await projector.append();
projector.async = false;
projector.append();

children.push(v('div', {
id: 'test-element',
Expand All @@ -931,25 +925,19 @@ registerSuite({

projector.callInvalidate();

await waitFor(() => {
return document.getElementById('test-element') !== null;
}, 'Element was never added');

const domNode = document.getElementById('test-element')!;

await waitFor(() => {
return domNode.classList.contains('fade-in') && domNode.classList.contains('active-fade-in');
}, 'fade-in classes never got added to element');
assert.isNotNull(domNode);
assert.isTrue(domNode.classList.contains('fade-in'));
assert.isTrue(domNode.classList.contains('active-fade-in'));

// manually fire the transition end events
sendAnimationEndEvents(domNode);

children = [];
projector.callInvalidate();

await waitFor(() => {
return domNode.classList.contains('fade-out') && domNode.classList.contains('active-fade-out');
}, 'fade-out classes never got added to element');
assert.isTrue(domNode.classList.contains('fade-out'));
assert.isTrue(domNode.classList.contains('active-fade-out'));

domNode.parentElement!.removeChild(domNode);
},
Expand All @@ -976,27 +964,21 @@ registerSuite({
}

const projector = new TestProjector();

projector.async = false;
await projector.append();

await waitFor(() => {
return document.getElementById('test-element') !== null;
}, 'Element was never added');

const domNode = document.getElementById('test-element')!;
assert.isNotNull(domNode);

children = [];
projector.callInvalidate();

await waitFor(() => {
return domNode.classList.contains('fade-out') && domNode.classList.contains('fade-out-active');
}, 'fade-out classes never got added to element');
assert.isTrue(domNode.classList.contains('fade-out'));
assert.isTrue(domNode.classList.contains('fade-out-active'));

// manually fire the transition end events
sendAnimationEndEvents(domNode);

await waitFor(() => {
return document.getElementById('test-element') === null;
}, 'Element never got removed');
assert.isNull(document.getElementById('test-element'));
}
});