From b51586f4a908e39f09f2a5a932745c48f31d08b8 Mon Sep 17 00:00:00 2001 From: Godhuda Date: Tue, 5 Apr 2016 12:32:29 -0700 Subject: [PATCH] Attempt to do more matrix testing for escaping Also removed the old escape test file since it's now covered by the new tests. --- .../tests/integration/content-test.js | 209 ++++++++++-------- .../tests/utils/abstract-test-case.js | 19 +- .../integration/escape_integration_test.js | 123 ----------- 3 files changed, 138 insertions(+), 213 deletions(-) delete mode 100644 packages/ember-htmlbars/tests/integration/escape_integration_test.js diff --git a/packages/ember-glimmer/tests/integration/content-test.js b/packages/ember-glimmer/tests/integration/content-test.js index 038ad144087..18c7e5f74e6 100644 --- a/packages/ember-glimmer/tests/integration/content-test.js +++ b/packages/ember-glimmer/tests/integration/content-test.js @@ -59,54 +59,6 @@ moduleFor('Static content tests', class extends RenderingTest { }); -moduleFor('Escaped content tests', class extends RenderingTest { - - ['@test it renders HTML as escaped text in normal curlies']() { - this.render('{{htmlContent}}{{nested.htmlContent}}', { - htmlContent: 'Max', - nested: { htmlContent: 'James' } - }); - this.assert.equal(this.$('b').length, 0); - this.assertText('MaxJames'); - - this.runTask(() => this.rerender()); - this.assert.equal(this.$('b').length, 0); - this.assertText('MaxJames'); - - this.runTask(() => set(this.context, 'htmlContent', 'Max')); - this.assert.equal(this.$('i,b').length, 0); - this.assertText('MaxJames'); - - this.runTask(() => set(this.context, 'htmlContent', 'Max')); - this.assert.equal(this.$('b').length, 0); - this.assertText('MaxJames'); - - this.runTask(() => set(this.context, 'nested', { htmlContent: 'James' })); - this.assert.equal(this.$('b,i').length, 0); - this.assertText('MaxJames'); - } - - ['@test it renders HTML in "trusted" curlies']() { - this.render('{{{htmlContent}}}{{{nested.htmlContent}}}', { - htmlContent: 'Max', - nested: { htmlContent: 'James' } - }); - this.assertHTML('MaxJames'); - - this.runTask(() => this.rerender()); - this.assertHTML('MaxJames'); - - this.runTask(() => set(this.context, 'htmlContent', 'Max')); - this.assertHTML('MaxJames'); - - this.runTask(() => set(this.context, 'htmlContent', 'Max')); - this.assertHTML('MaxJames'); - - this.runTask(() => set(this.context, 'nested', { htmlContent: 'James' })); - this.assertHTML('MaxJames'); - } -}); - class DynamicContentTest extends RenderingTest { /* abstract */ @@ -115,48 +67,49 @@ class DynamicContentTest extends RenderingTest { } assertIsEmpty() { - this.assertText(''); + this.assert.strictEqual(this.firstChild, null); + } + + /* abstract */ + assertContent(content) { + throw new Error('Not implemented: `assertContent`'); } ['@test it can render a dynamic path']() { this.renderPath('message', { message: 'hello' }); - this.assertText('hello'); + this.assertContent('hello'); this.assertStableRerender(); this.runTask(() => set(this.context, 'message', 'goodbye')); - this.assertText('goodbye'); + this.assertContent('goodbye'); this.assertInvariants(); this.runTask(() => set(this.context, 'message', 'hello')); - this.assertText('hello'); + this.assertContent('hello'); this.assertInvariants(); } - ['@test it can render a capitalized path with no deprication']() { - this.renderPath('CaptializedPath', { CaptializedPath: 'noDeprication' }); - + ['@test it can render a capitalized path with no deprecation']() { expectNoDeprecation(); - this.assertText('noDeprication'); + this.renderPath('CaptializedPath', { CaptializedPath: 'no deprecation' }); - this.assertStableRerender(); + this.assertContent('no deprecation'); - this.runTask(() => set(this.context, 'CaptializedPath', 'stillNoDeprication')); + this.assertStableRerender(); - expectNoDeprecation(); + this.runTask(() => set(this.context, 'CaptializedPath', 'still no deprecation')); - this.assertText('stillNoDeprication'); + this.assertContent('still no deprecation'); this.assertInvariants(); - this.runTask(() => set(this.context, 'CaptializedPath', 'noDeprication')); + this.runTask(() => set(this.context, 'CaptializedPath', 'no deprecation')); - expectNoDeprecation(); - - this.assertText('noDeprication'); + this.assertContent('no deprecation'); this.assertInvariants(); } @@ -165,18 +118,18 @@ class DynamicContentTest extends RenderingTest { a: { b: { c: { d: { e: { f: 'hello' } } } } } }); - this.assertText('hello'); + this.assertContent('hello'); this.assertStableRerender(); this.runTask(() => set(this.context, 'a.b.c.d.e.f', 'goodbye')); - this.assertText('goodbye'); + this.assertContent('goodbye'); this.assertInvariants(); this.runTask(() => set(this.context, 'a.b.c.d', { e: { f: 'aloha' } })); - this.assertText('aloha'); + this.assertContent('aloha'); this.assertInvariants(); this.runTask(() => { @@ -185,7 +138,7 @@ class DynamicContentTest extends RenderingTest { ); }); - this.assertText('hello'); + this.assertContent('hello'); this.assertInvariants(); } @@ -200,18 +153,18 @@ class DynamicContentTest extends RenderingTest { this.renderPath('m.formattedMessage', { m }); - this.assertText('HELLO'); + this.assertContent('HELLO'); this.assertStableRerender(); this.runTask(() => set(m, 'message', 'goodbye')); - this.assertText('GOODBYE'); + this.assertContent('GOODBYE'); this.assertInvariants(); this.runTask(() => set(this.context, 'm', Formatter.create({ message: 'hello' }))); - this.assertText('HELLO'); + this.assertContent('HELLO'); this.assertInvariants(); } @@ -221,13 +174,13 @@ class DynamicContentTest extends RenderingTest { this.renderPath('nullObject.message', { nullObject }); - this.assertText('hello'); + this.assertContent('hello'); this.assertStableRerender(); this.runTask(() => set(nullObject, 'message', 'goodbye')); - this.assertText('goodbye'); + this.assertContent('goodbye'); this.assertInvariants(); nullObject = Object.create(null); @@ -235,7 +188,7 @@ class DynamicContentTest extends RenderingTest { this.runTask(() => set(this.context, 'nullObject', nullObject)); - this.assertText('hello'); + this.assertContent('hello'); this.assertInvariants(); } @@ -263,7 +216,7 @@ class ContentTestGenerator { this.runTask(() => set(this.context, 'value', 'hello')); - this.assertText('hello'); + this.assertContent('hello'); this.runTask(() => set(this.context, 'value', value)); @@ -277,18 +230,18 @@ class ContentTestGenerator { [`${tag} rendering ${label}`]() { this.renderPath('value', { value }); - this.assertText(expected); + this.assertContent(expected); this.assertStableRerender(); this.runTask(() => set(this.context, 'value', 'hello')); - this.assertText('hello'); + this.assertContent('hello'); this.assertInvariants(); this.runTask(() => set(this.context, 'value', value)); - this.assertText(expected); + this.assertContent(expected); this.assertInvariants(); } @@ -316,7 +269,10 @@ const SharedContentTestCases = new ContentTestGenerator([ [(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'] + [{ valueOf() { return 1; } }, '[object Object]', 'an object with a custom valueOf function'], + + // Escaping tests + ['MaxJames', 'MaxJames'] ]); @@ -338,12 +294,9 @@ moduleFor('Dynamic content tests (content position)', class extends DynamicConte this.render(`{{${path}}}`, context); } -}); - -moduleFor('@htmlbars Dynamic content tests ("trusted" curlies)', class extends DynamicContentTest { - - renderPath(path, context = {}) { - this.render(`{{{${path}}}}`, context); + assertContent(content) { + this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one text node'); + this.assertTextNode(this.firstChild, content); } }); @@ -354,6 +307,11 @@ moduleFor('Dynamic content tests (content concat)', class extends DynamicContent this.render(`{{concat "" ${path} ""}}`, context); } + assertContent(content) { + this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one text node'); + this.assertTextNode(this.firstChild, content); + } + }); moduleFor('Dynamic content tests (inside an element)', class extends DynamicContentTest { @@ -362,6 +320,18 @@ moduleFor('Dynamic content tests (inside an element)', class extends DynamicCont this.render(`

