From aaafab2bc9ee88ed2141b12b40c6930817f7d068 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Fri, 22 Dec 2017 16:48:31 -0500 Subject: [PATCH 1/7] Remove eager jQuery destructuring. --- .../tests/system/bootstrap-test.js | 4 +--- packages/ember/tests/routing/basic_test.js | 16 +++++++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/ember-template-compiler/tests/system/bootstrap-test.js b/packages/ember-template-compiler/tests/system/bootstrap-test.js index cdd3b880fe6..4d85435abe5 100644 --- a/packages/ember-template-compiler/tests/system/bootstrap-test.js +++ b/packages/ember-template-compiler/tests/system/bootstrap-test.js @@ -16,8 +16,6 @@ import { AbstractTestCase } from 'internal-test-helpers'; -const { trim } = jQuery; - let component, fixture; function checkTemplate(templateName, assert) { @@ -84,7 +82,7 @@ moduleFor('ember-templates: bootstrap', class extends AbstractTestCase { assert.ok(template, 'template with name funkyTemplate available'); // This won't even work with Ember templates - assert.equal(trim(template({ name: 'Tobias' })), 'Tobias'); + assert.equal(template({ name: 'Tobias' }).trim(), 'Tobias'); } ['@test duplicated default application templates should throw exception'](assert) { diff --git a/packages/ember/tests/routing/basic_test.js b/packages/ember/tests/routing/basic_test.js index fc49007e080..4d1673838db 100644 --- a/packages/ember/tests/routing/basic_test.js +++ b/packages/ember/tests/routing/basic_test.js @@ -32,8 +32,6 @@ import { compile } from 'ember-template-compiler'; import { Application, Engine } from 'ember-application'; import { Transition } from 'router'; -let trim = jQuery.trim; - let Router, App, router, registry, container, originalLoggerError, originalRenderSupport; function bootApplication() { @@ -3020,13 +3018,13 @@ QUnit.test('Tolerates stacked renders', function() { } }); bootApplication(); - equal(trim(jQuery('#qunit-fixture').text()), 'hi'); + equal(jQuery('#qunit-fixture').text().trim(), 'hi'); run(router, 'send', 'openLayer'); - equal(trim(jQuery('#qunit-fixture').text()), 'hilayer'); + equal(jQuery('#qunit-fixture').text().trim(), 'hilayer'); run(router, 'send', 'openLayer'); - equal(trim(jQuery('#qunit-fixture').text()), 'hilayer'); + equal(jQuery('#qunit-fixture').text().trim(), 'hilayer'); run(router, 'send', 'close'); - equal(trim(jQuery('#qunit-fixture').text()), 'hi'); + equal(jQuery('#qunit-fixture').text().trim(), 'hi'); }); QUnit.test('Renders child into parent with non-default template name', function() { @@ -3081,11 +3079,11 @@ QUnit.test('Allows any route to disconnectOutlet another route\'s templates', fu } }); bootApplication(); - equal(trim(jQuery('#qunit-fixture').text()), 'hi'); + equal(jQuery('#qunit-fixture').text().trim(), 'hi'); run(router, 'send', 'openLayer'); - equal(trim(jQuery('#qunit-fixture').text()), 'hilayer'); + equal(jQuery('#qunit-fixture').text().trim(), 'hilayer'); run(router, 'send', 'close'); - equal(trim(jQuery('#qunit-fixture').text()), 'hi'); + equal(jQuery('#qunit-fixture').text().trim(), 'hi'); }); QUnit.test('Can this.render({into:...}) the render helper', function() { From 99a7c60b7154bcb21404da288ca5db578c751d8a Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Fri, 22 Dec 2017 16:53:33 -0500 Subject: [PATCH 2/7] Implement native DOM event dispatcher. Leverages work done by ember-native-dom-event-dispatcher addon, and switches implementation based on jQuery presence. --- .../lib/system/event_dispatcher.js | 216 ++++++++++++++---- 1 file changed, 168 insertions(+), 48 deletions(-) diff --git a/packages/ember-views/lib/system/event_dispatcher.js b/packages/ember-views/lib/system/event_dispatcher.js index 70d7eebf0fc..165e067ea2a 100644 --- a/packages/ember-views/lib/system/event_dispatcher.js +++ b/packages/ember-views/lib/system/event_dispatcher.js @@ -11,6 +11,7 @@ import jQuery from './jquery'; import ActionManager from './action_manager'; import fallbackViewRegistry from '../compat/fallback-view-registry'; +const HAS_JQUERY = jQuery !== undefined; const ROOT_ELEMENT_CLASS = 'ember-application'; const ROOT_ELEMENT_SELECTOR = `.${ROOT_ELEMENT_CLASS}`; @@ -150,6 +151,8 @@ export default EmberObject.extend({ until: '2.17.0' } ); + + this._eventHandlers = Object.create(null); }, /** @@ -164,26 +167,51 @@ export default EmberObject.extend({ @method setup @param addedEvents {Object} */ - setup(addedEvents, rootElement) { - let event; + setup(addedEvents, _rootElement) { + let event, rootElement; let events = this._finalEvents = assign({}, get(this, 'events'), addedEvents); - if (isNone(rootElement)) { - rootElement = get(this, 'rootElement'); - } else { - set(this, 'rootElement', rootElement); + if (!isNone(_rootElement)) { + set(this, 'rootElement', _rootElement); } - rootElement = jQuery(rootElement); + let rootElementSelector = get(this, 'rootElement'); + if (HAS_JQUERY) { + rootElement = jQuery(rootElementSelector); + assert(`You cannot use the same root element (${rootElement.selector || rootElement[0].tagName}) multiple times in an Ember.Application`, !rootElement.is(ROOT_ELEMENT_SELECTOR)); + assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest(ROOT_ELEMENT_SELECTOR).length); + assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find(ROOT_ELEMENT_SELECTOR).length); + + rootElement.addClass(ROOT_ELEMENT_CLASS); - assert(`You cannot use the same root element (${rootElement.selector || rootElement[0].tagName}) multiple times in an Ember.Application`, !rootElement.is(ROOT_ELEMENT_SELECTOR)); - assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest(ROOT_ELEMENT_SELECTOR).length); - assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find(ROOT_ELEMENT_SELECTOR).length); + if (!rootElement.is(ROOT_ELEMENT_SELECTOR)) { + throw new TypeError(`Unable to add '${ROOT_ELEMENT_CLASS}' class to root element (${rootElement.selector || rootElement[0].tagName}). Make sure you set rootElement to the body or an element in the body.`); + } + } else { + if (typeof rootElementSelector !== 'string') { + rootElement = rootElementSelector; + } else { + rootElement = document.querySelector(rootElementSelector); + } - rootElement.addClass(ROOT_ELEMENT_CLASS); + assert(`You cannot use the same root element (${get(this, 'rootElement') || rootElement.tagName}) multiple times in an Ember.Application`, !rootElement.classList.contains(ROOT_ELEMENT_CLASS)); + assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', (() => { + let target = rootElement.parentNode; + do { + if (target.classList.contains(ROOT_ELEMENT_CLASS)) { + return false; + } - if (!rootElement.is(ROOT_ELEMENT_SELECTOR)) { - throw new TypeError(`Unable to add '${ROOT_ELEMENT_CLASS}' class to root element (${rootElement.selector || rootElement[0].tagName}). Make sure you set rootElement to the body or an element in the body.`); + target = target.parentNode; + } while(target && target.nodeType === 1); + + return true; + })()); + assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.querySelector(ROOT_ELEMENT_SELECTOR)); + + rootElement.classList.add(ROOT_ELEMENT_CLASS); + + assert(`Unable to add '${ROOT_ELEMENT_CLASS}' class to root element (${get(this, 'rootElement') || rootElement.tagName}). Make sure you set rootElement to the body or an element in the body.`, rootElement.classList.contains(ROOT_ELEMENT_CLASS)); } let viewRegistry = this._getViewRegistry(); @@ -217,45 +245,119 @@ export default EmberObject.extend({ return; } - rootElement.on(`${event}.ember`, '.ember-view', function(evt, triggeringManager) { - let view = viewRegistry[this.id]; - let result = true; + if (HAS_JQUERY) { + rootElement.on(`${event}.ember`, '.ember-view', function(evt, triggeringManager) { + let view = viewRegistry[this.id]; + let result = true; - let manager = self.canDispatchToEventManager ? self._findNearestEventManager(view, eventName) : null; + let manager = self.canDispatchToEventManager ? self._findNearestEventManager(view, eventName) : null; - if (manager && manager !== triggeringManager) { - result = self._dispatchEvent(manager, evt, eventName, view); - } else if (view) { - result = self._bubbleEvent(view, evt, eventName); - } + if (manager && manager !== triggeringManager) { + result = self._dispatchEvent(manager, evt, eventName, view); + } else if (view) { + result = self._bubbleEvent(view, evt, eventName); + } - return result; - }); + return result; + }); + + rootElement.on(`${event}.ember`, '[data-ember-action]', evt => { + let attributes = evt.currentTarget.attributes; + let handledActions = []; + + for (let i = 0; i < attributes.length; i++) { + let attr = attributes.item(i); + let attrName = attr.name; + + if (attrName.lastIndexOf('data-ember-action-', 0) !== -1) { + let action = ActionManager.registeredActions[attr.value]; + + // We have to check for action here since in some cases, jQuery will trigger + // an event on `removeChild` (i.e. focusout) after we've already torn down the + // action handlers for the view. + if (action && action.eventName === eventName && handledActions.indexOf(action) === -1) { + action.handler(evt); + // Action handlers can mutate state which in turn creates new attributes on the element. + // This effect could cause the `data-ember-action` attribute to shift down and be invoked twice. + // To avoid this, we keep track of which actions have been handled. + handledActions.push(action); + } + } + } + }); + } else { + let viewHandler = (target, event) => { + let view = viewRegistry[target.id]; + let result = true; + + if (view) { + result = this._bubbleEvent(view, event, eventName); + } + + return result; + }; + + let actionHandler = (target, event) => { + let actionId = target.getAttribute('data-ember-action'); + let actions = ActionManager.registeredActions[actionId]; + + // In Glimmer2 this attribute is set to an empty string and an additional + // attribute it set for each action on a given element. In this case, the + // attributes need to be read so that a proper set of action handlers can + // be coalesced. + if (actionId === '') { + let attributes = target.attributes; + let attributeCount = attributes.length; + + actions = []; + + for (let i = 0; i < attributeCount; i++) { + let attr = attributes.item(i); + let attrName = attr.name; - rootElement.on(`${event}.ember`, '[data-ember-action]', evt => { - let attributes = evt.currentTarget.attributes; - let handledActions = []; - - for (let i = 0; i < attributes.length; i++) { - let attr = attributes.item(i); - let attrName = attr.name; - - if (attrName.lastIndexOf('data-ember-action-', 0) !== -1) { - let action = ActionManager.registeredActions[attr.value]; - - // We have to check for action here since in some cases, jQuery will trigger - // an event on `removeChild` (i.e. focusout) after we've already torn down the - // action handlers for the view. - if (action && action.eventName === eventName && handledActions.indexOf(action) === -1) { - action.handler(evt); - // Action handlers can mutate state which in turn creates new attributes on the element. - // This effect could cause the `data-ember-action` attribute to shift down and be invoked twice. - // To avoid this, we keep track of which actions have been handled. - handledActions.push(action); + if (attrName.indexOf('data-ember-action-') === 0) { + actions = actions.concat(ActionManager.registeredActions[attr.value]); + } } } - } - }); + + // We have to check for actions here since in some cases, jQuery will trigger + // an event on `removeChild` (i.e. focusout) after we've already torn down the + // action handlers for the view. + if (!actions) { + return; + } + + for (let index = 0; index < actions.length; index++) { + let action = actions[index]; + + if (action && action.eventName === eventName) { + return action.handler(event); + } + } + }; + + let handleEvent = this._eventHandlers[event] = (event) => { + let target = event.target; + + do { + if (viewRegistry[target.id]) { + if (viewHandler(target, event) === false) { + event.preventDefault(); + event.stopPropagation(); + break; + } + } else if (target.hasAttribute('data-ember-action')) { + actionHandler(target, event); + break; + } + + target = target.parentNode; + } while(target && target.nodeType === 1); + }; + + rootElement.addEventListener(event, handleEvent); + } }, _getViewRegistry() { @@ -298,8 +400,26 @@ export default EmberObject.extend({ }, destroy() { - let rootElement = get(this, 'rootElement'); - jQuery(rootElement).off('.ember', '**').removeClass(ROOT_ELEMENT_CLASS); + let rootElementSelector = get(this, 'rootElement'); + let rootElement; + if (rootElementSelector.nodeType) { + rootElement = rootElementSelector; + } else { + rootElement = document.querySelector(rootElementSelector); + } + + if (!rootElement) { return; } + + if (HAS_JQUERY) { + jQuery(rootElementSelector).off('.ember', '**'); + } else { + for (let event in this._eventHandlers) { + rootElement.removeEventListener(event, this._eventHandlers[event]); + } + } + + rootElement.classList.remove(ROOT_ELEMENT_CLASS); + return this._super(...arguments); }, From b78514e8ad6aab85e6d1ed66bb7ce0d9c18cfb31 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Fri, 22 Dec 2017 17:07:19 -0500 Subject: [PATCH 3/7] Remove jQuery usage in abstract test cases. --- packages/ember/tests/helpers/link_to_test.js | 16 ++++++++-------- .../lib/test-cases/abstract-application.js | 5 ++--- .../lib/test-cases/abstract-rendering.js | 4 ++-- .../lib/test-cases/abstract.js | 10 ++++++++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/ember/tests/helpers/link_to_test.js b/packages/ember/tests/helpers/link_to_test.js index f33f2cc122c..a29ffa57e6c 100644 --- a/packages/ember/tests/helpers/link_to_test.js +++ b/packages/ember/tests/helpers/link_to_test.js @@ -654,7 +654,7 @@ moduleFor('The {{link-to}} helper - nested routes and link-to arguments', class
    {{#each model as |person|}}
  • - {{#link-to 'item' person}} + {{#link-to 'item' person id=person.id}} {{person.name}} {{/link-to}}
  • @@ -689,7 +689,7 @@ moduleFor('The {{link-to}} helper - nested routes and link-to arguments', class assert.equal(this.$('h3:contains(List)').length, 1, 'The home template was rendered'); assert.equal(normalizeUrl(this.$('#home-link').attr('href')), '/', 'The home link points back at /'); - this.click('li a:contains(Yehuda)'); + this.click('#yehuda'); assert.equal(this.$('h3:contains(Item)').length, 1, 'The item template was rendered'); assert.equal(this.$('p').text(), 'Yehuda Katz', 'The name is correct'); @@ -701,7 +701,7 @@ moduleFor('The {{link-to}} helper - nested routes and link-to arguments', class assert.equal(normalizeUrl(this.$('li a:contains(Tom)').attr('href')), '/item/tom'); assert.equal(normalizeUrl(this.$('li a:contains(Erik)').attr('href')), '/item/erik'); - this.click('li a:contains(Erik)'); + this.click('#erik'); assert.equal(this.$('h3:contains(Item)').length, 1, 'The item template was rendered'); assert.equal(this.$('p').text(), 'Erik Brynroflsson', 'The name is correct'); @@ -1138,7 +1138,7 @@ moduleFor('The {{link-to}} helper - nested routes and link-to arguments', class
      {{#each model as |person|}}
    • - {{link-to person.name 'item' person}} + {{link-to person.name 'item' person id=person.id}}
    • {{/each}}
    @@ -1151,7 +1151,7 @@ moduleFor('The {{link-to}} helper - nested routes and link-to arguments', class this.visit('/'); - this.click('li a:contains(Yehuda)'); + this.click('#yehuda'); assert.equal(this.$('h3:contains(Item)').length, 1, 'The item template was rendered'); assert.equal(this.$('p').text(), 'Yehuda Katz', 'The name is correct'); @@ -1470,7 +1470,7 @@ moduleFor('The {{link-to}} helper - loading states and warnings', class extends assertLinkStatus(staticLink); expectWarning(()=> { - this.click(contextLink); + this.click(contextLink[0]); }, warningMessage); // Set the destinationRoute (context is still null). @@ -1496,14 +1496,14 @@ moduleFor('The {{link-to}} helper - loading states and warnings', class extends assertLinkStatus(contextLink); expectWarning(()=> { - this.click(staticLink); + this.click(staticLink[0]); }, warningMessage); this.runTask(() => controller.set('secondRoute', 'about')); assertLinkStatus(staticLink, '/about'); // Click the now-active link - this.click(staticLink); + this.click(staticLink[0]); } }); diff --git a/packages/internal-test-helpers/lib/test-cases/abstract-application.js b/packages/internal-test-helpers/lib/test-cases/abstract-application.js index bb5969443ac..bc8db07edd8 100644 --- a/packages/internal-test-helpers/lib/test-cases/abstract-application.js +++ b/packages/internal-test-helpers/lib/test-cases/abstract-application.js @@ -1,5 +1,4 @@ import { compile } from 'ember-template-compiler'; -import { jQuery } from 'ember-views'; import { EMBER_GLIMMER_REMOVE_APPLICATION_TEMPLATE_WRAPPER } from 'ember/features'; import AbstractTestCase from './abstract'; import { runDestroy } from '../run'; @@ -10,9 +9,9 @@ export default class AbstractApplicationTestCase extends AbstractTestCase { if (this._element) { return this._element; } else if (EMBER_GLIMMER_REMOVE_APPLICATION_TEMPLATE_WRAPPER) { - return this._element = jQuery('#qunit-fixture')[0]; + return this._element = document.querySelector('#qunit-fixture'); } else { - return this._element = jQuery('#qunit-fixture > div.ember-view')[0]; + return this._element = document.querySelector('#qunit-fixture > div.ember-view'); } } diff --git a/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js b/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js index 8dff854f386..c275fa5ce8e 100644 --- a/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js +++ b/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js @@ -1,6 +1,6 @@ import { assign } from 'ember-utils'; import { compile } from 'ember-template-compiler'; -import { jQuery, EventDispatcher } from 'ember-views'; +import { EventDispatcher } from 'ember-views'; import { helper, Helper, Component, _resetRenderers} from 'ember-glimmer'; import AbstractTestCase from './abstract'; @@ -21,7 +21,7 @@ export default class AbstractRenderingTestCase extends AbstractTestCase { }); this.renderer = this.owner.lookup('renderer:-dom'); - this.element = jQuery('#qunit-fixture')[0]; + this.element = document.querySelector('#qunit-fixture'); this.component = null; owner.register('event_dispatcher:main', EventDispatcher); diff --git a/packages/internal-test-helpers/lib/test-cases/abstract.js b/packages/internal-test-helpers/lib/test-cases/abstract.js index 3b34b63f952..e84e5fb1cc1 100644 --- a/packages/internal-test-helpers/lib/test-cases/abstract.js +++ b/packages/internal-test-helpers/lib/test-cases/abstract.js @@ -88,11 +88,17 @@ export default class AbstractTestCase { } click(selector) { - return this.$(selector).click(); + let element; + if (typeof selector === 'string') { + element = this.element.querySelector(selector); + } else { + element = selector; + } + return element.click(); } textValue() { - return this.$().text(); + return this.element.textContent; } takeSnapshot() { From ff9117a1d647c7bf098a44b881289d546b972fe7 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Fri, 22 Dec 2017 17:46:44 -0500 Subject: [PATCH 4/7] Create shared qunit-fixture setup functionality. --- .../ember-application/tests/system/application_test.js | 3 ++- .../ember-application/tests/system/bootstrap-test.js | 8 +++----- .../internal-test-helpers/lib/test-cases/abstract.js | 10 ++++++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/ember-application/tests/system/application_test.js b/packages/ember-application/tests/system/application_test.js index eebac7d26bd..71920c594f4 100644 --- a/packages/ember-application/tests/system/application_test.js +++ b/packages/ember-application/tests/system/application_test.js @@ -212,7 +212,8 @@ moduleFor('Ember.Application, default resolver with autoboot', class extends Def } [`@test Minimal Application initialized with just an application template`]() { - jQuery('#qunit-fixture').html(''); + this.setupFixture(''); + this.runTask(() => this.createApplication()); this.assertInnerHTML('Hello World'); } diff --git a/packages/ember-application/tests/system/bootstrap-test.js b/packages/ember-application/tests/system/bootstrap-test.js index 3cd58e6418a..8b395a4a8f9 100644 --- a/packages/ember-application/tests/system/bootstrap-test.js +++ b/packages/ember-application/tests/system/bootstrap-test.js @@ -1,19 +1,17 @@ import { assign } from 'ember-utils'; -import { jQuery } from 'ember-views'; import { moduleFor, DefaultResolverApplicationTestCase } from 'internal-test-helpers'; moduleFor('Ember.Application with default resolver and autoboot', class extends DefaultResolverApplicationTestCase { - constructor() { - jQuery('#qunit-fixture').html(` + get fixture() { + return `
    - `); - super(); + `; } get applicationOptions() { diff --git a/packages/internal-test-helpers/lib/test-cases/abstract.js b/packages/internal-test-helpers/lib/test-cases/abstract.js index e84e5fb1cc1..6c0259768b4 100644 --- a/packages/internal-test-helpers/lib/test-cases/abstract.js +++ b/packages/internal-test-helpers/lib/test-cases/abstract.js @@ -31,6 +31,11 @@ export default class AbstractTestCase { this.element = null; this.snapshot = null; this.assert = QUnit.config.current.assert; + + let { fixture } = this; + if (fixture) { + this.setupFixture(fixture); + } } teardown() {} @@ -43,6 +48,11 @@ export default class AbstractTestCase { return run.next(callback); } + setupFixture(innerHTML) { + let fixture = document.getElementById('#qunit-fixture'); + fixture.innerHTML = innerHTML; + } + // The following methods require `this.element` to work get firstChild() { From ef04a33961d48de1065ee6a306d01917b44f9cd9 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Fri, 22 Dec 2017 16:55:47 -0500 Subject: [PATCH 5/7] Remove jQuery usage in ember-application tests. --- .../tests/system/application_test.js | 26 ++--- .../tests/system/bootstrap-test.js | 2 +- .../custom_resolver_test.js | 5 +- .../tests/system/initializers_test.js | 9 +- .../system/instance_initializers_test.js | 9 +- .../tests/system/visit_test.js | 102 ++++++++---------- .../integration/application/engine-test.js | 5 +- 7 files changed, 70 insertions(+), 88 deletions(-) diff --git a/packages/ember-application/tests/system/application_test.js b/packages/ember-application/tests/system/application_test.js index 71920c594f4..8338ef92214 100644 --- a/packages/ember-application/tests/system/application_test.js +++ b/packages/ember-application/tests/system/application_test.js @@ -35,14 +35,13 @@ import { } from 'internal-test-helpers'; moduleFor('Ember.Application, autobooting multiple apps', class extends ApplicationTestCase { - constructor() { - jQuery('#qunit-fixture').html(` + get fixture() { + return `
    HI
    HI
    - `); - super(); + `; } get applicationOptions() { @@ -213,7 +212,6 @@ moduleFor('Ember.Application, default resolver with autoboot', class extends Def [`@test Minimal Application initialized with just an application template`]() { this.setupFixture(''); - this.runTask(() => this.createApplication()); this.assertInnerHTML('Hello World'); } @@ -236,14 +234,14 @@ moduleFor('Ember.Application, autobooting', class extends AutobootApplicationTes super.teardown(); } - [`@test initialized application goes to initial route`](assert) { + [`@test initialized application goes to initial route`]() { this.runTask(() => { this.createApplication(); this.addTemplate('application', '{{outlet}}'); this.addTemplate('index', '

    Hi from index

    '); }); - assert.equal(this.$('h1').text(), 'Hi from index'); + this.assertText('Hi from index'); } [`@test ready hook is called before routing begins`](assert) { @@ -290,10 +288,10 @@ moduleFor('Ember.Application, autobooting', class extends AutobootApplicationTes // need to make some assertions about the created router let router = this.application.__deprecatedInstance__.lookup('router:main'); assert.equal(router instanceof Router, true, 'Router was set from initialize call'); - assert.equal(this.$('h1').text(), 'Hello!'); + this.assertText('Hello!'); } - [`@test Application Controller backs the appplication template`](assert) { + [`@test Application Controller backs the appplication template`]() { this.runTask(() => { this.createApplication(); this.addTemplate('application', '

    {{greeting}}

    '); @@ -301,7 +299,7 @@ moduleFor('Ember.Application, autobooting', class extends AutobootApplicationTes greeting: 'Hello!' })); }); - assert.equal(this.$('h1').text(), 'Hello!'); + this.assertText('Hello!'); } [`@test enable log of libraries with an ENV var`](assert) { @@ -321,8 +319,12 @@ moduleFor('Ember.Application, autobooting', class extends AutobootApplicationTes this.runTask(() => this.createApplication()); assert.equal(messages[1], 'Ember : ' + VERSION); - assert.equal(messages[2], 'jQuery : ' + jQuery().jquery); - assert.equal(messages[3], 'my-lib : ' + '2.0.0a'); + if (jQuery) { + assert.equal(messages[2], 'jQuery : ' + jQuery().jquery); + assert.equal(messages[3], 'my-lib : ' + '2.0.0a'); + } else { + assert.equal(messages[2], 'my-lib : ' + '2.0.0a'); + } libraries.deRegister('my-lib'); } diff --git a/packages/ember-application/tests/system/bootstrap-test.js b/packages/ember-application/tests/system/bootstrap-test.js index 8b395a4a8f9..28a814d3353 100644 --- a/packages/ember-application/tests/system/bootstrap-test.js +++ b/packages/ember-application/tests/system/bootstrap-test.js @@ -23,6 +23,6 @@ moduleFor('Ember.Application with default resolver and autoboot', class extends ['@test templates in script tags are extracted at application creation'](assert) { this.runTask(() => this.createApplication()); - assert.equal(this.$('#app').text(), 'Hello World!'); + assert.equal(document.getElementById('app').textContent, 'Hello World!'); } }); diff --git a/packages/ember-application/tests/system/dependency_injection/custom_resolver_test.js b/packages/ember-application/tests/system/dependency_injection/custom_resolver_test.js index fa5a4733aad..0663de5f831 100644 --- a/packages/ember-application/tests/system/dependency_injection/custom_resolver_test.js +++ b/packages/ember-application/tests/system/dependency_injection/custom_resolver_test.js @@ -26,9 +26,8 @@ moduleFor('Ember.Application with extended default resolver and autoboot', class }); } - [`@test a resolver can be supplied to application`](assert) { + [`@test a resolver can be supplied to application`]() { this.runTask(() => this.createApplication()); - assert.equal(this.$('h1').text(), 'Fallback'); + this.assertText('Fallback'); } - }); diff --git a/packages/ember-application/tests/system/initializers_test.js b/packages/ember-application/tests/system/initializers_test.js index ab0d0e1659b..3acd2d1069c 100644 --- a/packages/ember-application/tests/system/initializers_test.js +++ b/packages/ember-application/tests/system/initializers_test.js @@ -1,15 +1,12 @@ import { assign } from 'ember-utils'; import { moduleFor, AutobootApplicationTestCase } from 'internal-test-helpers'; import { Application } from 'ember-application'; -import { jQuery } from 'ember-views'; moduleFor('Ember.Application initializers', class extends AutobootApplicationTestCase { - constructor() { - jQuery('#qunit-fixture').html(` -
    ONE
    + get fixture() { + return `
    ONE
    TWO
    - `); - super(); + `; } get applicationOptions() { diff --git a/packages/ember-application/tests/system/instance_initializers_test.js b/packages/ember-application/tests/system/instance_initializers_test.js index 71521493e81..434ebd38e44 100644 --- a/packages/ember-application/tests/system/instance_initializers_test.js +++ b/packages/ember-application/tests/system/instance_initializers_test.js @@ -1,15 +1,12 @@ import { assign } from 'ember-utils'; import { moduleFor, AutobootApplicationTestCase } from 'internal-test-helpers'; import { Application, ApplicationInstance } from 'ember-application'; -import { jQuery } from 'ember-views'; moduleFor('Ember.Application instance initializers', class extends AutobootApplicationTestCase { - constructor() { - jQuery('#qunit-fixture').html(` -
    ONE
    + get fixture() { + return `
    ONE
    TWO
    - `); - super(); + `; } get applicationOptions() { diff --git a/packages/ember-application/tests/system/visit_test.js b/packages/ember-application/tests/system/visit_test.js index a6dd203969f..ba99b9f2152 100644 --- a/packages/ember-application/tests/system/visit_test.js +++ b/packages/ember-application/tests/system/visit_test.js @@ -12,7 +12,6 @@ import Engine from '../../system/engine'; import { Route } from 'ember-routing'; import { Component, helper } from 'ember-glimmer'; import { compile } from 'ember-template-compiler'; -import { jQuery } from 'ember-views'; function expectAsyncError() { RSVP.off('error'); @@ -29,6 +28,13 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { return super.createApplication(options, Application.extend()); } + assertEmptyFixture(message) { + this.assert.strictEqual( + document.getElementById('qunit-fixture').children.length, 0, + `there are no elements in the fixture element ${message ? message : ''}` + ); + } + // This tests whether the application is "autobooted" by registering an // instance initializer and asserting it never gets run. Since this is // inherently testing that async behavior *doesn't* happen, we set a @@ -274,10 +280,7 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { [`@test visit() returns a promise that resolves when the view has rendered`](assert) { this.addTemplate('application', `

    Hello world

    `); - assert.strictEqual( - this.$().children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/').then(instance => { assert.ok( @@ -285,7 +288,7 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { 'promise is resolved with an ApplicationInstance' ); assert.equal( - this.$('h1').text(), 'Hello world', + this.element.textContent, 'Hello world', 'the application was rendered once the promise resolves' ); }); @@ -296,20 +299,15 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { this.addTemplate('application', '

    Hello world

    '); - assert.strictEqual( - this.$().children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/', { shouldRender: false }).then(instance => { assert.ok( instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance' ); - assert.strictEqual( - this.$().children().length, 0, - 'there are still no elements in the fixture element after visit' - ); + + this.assertEmptyFixture('after visit'); }); } @@ -318,10 +316,7 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { this.addTemplate('application', '

    Hello world

    '); - assert.strictEqual( - this.$('#qunit-fixture').children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/', { shouldRender: true }).then(instance => { assert.ok( @@ -329,7 +324,7 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { 'promise is resolved with an ApplicationInstance' ); assert.strictEqual( - this.$().children().length, 1, + document.querySelector('#qunit-fixture').children.length, 1, 'there is 1 element in the fixture element after visit' ); }); @@ -352,20 +347,15 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { let BlogMap = function() {}; this.add('route-map:blog', BlogMap); - assert.strictEqual( - this.$('#qunit-fixture').children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/blog', { shouldRender: false }).then(instance => { assert.ok( instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance' ); - assert.strictEqual( - this.$().children().length, 0, - 'there are still no elements in the fixture element after visit' - ); + + this.assertEmptyFixture('after visit'); }); } @@ -398,10 +388,7 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { let BlogMap = function() {}; this.add('route-map:blog', BlogMap); - assert.strictEqual( - this.$('#qunit-fixture').children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/blog', { isInteractive: false }).then(instance => { assert.ok( @@ -409,7 +396,7 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { 'promise is resolved with an ApplicationInstance' ); assert.strictEqual( - this.$().find('p').text(), 'Dis cache money', + this.element.querySelector('p').textContent, 'Dis cache money', 'Engine component is resolved' ); }); @@ -439,14 +426,11 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { let BlogMap = function() {}; this.add('route-map:blog', BlogMap); - assert.strictEqual( - this.$().children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/blog', { shouldRender: true }).then(() => { assert.strictEqual( - this.$().find('p').text(), 'Dis cache money', + this.element.querySelector('p').textContent, 'Dis cache money', 'Engine component is resolved' ); }); @@ -475,14 +459,11 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { let BlogMap = function() {}; this.add('route-map:blog', BlogMap); - assert.strictEqual( - this.$().children().length, 0, - 'there are no elements in the fixture element' - ); + this.assertEmptyFixture(); return this.visit('/blog', { shouldRender: true }).then(() => { assert.strictEqual( - this.$().text(), 'turnt up', + this.element.textContent, 'turnt up', 'Engine component is resolved' ); }); @@ -576,18 +557,21 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { } })); - let $foo = jQuery('
    ').appendTo('#qunit-fixture'); - let $bar = jQuery('
    ').appendTo('#qunit-fixture'); + let fixtureElement = document.querySelector('#qunit-fixture'); + let foo = document.createElement('div'); + let bar = document.createElement('div'); + fixtureElement.appendChild(foo); + fixtureElement.appendChild(bar); let data = encodeURIComponent(JSON.stringify({ name: 'Godfrey' })); let instances = []; return RSVP.all([ this.runTask(() => { - return this.application.visit(`/x-foo?data=${data}`, { rootElement: $foo[0] }); + return this.application.visit(`/x-foo?data=${data}`, { rootElement: foo }); }), this.runTask(() => { - return this.application.visit('/x-bar', { rootElement: $bar[0] }); + return this.application.visit('/x-bar', { rootElement: bar }); }) ]).then(_instances => { instances = _instances; @@ -598,28 +582,28 @@ moduleFor('Ember.Application - visit()', class extends ApplicationTestCase { assert.ok(xBarInitCalled); assert.ok(xBarDidInsertElementCalled); - assert.equal($foo.find('h1').text(), 'X-Foo'); - assert.equal($foo.find('p').text(), 'Hello Godfrey, I have been clicked 0 times (0 times combined)!'); - assert.ok($foo.text().indexOf('X-Bar') === -1); + assert.equal(foo.querySelector('h1').textContent, 'X-Foo'); + assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 0 times (0 times combined)!'); + assert.ok(foo.textContent.indexOf('X-Bar') === -1); - assert.equal($bar.find('h1').text(), 'X-Bar'); - assert.equal($bar.find('button').text(), 'Join 0 others in clicking me!'); - assert.ok($bar.text().indexOf('X-Foo') === -1); + assert.equal(bar.querySelector('h1').textContent, 'X-Bar'); + assert.equal(bar.querySelector('button').textContent, 'Join 0 others in clicking me!'); + assert.ok(bar.textContent.indexOf('X-Foo') === -1); this.runTask(() => { - $foo.find('x-foo').click(); + this.click(foo.querySelector('x-foo')); }); - assert.equal($foo.find('p').text(), 'Hello Godfrey, I have been clicked 1 times (1 times combined)!'); - assert.equal($bar.find('button').text(), 'Join 1 others in clicking me!'); + assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 1 times (1 times combined)!'); + assert.equal(bar.querySelector('button').textContent, 'Join 1 others in clicking me!'); this.runTask(() => { - $bar.find('button').click(); - $bar.find('button').click(); + this.click(bar.querySelector('button')); + this.click(bar.querySelector('button')); }); - assert.equal($foo.find('p').text(), 'Hello Godfrey, I have been clicked 1 times (3 times combined)!'); - assert.equal($bar.find('button').text(), 'Join 3 others in clicking me!'); + assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 1 times (3 times combined)!'); + assert.equal(bar.querySelector('button').textContent, 'Join 3 others in clicking me!'); }).finally(() => { this.runTask(() => { diff --git a/packages/ember-glimmer/tests/integration/application/engine-test.js b/packages/ember-glimmer/tests/integration/application/engine-test.js index aef0ac2f0cb..138ed5403c3 100644 --- a/packages/ember-glimmer/tests/integration/application/engine-test.js +++ b/packages/ember-glimmer/tests/integration/application/engine-test.js @@ -311,7 +311,10 @@ moduleFor('Application test: engine rendering', class extends ApplicationTest { this.setupAppAndRoutableEngine(hooks); return this.visit('/blog', { shouldRender: false }).then(() => { - this.assertText(''); + assert.strictEqual( + document.getElementById('qunit-fixture').children.length, 0, + `there are no elements in the qunit-fixture element` + ); this.assert.deepEqual(hooks, [ 'application - application', From f71bbd07cacd406d3ac12db7d170be791b3a5c50 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Fri, 22 Dec 2017 18:37:18 -0500 Subject: [PATCH 6/7] Run tests without jQuery (package by package). Adds support for running tests per-package without jQuery so that jQuery requirements do not creep into the rest of the framework. --- bin/run-tests.js | 6 ++++++ lib/packages.js | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/bin/run-tests.js b/bin/run-tests.js index 7d46ca226ea..f3b937039d8 100755 --- a/bin/run-tests.js +++ b/bin/run-tests.js @@ -184,9 +184,15 @@ function generateEachPackageTests() { testFunctions.push(function() { return run('package=' + packageName); }); + if (packages[packageName].requiresJQuery === false) { + testFunctions.push(function() { + return run('package=' + packageName + '&jquery=none'); + }); + } testFunctions.push(function() { return run('package=' + packageName + '&enableoptionalfeatures=true'); }); + }); } diff --git a/lib/packages.js b/lib/packages.js index 5d87c84e3ca..825a494f3dd 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -1,14 +1,14 @@ module.exports = function() { var packages = { - 'container': { trees: null, requirements: ['ember-utils'], isTypeScript: true, vendorRequirements: ['@glimmer/di'] }, - 'ember-environment': { trees: null, requirements: [], skipTests: true }, - 'ember-utils': { trees: null, requirements: [] }, - 'ember-console': { trees: null, requirements: [], skipTests: true }, - 'ember-metal': { trees: null, requirements: ['ember-environment', 'ember-utils'], vendorRequirements: ['backburner'] }, - 'ember-debug': { trees: null, requirements: [] }, - 'ember-runtime': { trees: null, vendorRequirements: ['rsvp'], requirements: ['container', 'ember-environment', 'ember-console', 'ember-metal'] }, + 'container': { trees: null, requirements: ['ember-utils'], isTypeScript: true, vendorRequirements: ['@glimmer/di'], requiresJQuery: false }, + 'ember-environment': { trees: null, requirements: [], skipTests: true, requiresJQuery: false }, + 'ember-utils': { trees: null, requirements: [], requiresJQuery: false }, + 'ember-console': { trees: null, requirements: [], skipTests: true, requiresJQuery: false }, + 'ember-metal': { trees: null, requirements: ['ember-environment', 'ember-utils'], vendorRequirements: ['backburner'], requiresJQuery: false }, + 'ember-debug': { trees: null, requirements: [], requiresJQuery: false }, + 'ember-runtime': { trees: null, vendorRequirements: ['rsvp'], requirements: ['container', 'ember-environment', 'ember-console', 'ember-metal'], requiresJQuery: false }, 'ember-views': { trees: null, requirements: ['ember-runtime'], skipTests: true }, - 'ember-extension-support': { trees: null, requirements: ['ember-application'] }, + 'ember-extension-support': { trees: null, requirements: ['ember-application'], requiresJQuery: false }, 'ember-testing': { trees: null, requirements: ['ember-application', 'ember-routing'], testing: true }, 'ember-template-compiler': { trees: null, @@ -27,10 +27,10 @@ module.exports = function() { ] }, 'ember-routing': { trees: null, vendorRequirements: ['router', 'route-recognizer'], - requirements: ['ember-runtime', 'ember-views'] }, - 'ember-application': { trees: null, vendorRequirements: ['dag-map'], requirements: ['ember-routing'] }, + requirements: ['ember-runtime', 'ember-views'], requiresJQuery: false }, + 'ember-application': { trees: null, vendorRequirements: ['dag-map'], requirements: ['ember-routing'], requiresJQuery: false }, 'ember': { trees: null, requirements: ['ember-application'] }, - 'internal-test-helpers': { trees: null }, + 'internal-test-helpers': { trees: null, requiresJQuery: false }, 'ember-glimmer': { trees: null, @@ -43,7 +43,7 @@ module.exports = function() { '@glimmer/wire-format', '@glimmer/node' ], - testingVendorRequirements: [] + testingVendorRequirements: [], } }; From 49e672759bfcd89e92c98a6d61bb6a17dd593b82 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 26 Dec 2017 20:22:03 -0800 Subject: [PATCH 7/7] Fix stray `#` in change to getElementById --- packages/internal-test-helpers/lib/test-cases/abstract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/internal-test-helpers/lib/test-cases/abstract.js b/packages/internal-test-helpers/lib/test-cases/abstract.js index 6c0259768b4..26233173182 100644 --- a/packages/internal-test-helpers/lib/test-cases/abstract.js +++ b/packages/internal-test-helpers/lib/test-cases/abstract.js @@ -49,7 +49,7 @@ export default class AbstractTestCase { } setupFixture(innerHTML) { - let fixture = document.getElementById('#qunit-fixture'); + let fixture = document.getElementById('qunit-fixture'); fixture.innerHTML = innerHTML; }