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

Test and refactor rendering #40

Merged
merged 3 commits into from
Apr 19, 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
68 changes: 50 additions & 18 deletions src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import {
Simple,
templateFactory,
RenderResult,
ComponentDefinition,
Component
} from '@glimmer/runtime';
Expand All @@ -25,6 +24,8 @@ import DynamicScope from './dynamic-scope';
import Environment from './environment';
import mainTemplate from './templates/main';

function NOOP() {}

export interface ApplicationOptions {
rootName: string;
resolver: Resolver;
Expand All @@ -50,17 +51,20 @@ export default class Application implements Owner {
private _rootsIndex: number = 0;
private _registry: Registry;
private _container: Container;
private _renderResult: RenderResult;
/** Whether the initial render has completed. */
private _rendered: boolean;
/** Whether a re-render has been scheduled. */
private _scheduled: boolean;
private _initializers: Initializer[] = [];
private _initialized = false;
private _rendered = false;
private _scheduled = false;
private _rerender: () => void = NOOP;
private _afterRender: () => void = NOOP;
private _renderPromise: Option<Promise<void>>;

constructor(options: ApplicationOptions) {
this.rootName = options.rootName;
this.resolver = options.resolver;
this._renderPromise = new Promise<void>(resolve => {
this._afterRender = resolve;
});
}

/** @hidden */
Expand Down Expand Up @@ -136,30 +140,58 @@ export default class Application implements Owner {

this.env.commit();

let renderResult = result.value;

this._rerender = () => {
this.env.begin();
renderResult.rerender();
this.env.commit();
this._didRender();
};

this._didRender();
}

_didRender(): void {
let { _afterRender } = this;

this._afterRender = NOOP;
this._renderPromise = null;
this._rendered = true;
this._renderResult = result.value;

_afterRender();
}

renderComponent(component: string | ComponentDefinition<Component>, parent: Simple.Node, nextSibling: Option<Simple.Node>): void {
renderComponent(
component: string | ComponentDefinition<Component>,
parent: Simple.Node,
nextSibling: Option<Simple.Node> = null
): Promise<void> {
this._roots.push({ id: this._rootsIndex++, component, parent, nextSibling });
this.scheduleRerender();
return this.scheduleRerender();
}

/** @hidden */
rerender(): void {
this.env.begin();
this._renderResult.rerender();
this.env.commit();
scheduleRerender(): Promise<void> {
let { _renderPromise } = this;

if (_renderPromise === null) {
_renderPromise = this._renderPromise = new Promise<void>(resolve => {
this._afterRender = resolve;
});

this._scheduleRerender();
}

return _renderPromise;
}

/** @hidden */
scheduleRerender(): void {
if (this._scheduled || !this._rendered) { return; }
_scheduleRerender(): void {
if (this._scheduled || !this._rendered) return;

this._scheduled = true;
requestAnimationFrame(() => {
this._scheduled = false;
this.rerender();
this._rerender();
});
}

Expand Down
2 changes: 1 addition & 1 deletion test/action-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test('can curry arguments to actions', function(assert) {
passedEvent = null;

helloWorldComponent.name = "cruel world";
app.rerender();
app.scheduleRerender();

h1 = app.rootElement.querySelector('h1');
h1.onclick(fakeEvent);
Expand Down
6 changes: 3 additions & 3 deletions test/environment-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ test('can render a component with the component helper', function(assert) {

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');

app.rerender();
app.scheduleRerender();

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');
});
Expand Down Expand Up @@ -95,7 +95,7 @@ test('can render a custom helper', function(assert) {

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');

app.rerender();
app.scheduleRerender();

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');
});
Expand All @@ -114,7 +114,7 @@ test('can render a custom helper that takes args', function(assert) {

assert.equal(app.rootElement.innerText, 'Hello Tom Dale!');

app.rerender();
app.scheduleRerender();

assert.equal(app.rootElement.innerText, 'Hello Tom Dale!');
});
Expand Down
122 changes: 122 additions & 0 deletions test/render-component-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import buildApp from './test-helpers/test-app';

const { module, test } = QUnit;

module('renderComponent');

test('renders a component', function(assert) {
assert.expect(1);

let containerElement = document.createElement('div');

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.boot();

return app.renderComponent('hello-world', containerElement).then(() => {
assert.equal(containerElement.innerHTML, '<h1>Hello Glimmer!</h1>');
});
});

Choose a reason for hiding this comment

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

Can you add tests for rendering multiple components, rendering into tags with existing content (with or without nextSibling), etc?


test('renders a component without affecting existing content', function(assert) {
assert.expect(2);

let containerElement = document.createElement('div');
let previousSibling = document.createElement('p');

previousSibling.appendChild(document.createTextNode('foo'));
containerElement.appendChild(previousSibling);
containerElement.appendChild(document.createTextNode('bar'));

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.boot();

assert.equal(containerElement.innerHTML, '<p>foo</p>bar');

return app.renderComponent('hello-world', containerElement).then(() => {
assert.equal(containerElement.innerHTML, '<p>foo</p>bar<h1>Hello Glimmer!</h1>');
});
});

test('renders a component before a given sibling', function(assert) {
assert.expect(2);

let containerElement = document.createElement('div');
let previousSibling = document.createElement('p');
let nextSibling = document.createElement('aside');

containerElement.appendChild(previousSibling);
containerElement.appendChild(nextSibling);

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.boot();

assert.equal(containerElement.innerHTML, '<p></p><aside></aside>');

return app.renderComponent('hello-world', containerElement, nextSibling).then(() => {
assert.equal(containerElement.innerHTML, '<p></p><h1>Hello Glimmer!</h1><aside></aside>');
});
});

test('renders multiple components in different places', function(assert) {
assert.expect(2);

let firstContainerElement = document.createElement('div');
let secondContainerElement = document.createElement('div');

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.template('hello-robbie', `<h1>Hello Robbie!</h1>`)
.boot();

return Promise.all([
app.renderComponent('hello-world', firstContainerElement),
app.renderComponent('hello-robbie', secondContainerElement)
]).then(() => {
assert.equal(firstContainerElement.innerHTML, '<h1>Hello Glimmer!</h1>');
assert.equal(secondContainerElement.innerHTML, '<h1>Hello Robbie!</h1>');
});
});

test('renders multiple components in the same container', function(assert) {
assert.expect(1);

let containerElement = document.createElement('div');

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.template('hello-robbie', `<h1>Hello Robbie!</h1>`)
.boot();

return Promise.all([
app.renderComponent('hello-world', containerElement),
app.renderComponent('hello-robbie', containerElement)
]).then(() => {
assert.equal(containerElement.innerHTML, '<h1>Hello Glimmer!</h1><h1>Hello Robbie!</h1>');
});
});

test('renders multiple components in the same container in particular places', function(assert) {
assert.expect(2);

let containerElement = document.createElement('div');
let nextSibling = document.createElement('aside');

containerElement.appendChild(nextSibling);

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.template('hello-robbie', `<h1>Hello Robbie!</h1>`)
.boot();

assert.equal(containerElement.innerHTML, '<aside></aside>');

return Promise.all([
app.renderComponent('hello-world', containerElement),
app.renderComponent('hello-robbie', containerElement, nextSibling)
]).then(() => {
assert.equal(containerElement.innerHTML, '<h1>Hello Robbie!</h1><aside></aside><h1>Hello Glimmer!</h1>');
});
});
4 changes: 2 additions & 2 deletions test/test-helpers/test-app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Application from '../../src/application';
import Resolver, { BasicModuleRegistry } from '@glimmer/resolver';
import { Factory } from '@glimmer/di';

import { TestComponent, TestComponentManager } from './components';
import { precompile } from './compiler';

interface ComponentFactory {
export interface ComponentFactory {
create(injections: object): TestComponent;
}

Expand Down Expand Up @@ -52,6 +51,7 @@ export class AppBuilder {
constructor(name: string) {
this.rootName = name;
this.modules[`component-manager:/${this.rootName}/component-managers/main`] = TestComponentManager;
this.template('main', '<div />');
}

template(name: string, template: string) {
Expand Down