{{${path}}}

`, context); } + assertIsEmpty() { + this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one

tag'); + this.assertElement(this.firstChild, { tagName: 'p' }); + this.assertText(''); + } + + assertContent(content) { + this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one

tag'); + this.assertElement(this.firstChild, { tagName: 'p' }); + this.assertText(content); + } + }); moduleFor('Dynamic content tests (attribute position)', class extends DynamicContentTest { @@ -370,8 +340,73 @@ moduleFor('Dynamic content tests (attribute position)', class extends DynamicCon this.render(`

`, context); } - textValue() { - return this.$('div').attr('data-foo'); + assertIsEmpty() { + this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one
tag'); + this.assertElement(this.firstChild, { tagName: 'div', attrs: { 'data-foo': '' }, content: '' }); + } + + assertContent(content) { + this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one
tag'); + this.assertElement(this.firstChild, { tagName: 'div', attrs: { 'data-foo': content }, content: '' }); + } + +}); + +class TrustedContentTest extends DynamicContentTest { + + assertIsEmpty() { + this.assert.strictEqual(this.firstChild, null); + } + + assertContent(content) { + this.assertHTML(content); + } + + assertStableRerender() { + this.takeSnapshot(); + this.runTask(() => this.rerender()); + super.assertInvariants(); + } + + assertInvariants() { + // If it's not stable, we will wipe out all the content and replace them, + // so there are no invariants + } + +} + +moduleFor('Dynamic content tests (trusted)', class extends TrustedContentTest { + + renderPath(path, context = {}) { + this.render(`{{{${path}}}}`, context); + } + + ['@test updating trusted curlies']() { + this.render('{{{htmlContent}}}{{{nested.htmlContent}}}', { + htmlContent: 'Max', + nested: { htmlContent: 'James' } + }); + + this.assertContent('MaxJames'); + + this.runTask(() => this.rerender()); + + this.assertStableRerender(); + + this.runTask(() => set(this.context, 'htmlContent', 'Max')); + + this.assertContent('MaxJames'); + + this.runTask(() => set(this.context, 'nested.htmlContent', 'Jammie')); + + this.assertContent('MaxJammie'); + + this.runTask(() => { + set(this.context, 'htmlContent', 'Max'); + set(this.context, 'nested', { htmlContent: 'James' }); + }); + + this.assertContent('MaxJames'); } }); diff --git a/packages/ember-glimmer/tests/utils/abstract-test-case.js b/packages/ember-glimmer/tests/utils/abstract-test-case.js index 6b6af0c8942..27ae18a7076 100644 --- a/packages/ember-glimmer/tests/utils/abstract-test-case.js +++ b/packages/ember-glimmer/tests/utils/abstract-test-case.js @@ -123,6 +123,21 @@ export class TestCase { return node; } + get nodesCount() { + let count = 0; + let node = this.element.firstChild; + + while (node) { + if (!isMarker(node)) { + count++; + } + + node = node.nextSibling; + } + + return count; + } + $(sel) { return sel ? jQuery(sel, this.element) : jQuery(this.element); } @@ -179,9 +194,7 @@ export class TestCase { this.assert.strictEqual(newSnapshot.length, oldSnapshot.length, 'Same number of nodes'); for (let i = 0; i < oldSnapshot.length; i++) { - if (!(newSnapshot[i] instanceof TextNode && oldSnapshot[i] instanceof TextNode)) { - this.assertSameNode(newSnapshot[i], oldSnapshot[i]); - } + this.assertSameNode(newSnapshot[i], oldSnapshot[i]); } } diff --git a/packages/ember-htmlbars/tests/integration/escape_integration_test.js b/packages/ember-htmlbars/tests/integration/escape_integration_test.js deleted file mode 100644 index 1b6c59cb19d..00000000000 --- a/packages/ember-htmlbars/tests/integration/escape_integration_test.js +++ /dev/null @@ -1,123 +0,0 @@ -import run from 'ember-metal/run_loop'; -import EmberView from 'ember-views/views/view'; -import compile from 'ember-template-compiler/system/compile'; - -import { set } from 'ember-metal/property_set'; -import { runAppend, runDestroy } from 'ember-runtime/tests/utils'; - -var view; - -import isEnabled from 'ember-metal/features'; -if (!isEnabled('ember-glimmer')) { - // jscs:disable - -QUnit.module('ember-htmlbars: Escaped Integration', { - teardown() { - runDestroy(view); - - view = null; - } -}); - -QUnit.test('should read from a global-ish simple local path without deprecation', function() { - view = EmberView.create({ - context: { NotGlobal: 'Gwar' }, - template: compile('{{NotGlobal}}') - }); - - expectNoDeprecation(); - runAppend(view); - - equal(view.$().text(), 'Gwar'); -}); - -QUnit.test('should read a number value', function() { - var context = { aNumber: 1 }; - view = EmberView.create({ - context: context, - template: compile('{{aNumber}}') - }); - - runAppend(view); - equal(view.$().text(), '1'); - - run(function() { - set(context, 'aNumber', 2); - }); - - equal(view.$().text(), '2'); -}); - -QUnit.test('should read an escaped number value', function() { - var context = { aNumber: 1 }; - view = EmberView.create({ - context: context, - template: compile('{{{aNumber}}}') - }); - - runAppend(view); - equal(view.$().text(), '1'); - - run(function() { - set(context, 'aNumber', 2); - }); - - equal(view.$().text(), '2'); -}); - -QUnit.test('should read from an Object.create(null)', function() { - // Use ember's polyfill for Object.create - var nullObject = Object.create(null); - nullObject['foo'] = 'bar'; - view = EmberView.create({ - context: { nullObject: nullObject }, - template: compile('{{nullObject.foo}}') - }); - - runAppend(view); - equal(view.$().text(), 'bar'); - - run(function() { - set(nullObject, 'foo', 'baz'); - }); - - equal(view.$().text(), 'baz'); -}); - -QUnit.test('should escape HTML in primitive value contexts when using normal mustaches', function() { - view = EmberView.create({ - context: 'MaxJames', - template: compile('{{this}}') - }); - - runAppend(view); - - equal(view.$('b').length, 0, 'does not create an element'); - equal(view.$().text(), 'MaxJames', 'inserts entities, not elements'); - - run(function() { - set(view, 'context', 'MaxJames'); - }); - - equal(view.$().text(), 'MaxJames', 'updates with entities, not elements'); - equal(view.$('i').length, 0, 'does not create an element when value is updated'); -}); - -QUnit.test('should not escape HTML in primitive value contexts when using triple mustaches', function() { - view = EmberView.create({ - context: 'MaxJames', - template: compile('{{{this}}}') - }); - - runAppend(view); - - equal(view.$('b').length, 2, 'creates an element'); - - run(function() { - set(view, 'context', 'MaxJames'); - }); - - equal(view.$('i').length, 2, 'creates an element when value is updated'); -}); - -}