Skip to content

Commit

Permalink
Merge pull request #13141 from emberjs/fix-content-normalization
Browse files Browse the repository at this point in the history
Fix Glimmer 2 content normalization
  • Loading branch information
chancancode committed Mar 20, 2016
2 parents bf067f7 + c655638 commit 5c12157
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 114 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"express": "^4.5.0",
"finalhandler": "^0.4.0",
"github": "^0.2.3",
"glimmer-engine": "tildeio/glimmer#d6ae47a",
"glimmer-engine": "tildeio/glimmer#9dff505",
"glob": "^5.0.13",
"htmlbars": "0.14.16",
"mocha": "^2.4.5",
Expand Down
3 changes: 2 additions & 1 deletion packages/ember-glimmer/lib/helpers/concat.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { helper } from '../helper';
import { normalizeTextValue } from 'glimmer-runtime';

/**
@module ember
Expand All @@ -22,7 +23,7 @@ import { helper } from '../helper';
@since 1.13.0
*/
function concat(args) {
return args.join('');
return args.map(normalizeTextValue).join('');
}

export default helper(concat);
3 changes: 0 additions & 3 deletions packages/ember-glimmer/lib/helpers/if-unless.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ function makeConditionalHelper(toBool) {

if (toBool(condition)) {
return positional.at(1).value();
} else {
// TODO: this should probably be `undefined`: https://github.com/emberjs/ember.js/pull/12920#discussion_r53213383
return '';
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/ember-glimmer/lib/helpers/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import Logger from 'ember-metal/logger';
*/
function log(params) {
Logger.log.apply(null, params);
return ''; // FIXME: Helper will currently return and render 'undefined', remove when fixed
}

export default helper(log);
230 changes: 171 additions & 59 deletions packages/ember-glimmer/tests/integration/content-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RenderingTest, moduleFor } from '../utils/test-case';
import { applyMixins } from '../utils/abstract-test-case';
import { set } from 'ember-metal/property_set';
import { computed } from 'ember-metal/computed';
import EmberObject from 'ember-runtime/system/object';
Expand Down Expand Up @@ -58,59 +59,71 @@ moduleFor('Static content tests', class extends RenderingTest {

});

moduleFor('Dynamic content tests', class extends RenderingTest {
class DynamicContentTest extends RenderingTest {

['@test it can render a dynamic text node']() {
this.render('{{message}}', {
message: 'hello'
});
let text1 = this.assertTextNode(this.firstChild, 'hello');
/* abstract */
renderPath(path, context = {}) {
throw new Error('Not implemented: `renderValues`');
}

this.runTask(() => this.rerender());
assertIsEmpty() {
this.assertText('');
}

let text2 = this.assertTextNode(this.firstChild, 'hello');
['@test it can render a dynamic path']() {
this.renderPath('message', { message: 'hello' });

this.assertSameNode(text1, text2);
this.assertText('hello');

this.runTask(() => set(this.context, 'message', 'goodbye'));
// FIXME: use @mmun's assertStableRerender
this.takeSnapshot();
this.runTask(() => this.rerender());
this.assertInvariants();

let text3 = this.assertTextNode(this.firstChild, 'goodbye');
this.runTask(() => set(this.context, 'message', 'goodbye'));

this.assertSameNode(text1, text3);
this.assertText('goodbye');
this.assertInvariants();

this.runTask(() => set(this.context, 'message', 'hello'));

let text4 = this.assertTextNode(this.firstChild, 'hello');

this.assertSameNode(text1, text4);
this.assertText('hello');
this.assertInvariants();
}

['@test it can render a dynamic text node with deeply nested paths']() {
this.render('{{a.b.c.d.e.f}}', {
['@test it can render a deeply nested dynamic path']() {
this.renderPath('a.b.c.d.e.f', {
a: { b: { c: { d: { e: { f: 'hello' } } } } }
});
let text1 = this.assertTextNode(this.firstChild, 'hello');

this.runTask(() => this.rerender());
this.assertText('hello');

let text2 = this.assertTextNode(this.firstChild, 'hello');

this.assertSameNode(text1, text2);
// FIXME: use @mmun's assertStableRerender
this.takeSnapshot();
this.runTask(() => this.rerender());
this.assertInvariants();

this.runTask(() => set(this.context, 'a.b.c.d.e.f', 'goodbye'));

let text3 = this.assertTextNode(this.firstChild, 'goodbye');
this.assertText('goodbye');
this.assertInvariants();

this.assertSameNode(text1, text3);
this.runTask(() => set(this.context, 'a.b.c.d', { e: { f: 'aloha' } }));

this.runTask(() => set(this.context, 'a.b.c.d.e.f', 'hello'));
this.assertText('aloha');
this.assertInvariants();

let text4 = this.assertTextNode(this.firstChild, 'hello');
this.runTask(() => {
set(this.context, 'a',
{ b: { c: { d: { e: { f: 'hello' } } } } }
);
});

this.assertSameNode(text1, text4);
this.assertText('hello');
this.assertInvariants();
}

['@test it can render a dynamic text node where the value is a computed property']() {
['@test it can render a computed property']() {
let Formatter = EmberObject.extend({
formattedMessage: computed('message', function() {
return this.get('message').toUpperCase();
Expand All @@ -119,61 +132,160 @@ moduleFor('Dynamic content tests', class extends RenderingTest {

let m = Formatter.create({ message: 'hello' });

this.render('{{m.formattedMessage}}', { m });
this.renderPath('m.formattedMessage', { m });

let text1 = this.assertTextNode(this.firstChild, 'HELLO');
this.assertText('HELLO');

// FIXME: use @mmun's assertStableRerender
this.takeSnapshot();
this.runTask(() => this.rerender());
this.assertInvariants();

let text2 = this.assertTextNode(this.firstChild, 'HELLO');
this.runTask(() => set(m, 'message', 'goodbye'));

this.assertSameNode(text1, text2);
this.assertText('GOODBYE');
this.assertInvariants();

this.runTask(() => set(m, 'message', 'goodbye'));
this.runTask(() => set(this.context, 'm', Formatter.create({ message: 'hello' })));

this.assertText('HELLO');
this.assertInvariants();
}

}

const EMPTY = {};

class ContentTestGenerator {
constructor(cases, tag = '@test') {
this.cases = cases;
this.tag = tag;
}

generate([ value, expected, label ]) {
let tag = this.tag;
label = label || value;

if (expected === EMPTY) {
return {

[`${tag} rendering ${label}`]() {
this.renderPath('value', { value });

this.assertIsEmpty();

this.runTask(() => set(this.context, 'value', 'hello'));

this.assertText('hello');

this.runTask(() => set(this.context, 'value', value));

this.assertIsEmpty();
}

let text3 = this.assertTextNode(this.firstChild, 'GOODBYE');
};
} else {
return {

this.assertSameNode(text1, text3);
[`${tag} rendering ${label}`]() {
this.renderPath('value', { value });

this.runTask(() => set(m, 'message', 'hello'));
this.assertText(expected);

let text4 = this.assertTextNode(this.firstChild, 'HELLO');
// FIXME: use @mmun's assertStableRerender
this.takeSnapshot();
this.runTask(() => this.rerender());
this.assertInvariants();

this.assertSameNode(text1, text4);
this.runTask(() => set(this.context, 'value', 'hello'));

this.assertText('hello');
this.assertInvariants();

this.runTask(() => set(this.context, 'value', value));

this.assertText(expected);
this.assertInvariants();
}

};
}
}
}

['@test it can render a dynamic element']() {
this.render('<p>{{message}}</p>', {
message: 'hello'
});
let p1 = this.assertElement(this.firstChild, { tagName: 'p' });
let text1 = this.assertTextNode(this.firstChild.firstChild, 'hello');
const SharedContentTestCases = new ContentTestGenerator([

this.runTask(() => this.rerender());
['foo', 'foo'],
[0, '0'],
[-0, '0', '-0'],
[1, '1'],
[-1, '-1'],
[0.0, '0', '0.0'],
[0.5, '0.5'],
[undefined, EMPTY],
[null, EMPTY],
[true, 'true'],
[false, 'false'],
[NaN, 'NaN'],
[new Date(2000, 0, 1), String(new Date(2000, 0, 1)), 'a Date object'],
[Infinity, 'Infinity'],
[(1 / -0), '-Infinity'],
[{ foo: 'bar' }, '[object Object]', `{ foo: 'bar' }`],
[{ toString() { return 'foo'; } }, 'foo', 'an object with a custom toString function'],
[{ valueOf() { return 1; } }, '[object Object]', 'an object with a custom valueOf function']

let p2 = this.assertElement(this.firstChild, { tagName: 'p' });
let text2 = this.assertTextNode(this.firstChild.firstChild, 'hello');
]);

this.assertSameNode(p1, p2);
this.assertSameNode(text1, text2);
let GlimmerContentTestCases = new ContentTestGenerator([

this.runTask(() => set(this.context, 'message', 'goodbye'));
[Object.create(null), EMPTY, 'an object with no toString']

let p3 = this.assertElement(this.firstChild, { tagName: 'p' });
let text3 = this.assertTextNode(this.firstChild.firstChild, 'goodbye');
], '@glimmer');

this.assertSameNode(p1, p3);
this.assertSameNode(text1, text3);
if (typeof Symbol !== 'undefined') {
GlimmerContentTestCases.cases.push([Symbol('debug'), 'Symbol(debug)', 'a symbol']);
}

this.runTask(() => set(this.context, 'message', 'hello'));
applyMixins(DynamicContentTest, SharedContentTestCases, GlimmerContentTestCases);

moduleFor('Dynamic content tests (content position)', class extends DynamicContentTest {

renderPath(path, context = {}) {
this.render(`{{${path}}}`, context);
}

let p4 = this.assertElement(this.firstChild, { tagName: 'p' });
let text4 = this.assertTextNode(this.firstChild.firstChild, 'hello');
});

moduleFor('Dynamic content tests (content concat)', class extends DynamicContentTest {

this.assertSameNode(p1, p4);
this.assertSameNode(text1, text4);
renderPath(path, context = {}) {
this.render(`{{concat "" ${path} ""}}`, context);
}

});

moduleFor('Dynamic content tests (inside an element)', class extends DynamicContentTest {

renderPath(path, context = {}) {
this.render(`<p>{{${path}}}</p>`, context);
}

});

moduleFor('Dynamic content tests (attribute position)', class extends DynamicContentTest {

renderPath(path, context = {}) {
this.render(`<div data-foo="{{${path}}}"></div>`, context);
}

textValue() {
return this.$('div').attr('data-foo');
}

});

moduleFor('Dynamic content tests (integration)', class extends RenderingTest {

['@test it can render a dynamic template']() {
let template = `
<div class="header">
Expand Down
49 changes: 0 additions & 49 deletions packages/ember-htmlbars/tests/hooks/text_node_test.js

This file was deleted.

0 comments on commit 5c12157

Please sign in to comment.