diff --git a/.travis.yml b/.travis.yml index 786ab8881..303d57b63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ env: - EMBER_TRY_SCENARIO=ember-lts-2.12 - EMBER_TRY_SCENARIO=ember-lts-2.16 - EMBER_TRY_SCENARIO=ember-release + - EMBER_TRY_SCENARIO=ember-release-with-jquery - EMBER_TRY_SCENARIO=ember-beta - EMBER_TRY_SCENARIO=ember-canary - EMBER_TRY_SCENARIO=ember-default diff --git a/config/ember-try.js b/config/ember-try.js index 6a35f5ef9..29c94ef62 100644 --- a/config/ember-try.js +++ b/config/ember-try.js @@ -12,6 +12,8 @@ module.exports = { npm: { devDependencies: { 'ember-source': null, + 'ember-native-dom-event-dispatcher': null, + 'ember-fetch': null, }, }, }, @@ -28,6 +30,8 @@ module.exports = { npm: { devDependencies: { 'ember-source': null, + 'ember-native-dom-event-dispatcher': null, + 'ember-fetch': null, }, }, }, @@ -44,6 +48,8 @@ module.exports = { npm: { devDependencies: { 'ember-source': null, + 'ember-native-dom-event-dispatcher': null, + 'ember-fetch': null, }, }, }, @@ -52,6 +58,8 @@ module.exports = { npm: { devDependencies: { 'ember-source': '~2.12.0', + 'ember-native-dom-event-dispatcher': null, + 'ember-fetch': null, }, }, }, @@ -111,6 +119,15 @@ module.exports = { }, }, }, + { + name: 'ember-release-with-jquery', + npm: { + devDependencies: { + 'ember-native-dom-event-dispatcher': null, + 'ember-fetch': null, + }, + }, + }, { name: 'ember-default', npm: { diff --git a/ember-cli-build.js b/ember-cli-build.js index 0b0790c38..235084d1a 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -4,11 +4,17 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); module.exports = function(defaults) { - let app = new EmberAddon(defaults, { + let options = { eslint: { testGenerator: 'qunit', }, - }); + }; + + if (defaults.project.findAddonByName('ember-native-dom-event-dispatcher')) { + options.vendorFiles = { 'jquery.js': null }; + } + + let app = new EmberAddon(defaults, options); /* This build file specifies the options for the dummy test app of this diff --git a/package-lock.json b/package-lock.json index 558ef091e..4adde16f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1984,6 +1984,134 @@ } } }, + "broccoli-templater": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/broccoli-templater/-/broccoli-templater-1.0.0.tgz", + "integrity": "sha1-fAVKrPWW0YaNGkQpH57HuQfTDs8=", + "dev": true, + "requires": { + "broccoli-filter": "0.1.14", + "broccoli-stew": "1.5.0", + "lodash.template": "3.6.2" + }, + "dependencies": { + "broccoli-filter": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/broccoli-filter/-/broccoli-filter-0.1.14.tgz", + "integrity": "sha1-I8rjiR/567e019sAxtzwNTXa960=", + "dev": true, + "requires": { + "broccoli-kitchen-sink-helpers": "0.2.9", + "broccoli-writer": "0.1.1", + "mkdirp": "0.3.5", + "promise-map-series": "0.2.3", + "quick-temp": "0.1.8", + "rsvp": "3.6.2", + "symlink-or-copy": "1.1.8", + "walk-sync": "0.1.3" + } + }, + "broccoli-kitchen-sink-helpers": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/broccoli-kitchen-sink-helpers/-/broccoli-kitchen-sink-helpers-0.2.9.tgz", + "integrity": "sha1-peCYbtjXb7WYS2jD8EUNOpbjbsw=", + "dev": true, + "requires": { + "glob": "5.0.15", + "mkdirp": "0.5.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "dev": true + }, + "walk-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.1.3.tgz", + "integrity": "sha1-igcmGgC9ps+xviXp8QD61XVG9YM=", + "dev": true + } + } + }, "broccoli-test-helper": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/broccoli-test-helper/-/broccoli-test-helper-1.2.0.tgz", @@ -3792,6 +3920,50 @@ "ember-cli-babel": "6.8.2" } }, + "ember-fetch": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ember-fetch/-/ember-fetch-3.4.0.tgz", + "integrity": "sha512-T8isYKpE4HljM49eTpgSmqgtoFLY85CC4gCZZwMtC4sRpbQd/mewTR/OUtfcUFsb6MKzu5sDugzEXCePXyp7fg==", + "dev": true, + "requires": { + "broccoli-funnel": "1.2.0", + "broccoli-stew": "1.5.0", + "broccoli-templater": "1.0.0", + "ember-cli-babel": "6.8.2", + "node-fetch": "2.0.0-alpha.9", + "whatwg-fetch": "2.0.3" + }, + "dependencies": { + "broccoli-funnel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/broccoli-funnel/-/broccoli-funnel-1.2.0.tgz", + "integrity": "sha1-zdw6/F/xaFqAI0iP/3TOb7WlEpY=", + "dev": true, + "requires": { + "array-equal": "1.0.0", + "blank-object": "1.0.2", + "broccoli-plugin": "1.3.0", + "debug": "2.6.9", + "exists-sync": "0.0.4", + "fast-ordered-set": "1.0.3", + "fs-tree-diff": "0.5.6", + "heimdalljs": "0.2.5", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "path-posix": "1.0.0", + "rimraf": "2.6.2", + "symlink-or-copy": "1.1.8", + "walk-sync": "0.3.2" + } + }, + "node-fetch": { + "version": "2.0.0-alpha.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.0.0-alpha.9.tgz", + "integrity": "sha512-I7wP1QkmBNX1mt4BS5zyLRTegl5Ii+MSalpfFefn+EZFrGVsdfCvLTKt9eHkNlU4phKgp3tqLWW8VXDcCm9m9w==", + "dev": true + } + } + }, "ember-inflector": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ember-inflector/-/ember-inflector-2.0.1.tgz", @@ -3810,6 +3982,15 @@ "ember-cli-babel": "6.8.2" } }, + "ember-native-dom-event-dispatcher": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/ember-native-dom-event-dispatcher/-/ember-native-dom-event-dispatcher-0.6.3.tgz", + "integrity": "sha1-Oy8Q3PgvmqpN0hGnBKxRH9hxRyA=", + "dev": true, + "requires": { + "ember-cli-babel": "6.8.2" + } + }, "ember-resolver": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/ember-resolver/-/ember-resolver-4.5.0.tgz", @@ -6962,6 +7143,18 @@ "lodash.isarray": "3.0.4" } }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, "lodash._bindcallback": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", @@ -7051,6 +7244,12 @@ "lodash.keys": "2.3.0" } }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, "lodash._setbinddata": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/lodash._setbinddata/-/lodash._setbinddata-2.3.0.tgz", @@ -9561,6 +9760,12 @@ "integrity": "sha1-Dhh4HeYpoYMIzhSBZQ9n/6JpOl0=", "dev": true }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=", + "dev": true + }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", diff --git a/package.json b/package.json index cc760131a..5b120492d 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,9 @@ "ember-debug-handlers-polyfill": "^1.0.3", "ember-disable-prototype-extensions": "^1.1.2", "ember-export-application-global": "^2.0.0", + "ember-fetch": "^3.4.0", "ember-load-initializers": "^1.0.0", + "ember-native-dom-event-dispatcher": "^0.6.3", "ember-resolver": "^4.0.0", "ember-source": "~2.15.0", "ember-welcome-page": "^3.0.0", diff --git a/tests/dummy/app/templates/components/.gitkeep b/tests/dummy/app/templates/.gitkeep similarity index 100% rename from tests/dummy/app/templates/components/.gitkeep rename to tests/dummy/app/templates/.gitkeep diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs deleted file mode 100644 index 1eac0a203..000000000 --- a/tests/dummy/app/templates/application.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{!-- The following component displays Ember's default welcome message. --}} -{{welcome-page}} -{{!-- Feel free to remove this! --}} - -{{outlet}} \ No newline at end of file diff --git a/tests/helpers/events.js b/tests/helpers/events.js new file mode 100644 index 000000000..0551d5ac3 --- /dev/null +++ b/tests/helpers/events.js @@ -0,0 +1,172 @@ +import { run } from '@ember/runloop'; +import { merge } from '@ember/polyfills'; + +const DEFAULT_EVENT_OPTIONS = { canBubble: true, cancelable: true }; +const KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup']; +const MOUSE_EVENT_TYPES = [ + 'click', + 'mousedown', + 'mouseup', + 'dblclick', + 'mouseenter', + 'mouseleave', + 'mousemove', + 'mouseout', + 'mouseover', +]; + +export const elMatches = + typeof Element !== 'undefined' && + (Element.prototype.matches || + Element.prototype.matchesSelector || + Element.prototype.mozMatchesSelector || + Element.prototype.msMatchesSelector || + Element.prototype.oMatchesSelector || + Element.prototype.webkitMatchesSelector); + +export function matches(el, selector) { + return elMatches.call(el, selector); +} + +function isFocusable(el) { + let focusableTags = ['INPUT', 'BUTTON', 'LINK', 'SELECT', 'A', 'TEXTAREA']; + let { tagName, type } = el; + + if (type === 'hidden') { + return false; + } + + return focusableTags.indexOf(tagName) > -1 || el.contentEditable === 'true'; +} + +export function focus(el) { + if (!el) { + return; + } + if (isFocusable(el)) { + run(null, function() { + let browserIsNotFocused = document.hasFocus && !document.hasFocus(); + + // Firefox does not trigger the `focusin` event if the window + // does not have focus. If the document doesn't have focus just + // use trigger('focusin') instead. + if (browserIsNotFocused) { + fireEvent(el, 'focusin'); + } + + // makes `document.activeElement` be `el`. If the browser is focused, it also fires a focus event + el.focus(); + + // if the browser is not focused the previous `el.focus()` didn't fire an event, so we simulate it + if (browserIsNotFocused) { + fireEvent(el, 'focus'); + } + }); + } +} + +export function blur(el) { + if (isFocusable(el)) { + run(null, function() { + let browserIsNotFocused = document.hasFocus && !document.hasFocus(); + + fireEvent(el, 'focusout'); + + // makes `document.activeElement` be `body`. + // If the browser is focused, it also fires a blur event + el.blur(); + + // Chrome/Firefox does not trigger the `blur` event if the window + // does not have focus. If the document does not have focus then + // fire `blur` event via native event. + if (browserIsNotFocused) { + fireEvent(el, 'blur'); + } + }); + } +} + +export function fireEvent(element, type, options = {}) { + if (!element) { + return; + } + let event; + if (KEYBOARD_EVENT_TYPES.indexOf(type) > -1) { + event = buildKeyboardEvent(type, options); + } else if (MOUSE_EVENT_TYPES.indexOf(type) > -1) { + let rect = element.getBoundingClientRect(); + let x = rect.left + 1; + let y = rect.top + 1; + let simulatedCoordinates = { + screenX: x + 5, + screenY: y + 95, + clientX: x, + clientY: y, + }; + event = buildMouseEvent(type, merge(simulatedCoordinates, options)); + } else { + event = buildBasicEvent(type, options); + } + element.dispatchEvent(event); +} + +function buildBasicEvent(type, options = {}) { + let event = document.createEvent('Events'); + event.initEvent(type, true, true); + merge(event, options); + return event; +} + +function buildMouseEvent(type, options = {}) { + let event; + try { + event = document.createEvent('MouseEvents'); + let eventOpts = merge({}, DEFAULT_EVENT_OPTIONS); + merge(eventOpts, options); + + event.initMouseEvent( + type, + eventOpts.canBubble, + eventOpts.cancelable, + window, + eventOpts.detail, + eventOpts.screenX, + eventOpts.screenY, + eventOpts.clientX, + eventOpts.clientY, + eventOpts.ctrlKey, + eventOpts.altKey, + eventOpts.shiftKey, + eventOpts.metaKey, + eventOpts.button, + eventOpts.relatedTarget + ); + } catch (e) { + event = buildBasicEvent(type, options); + } + return event; +} + +function buildKeyboardEvent(type, options = {}) { + let event; + try { + event = document.createEvent('KeyEvents'); + let eventOpts = merge({}, DEFAULT_EVENT_OPTIONS); + merge(eventOpts, options); + event.initKeyEvent( + type, + eventOpts.canBubble, + eventOpts.cancelable, + window, + eventOpts.ctrlKey, + eventOpts.altKey, + eventOpts.shiftKey, + eventOpts.metaKey, + eventOpts.keyCode, + eventOpts.charCode + ); + } catch (e) { + event = buildBasicEvent(type, options); + } + return event; +} diff --git a/tests/helpers/has-jquery.js b/tests/helpers/has-jquery.js new file mode 100644 index 000000000..205cdbc7d --- /dev/null +++ b/tests/helpers/has-jquery.js @@ -0,0 +1,3 @@ +export default function() { + return !!self.jQuery; +} diff --git a/tests/helpers/resolver.js b/tests/helpers/resolver.js index 87c5506a0..0a6158ed2 100644 --- a/tests/helpers/resolver.js +++ b/tests/helpers/resolver.js @@ -1,18 +1,21 @@ import Ember from 'ember'; import { run } from '@ember/runloop'; import { dasherize } from '@ember/string'; -import Resolver from '../../resolver'; +import AppResolver from '../../resolver'; import config from '../../config/environment'; import { setResolver } from 'ember-test-helpers'; +import require from 'require'; -const resolver = Resolver.create({ +const Resolver = AppResolver.extend({ registry: {}, resolve(fullName) { - return this.registry[fullName]; + return this.registry[fullName] || this._super(...arguments); }, }); +const resolver = Resolver.create(); + resolver.namespace = { modulePrefix: config.modulePrefix, podModulePrefix: config.podModulePrefix, @@ -31,6 +34,17 @@ export default { }; export function createCustomResolver(registry) { + if (require.has('ember-native-dom-event-dispatcher')) { + // the raw value looked up by ember and these test helpers + registry[ + 'event_dispatcher:main' + ] = require('ember-native-dom-event-dispatcher').default; + // the normalized value looked up + registry[ + 'event-dispatcher:main' + ] = require('ember-native-dom-event-dispatcher').default; + } + var Resolver = Ember.DefaultResolver.extend({ registry: null, diff --git a/tests/unit/test-module-for-acceptance-test.js b/tests/unit/test-module-for-acceptance-test.js index 1c50d3fd4..3982b2bd4 100644 --- a/tests/unit/test-module-for-acceptance-test.js +++ b/tests/unit/test-module-for-acceptance-test.js @@ -1,5 +1,4 @@ import { test } from 'qunit'; -import $ from 'jquery'; import EmberRouter from '@ember/routing/router'; import EmberApplication from '@ember/application'; import { TestModuleForAcceptance } from 'ember-test-helpers'; @@ -40,7 +39,8 @@ moduleForAcceptance('TestModuleForAcceptance | Lifecycle', { afterTeardown(assert) { assert.strictEqual(getContext(), undefined); - assert.strictEqual($('#ember-testing').children().length, 0); + let testingElement = document.getElementById('ember-testing'); + assert.strictEqual(testingElement.children.length, 0); }, }); @@ -63,7 +63,8 @@ test('Basic acceptance test using instance test helpers', function(assert) { this.application.testHelpers.visit('/'); this.application.testHelpers.andThen(function() { - assert.equal($('#ember-testing').text(), 'This is the index page.'); + let testingElement = document.getElementById('ember-testing'); + assert.equal(testingElement.textContent, 'This is the index page.'); }); }); @@ -71,7 +72,8 @@ test('Basic acceptance test using global test helpers', function(assert) { window.visit('/'); window.andThen(function() { - assert.equal($('#ember-testing').text(), 'This is the index page.'); + let testingElement = document.getElementById('ember-testing'); + assert.equal(testingElement.textContent, 'This is the index page.'); }); }); diff --git a/tests/unit/test-module-for-component-test.js b/tests/unit/test-module-for-component-test.js index 84a8b8df6..715cce556 100644 --- a/tests/unit/test-module-for-component-test.js +++ b/tests/unit/test-module-for-component-test.js @@ -8,14 +8,15 @@ import Controller from '@ember/controller'; import Component from '@ember/component'; import EmberObject from '@ember/object'; import EmberService, { inject as service } from '@ember/service'; -import $ from 'jquery'; import Ember from 'ember'; import { TestModuleForComponent } from 'ember-test-helpers'; import hasEmberVersion from 'ember-test-helpers/has-ember-version'; import { setResolverRegistry } from '../helpers/resolver'; import wait from 'ember-test-helpers/wait'; import qunitModuleFor from '../helpers/qunit-module-for'; +import hasjQuery from '../helpers/has-jquery'; import hbs from 'htmlbars-inline-precompile'; +import { fireEvent, focus, blur } from '../helpers/events'; var Service = EmberService || EmberObject; @@ -57,7 +58,7 @@ var ColorController = Controller.extend({ var BoringColor = Component.extend({ willDestroyElement() { var stateIndicatesInDOM = this._state === 'inDOM'; - var actuallyInDOM = $.contains(document, this.$()[0]); + var actuallyInDOM = document.contains(this.element); QUnit.config.current.assert.ok( actuallyInDOM === true && actuallyInDOM === stateIndicatesInDOM, @@ -101,22 +102,24 @@ test('renders', function(assert) { assert.equal(component._state, 'inDOM'); }); -test('append', function(assert) { - assert.expect(4); +if (hasjQuery()) { + test('append', function(assert) { + assert.expect(4); - var $el; - var component; + var $el; + var component; - component = this.subject(); - assert.equal(component._state, 'preRender'); - $el = this.append(); - assert.equal(component._state, 'inDOM'); - assert.ok($el && $el.length, 'append returns $el'); + component = this.subject(); + assert.equal(component._state, 'preRender'); + $el = this.append(); + assert.equal(component._state, 'inDOM'); + assert.ok($el && $el.length, 'append returns $el'); - assert.deprecationsInclude( - 'this.append() is deprecated. Please use this.render() or this.$() instead.' - ); -}); + assert.deprecationsInclude( + 'this.append() is deprecated. Please use this.render() or this.$() instead.' + ); + }); +} test('yields', function(assert) { assert.expect(2); @@ -168,27 +171,21 @@ moduleForComponent('pretty-color', { }); test('className', function(assert) { - // first call to this.$() renders the component. - assert.ok(this.$().is('.pretty-color')); + this.render(); + assert.ok(this._element.matches('.pretty-color')); }); test('template', function(assert) { var component = this.subject(); - assert.equal($.trim(this.$().text()), 'Pretty Color:'); + this.render(); + assert.equal(this._element.textContent, 'Pretty Color: '); run(function() { component.set('name', 'green'); }); - assert.equal($.trim(this.$().text()), 'Pretty Color: green'); -}); - -test('$', function(assert) { - this.subject({ name: 'green' }); - - assert.equal($.trim(this.$('.color-name').text()), 'green'); - assert.equal($.trim(this.$().text()), 'Pretty Color: green'); + assert.equal(this._element.textContent, 'Pretty Color: green'); }); test('it can access the element', function(assert) { @@ -198,6 +195,15 @@ test('it can access the element', function(assert) { assert.equal(this._element.textContent, 'Pretty Color: green'); }); +if (hasjQuery()) { + test('$', function(assert) { + this.subject({ name: 'green' }); + + assert.equal(this.$('.color-name').text(), 'green'); + assert.equal(this.$().text(), 'Pretty Color: green'); + }); +} + moduleForComponent( 'pretty-color', 'component:pretty-color -- this.render in setup', @@ -221,7 +227,8 @@ test('className', function(assert) { // calling `this.$` or `this.subject.$` would // force it to `render` initially, so we access the `ember-testing` // div contents directly - assert.equal($.trim($('#ember-testing').text()), 'Pretty Color: red'); + let testingElement = document.getElementById('ember-testing'); + assert.equal(testingElement.textContent, 'Pretty Color: red'); }); test('`toString` returns the test subject', function(assert) { @@ -271,7 +278,7 @@ test('can handle click', function(assert) { this.render(); run(function() { - component.$().click(); + component.element.click(); }); }); @@ -404,7 +411,8 @@ moduleForComponent('Component Integration Tests', { test('it can render a template', function(assert) { this.render(hbs`Hello`); - assert.equal(this.$('span').text(), 'Hello'); + let actual = this._element.querySelector('span').textContent; + assert.equal(actual, 'Hello'); }); test('it can access the element', function(assert) { @@ -436,9 +444,10 @@ test('it complains if you try to use subject()', function(assert) { test('it can access the full container', function(assert) { this.set('myColor', 'red'); this.render(hbs`{{my-component name=myColor}}`); - assert.equal(this.$('span').text(), 'red'); + + assert.equal(this._element.querySelector('span').textContent, 'red'); this.set('myColor', 'blue'); - assert.equal(this.$('span').text(), 'blue'); + assert.equal(this._element.querySelector('span').textContent, 'blue'); }); test('it can handle actions', function(assert) { @@ -447,13 +456,13 @@ test('it can handle actions', function(assert) { this.on('didFoo', function(thing) { handlerArg = thing; }); - this.$('button').click(); + this._element.querySelector('button').click(); assert.equal(handlerArg, 42); }); test('it accepts precompiled templates', function(assert) { this.render(hbs`Hello`); - assert.equal(this.$('span').text(), 'Hello'); + assert.equal(this._element.querySelector('span').textContent, 'Hello'); }); test('it supports DOM events', function(assert) { @@ -467,8 +476,8 @@ test('it supports DOM events', function(assert) { }), }); this.render(hbs`{{my-component}}`); - this.$('.target').click(); - assert.equal(this.$('.value').text(), '1'); + this._element.querySelector('.target').click(); + assert.equal(this._element.querySelector('.value').textContent, '1'); }); test('it supports updating an input', function(assert) { @@ -478,9 +487,10 @@ test('it supports updating an input', function(assert) { }), }); this.render(hbs`{{my-input value=value}}`); - this.$('input') - .val('1') - .change(); + let input = this._element.querySelector('input'); + input.value = '1'; + + fireEvent(input, 'change'); assert.equal(this.get('value'), '1'); }); @@ -499,13 +509,15 @@ test('it supports dom triggered focus events', function(assert) { }), }); this.render(hbs`{{my-input}}`); - assert.equal(this.$('input').val(), 'init'); - this.$('input').trigger('focusin'); - assert.equal(this.$('input').val(), 'focusin'); + let input = this._element.querySelector('input'); + assert.equal(input.value, 'init'); + + focus(input); + assert.equal(input.value, 'focusin'); - this.$('input').trigger('focusout'); - assert.equal(this.$('input').val(), 'focusout'); + blur(input); + assert.equal(input.value, 'focusout'); }); moduleForComponent('Component Integration Tests: render during setup', { @@ -527,8 +539,8 @@ moduleForComponent('Component Integration Tests: render during setup', { }); test('it has working events', function(assert) { - this.$('.target').click(); - assert.equal(this.$('.value').text(), '1'); + this._element.querySelector('.target').click(); + assert.equal(this._element.querySelector('.value').textContent, '1'); }); moduleForComponent('Component Integration Tests: context', { @@ -553,7 +565,7 @@ test('it can set and get properties', function(assert) { this.render(hbs`{{my-component foo=foo}}`); assert.equal(this.get('foo'), '1'); - assert.equal(this.$('.foo').text(), '1'); + assert.equal(this._element.querySelector('.foo').textContent, '1'); }); test('it can setProperties and getProperties', function(assert) { @@ -573,8 +585,9 @@ test('it can setProperties and getProperties', function(assert) { var properties = this.getProperties('foo', 'bar'); assert.equal(properties.foo, '1'); assert.equal(properties.bar, '2'); - assert.equal(this.$('.foo').text(), '1'); - assert.equal(this.$('.bar').text(), '2'); + let element = this._element; + assert.equal(element.querySelector('.foo').textContent, '1'); + assert.equal(element.querySelector('.bar').textContent, '2'); }); test('two way bound arguments are updated', function(assert) { @@ -659,7 +672,11 @@ test('can register a component', function(assert) { }) ); this.render(hbs`{{x-foo}}`); - assert.equal(this.$('.i-am-x-foo').length, 1, 'found i-am-x-foo'); + assert.equal( + this._element.querySelectorAll('.i-am-x-foo').length, + 1, + 'found i-am-x-foo' + ); }); test('can register a service', function(assert) { @@ -677,12 +694,7 @@ test('can register a service', function(assert) { }) ); this.render(hbs`{{x-foo}}`); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'extreme' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'extreme'); }); test('can inject a service directly into test context', function(assert) { @@ -704,20 +716,9 @@ test('can inject a service directly into test context', function(assert) { this.inject.service('unicorn'); this.render(hbs`{{x-foo}}`); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'extreme' - ); - + assert.equal(this._element.querySelector('.x-foo').textContent, 'extreme'); this.set('unicorn.sparkliness', 'amazing'); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'amazing' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'amazing'); }); test('can inject a service directly into test context, with aliased name', function( @@ -738,19 +739,10 @@ test('can inject a service directly into test context, with aliased name', funct ); this.inject.service('unicorn', { as: 'hornedBeast' }); this.render(hbs`{{x-foo}}`); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'extreme' - ); + + assert.equal(this._element.querySelector('.x-foo').textContent, 'extreme'); this.set('hornedBeast.sparkliness', 'amazing'); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'amazing' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'amazing'); }); moduleForComponent('Component Integration Tests: willDestoryElement', { @@ -759,12 +751,16 @@ moduleForComponent('Component Integration Tests: willDestoryElement', { setResolverRegistry({ 'component:my-component': Component.extend({ willDestroyElement() { - var stateIndicatesInDOM = this._state === 'inDOM'; - var actuallyInDOM = $.contains(document, this.$()[0]); + let { assert } = QUnit.config.current; - QUnit.config.current.assert.ok( - actuallyInDOM === true && actuallyInDOM === stateIndicatesInDOM, - 'component should still be in the DOM' + assert.equal( + this._state, + 'inDOM', + 'still in dom during willDestroyElement' + ); + assert.ok( + document.contains(this.element), + 'component element still contained within `document`' ); }, }), @@ -773,12 +769,12 @@ moduleForComponent('Component Integration Tests: willDestoryElement', { }); test('still in DOM in willDestroyElement', function(assert) { - assert.expect(1); + assert.expect(2); this.render(hbs`{{my-component}}`); }); test('is destroyed when rendered twice', function(assert) { - assert.expect(2); + assert.expect(4); this.render(hbs`{{my-component}}`); this.render(hbs`{{my-component}}`); }); @@ -834,7 +830,7 @@ test('it can set and get properties', function(assert) { }); this.render(hbs`{{my-component}}`); - let testElement = this.$()[0]; + let testElement = this._element; let instanceElement = instance.element; assert.ok( @@ -894,6 +890,6 @@ test('does not require manual run wrapping', function(assert) { return wait(); }) .then(() => { - assert.equal(this.$().text(), 'async value'); + assert.equal(this._element.textContent, 'async value'); }); }); diff --git a/tests/unit/test-module-for-integration-test.js b/tests/unit/test-module-for-integration-test.js index 26ded32d0..3594af46f 100644 --- a/tests/unit/test-module-for-integration-test.js +++ b/tests/unit/test-module-for-integration-test.js @@ -1,5 +1,4 @@ import QUnit, { test } from 'qunit'; -import $ from 'jquery'; import TextField from '@ember/component/text-field'; import { on } from '@ember/object/evented'; import Component from '@ember/component'; @@ -10,6 +9,7 @@ import { TestModuleForIntegration } from 'ember-test-helpers'; import { setResolverRegistry, createCustomResolver } from '../helpers/resolver'; import qunitModuleFor from '../helpers/qunit-module-for'; import hbs from 'htmlbars-inline-precompile'; +import { fireEvent, focus, blur } from '../helpers/events'; const Service = EmberService || EmberObject; @@ -28,7 +28,8 @@ moduleForIntegration('Component Integration Tests', { test('it can render a template', function(assert) { this.render(hbs`Hello`); - assert.equal(this.$('span').text(), 'Hello'); + let actual = this._element.querySelector('span').textContent; + assert.equal(actual, 'Hello'); }); if (hasEmberVersion(1, 11)) { @@ -48,9 +49,10 @@ test('it complains if you try to use bare render', function(assert) { test('it can access the full container', function(assert) { this.set('myColor', 'red'); this.render(hbs`{{my-component name=myColor}}`); - assert.equal(this.$('span').text(), 'red'); + + assert.equal(this._element.querySelector('span').textContent, 'red'); this.set('myColor', 'blue'); - assert.equal(this.$('span').text(), 'blue'); + assert.equal(this._element.querySelector('span').textContent, 'blue'); }); test('it can handle actions', function(assert) { @@ -59,13 +61,13 @@ test('it can handle actions', function(assert) { this.on('didFoo', function(thing) { handlerArg = thing; }); - this.$('button').click(); + this._element.querySelector('button').click(); assert.equal(handlerArg, 42); }); test('it accepts precompiled templates', function(assert) { this.render(hbs`Hello`); - assert.equal(this.$('span').text(), 'Hello'); + assert.equal(this._element.querySelector('span').textContent, 'Hello'); }); test('it supports DOM events', function(assert) { @@ -79,8 +81,8 @@ test('it supports DOM events', function(assert) { }), }); this.render(hbs`{{my-component}}`); - this.$('.target').click(); - assert.equal(this.$('.value').text(), '1'); + this._element.querySelector('.target').click(); + assert.equal(this._element.querySelector('.value').textContent, '1'); }); test('it supports updating an input', function(assert) { @@ -90,9 +92,10 @@ test('it supports updating an input', function(assert) { }), }); this.render(hbs`{{my-input value=value}}`); - this.$('input') - .val('1') - .change(); + + let input = this._element.querySelector('input'); + input.value = '1'; + fireEvent(input, 'change'); assert.equal(this.get('value'), '1'); }); @@ -111,13 +114,14 @@ test('it supports dom triggered focus events', function(assert) { }), }); this.render(hbs`{{my-input}}`); - assert.equal(this.$('input').val(), 'init'); + let input = this._element.querySelector('input'); + assert.equal(input.value, 'init'); - this.$('input').trigger('focusin'); - assert.equal(this.$('input').val(), 'focusin'); + focus(input); + assert.equal(input.value, 'focusin'); - this.$('input').trigger('focusout'); - assert.equal(this.$('input').val(), 'focusout'); + blur(input); + assert.equal(input.value, 'focusout'); }); test('`toString` returns the test name', function(assert) { @@ -146,8 +150,8 @@ moduleForIntegration('TestModuleForIntegration | render during setup', { }); test('it has working events', function(assert) { - this.$('.target').click(); - assert.equal(this.$('.value').text(), '1'); + this._element.querySelector('.target').click(); + assert.equal(this._element.querySelector('.value').textContent, '1'); }); moduleForIntegration('TestModuleForIntegration | context', { @@ -171,7 +175,7 @@ test('it can set and get properties', function(assert) { this.render(hbs`{{my-component foo=foo}}`); assert.equal(this.get('foo'), '1'); - assert.equal(this.$('.foo').text(), '1'); + assert.equal(this._element.querySelector('.foo').textContent, '1'); }); test('it can setProperties and getProperties', function(assert) { @@ -191,8 +195,9 @@ test('it can setProperties and getProperties', function(assert) { var properties = this.getProperties('foo', 'bar'); assert.equal(properties.foo, '1'); assert.equal(properties.bar, '2'); - assert.equal(this.$('.foo').text(), '1'); - assert.equal(this.$('.bar').text(), '2'); + let element = this._element; + assert.equal(element.querySelector('.foo').textContent, '1'); + assert.equal(element.querySelector('.bar').textContent, '2'); }); moduleForIntegration('TestModuleForIntegration | register and inject', {}); @@ -205,7 +210,11 @@ test('can register a component', function(assert) { }) ); this.render(hbs`{{x-foo}}`); - assert.equal(this.$('.i-am-x-foo').length, 1, 'found i-am-x-foo'); + assert.equal( + this._element.querySelectorAll('.i-am-x-foo').length, + 1, + 'found i-am-x-foo' + ); }); test('can register a service', function(assert) { @@ -223,12 +232,7 @@ test('can register a service', function(assert) { }) ); this.render(hbs`{{x-foo}}`); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'extreme' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'extreme'); }); test('can inject a service directly into test context', function(assert) { @@ -247,19 +251,9 @@ test('can inject a service directly into test context', function(assert) { ); this.inject.service('unicorn'); this.render(hbs`{{x-foo}}`); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'extreme' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'extreme'); this.set('unicorn.sparkliness', 'amazing'); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'amazing' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'amazing'); }); test('can inject a service directly into test context, with aliased name', function( @@ -280,19 +274,10 @@ test('can inject a service directly into test context, with aliased name', funct ); this.inject.service('unicorn', { as: 'hornedBeast' }); this.render(hbs`{{x-foo}}`); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'extreme' - ); + + assert.equal(this._element.querySelector('.x-foo').textContent, 'extreme'); this.set('hornedBeast.sparkliness', 'amazing'); - assert.equal( - this.$('.x-foo') - .text() - .trim(), - 'amazing' - ); + assert.equal(this._element.querySelector('.x-foo').textContent, 'amazing'); }); moduleForIntegration('TestModuleForIntegration | willDestoryElement', { @@ -300,12 +285,16 @@ moduleForIntegration('TestModuleForIntegration | willDestoryElement', { setResolverRegistry({ 'component:my-component': Component.extend({ willDestroyElement() { - var stateIndicatesInDOM = this._state === 'inDOM'; - var actuallyInDOM = $.contains(document, this.$()[0]); + let { assert } = QUnit.config.current; - QUnit.config.current.assert.ok( - actuallyInDOM === true && stateIndicatesInDOM === true, - 'component should still be in the DOM' + assert.equal( + this._state, + 'inDOM', + 'still in dom during willDestroyElement' + ); + assert.ok( + document.contains(this.element), + 'component element still contained within `document`' ); }, }), @@ -314,7 +303,7 @@ moduleForIntegration('TestModuleForIntegration | willDestoryElement', { }); test('still in DOM in willDestroyElement', function(assert) { - assert.expect(1); + assert.expect(2); this.render(hbs`{{my-component}}`); }); @@ -360,5 +349,5 @@ moduleForIntegration('TestModuleForIntegration | custom resolver', { test('can render with a custom resolver', function(assert) { this.render(hbs`{{y-foo}}`); - assert.equal(this.$('.name').text(), 'Y u no foo?!', 'rendered properly'); + assert.equal(this._element.textContent, 'Y u no foo?!', 'rendered properly'); }); diff --git a/tests/unit/test-module-for-model-test.js b/tests/unit/test-module-for-model-test.js index 87261b3fc..33057e5b0 100644 --- a/tests/unit/test-module-for-model-test.js +++ b/tests/unit/test-module-for-model-test.js @@ -6,6 +6,7 @@ import { TestModuleForModel } from 'ember-test-helpers'; import { setResolverRegistry } from '../helpers/resolver'; import DS from 'ember-data'; import qunitModuleFor from '../helpers/qunit-module-for'; +import require from 'require'; function moduleForModel(name, description, callbacks) { var module = new TestModuleForModel(name, description, callbacks); @@ -14,19 +15,26 @@ function moduleForModel(name, description, callbacks) { var server; -var adapter = DS.JSONAPIAdapter || DS.FixtureAdapter; +var Adapter = DS.JSONAPIAdapter || DS.FixtureAdapter; +var ApplicationAdapter = (() => { + if (require.has('ember-fetch/mixins/adapter-fetch')) { + const AdapterFetch = require('ember-fetch/mixins/adapter-fetch').default; + return Adapter.extend(AdapterFetch); + } else { + return Adapter.extend(); + } +})(); var Whazzit = DS.Model.extend({ gear: DS.attr('string') }); + var whazzitAdapterFindAllCalled = false; -var WhazzitAdapter = adapter.extend({ +var WhazzitAdapter = ApplicationAdapter.extend({ findAll() { whazzitAdapterFindAllCalled = true; return this._super.apply(this, arguments); }, }); -var ApplicationAdapter = adapter.extend(); - function setupRegistry() { setResolverRegistry({ 'model:whazzit': Whazzit, @@ -80,7 +88,7 @@ test('JSONAPIAdapter (ED >= 2) or FixtureAdapter (ED < 2) is registered for mode var model = this.subject(), store = this.store(); - assert.ok(store.adapterFor(model.constructor.modelName) instanceof adapter); + assert.ok(store.adapterFor(model.constructor.modelName) instanceof Adapter); assert.ok( !(store.adapterFor(model.constructor.modelName) instanceof WhazzitAdapter) ); @@ -126,7 +134,7 @@ moduleForModel('whazzit', 'model:whazzit with custom adapter', { setup() { Whazzit.FIXTURES = []; - if (DS.JSONAPIAdapter && adapter === DS.JSONAPIAdapter) { + if (DS.JSONAPIAdapter && Adapter === DS.JSONAPIAdapter) { server = new Pretender(function() { this.get('/whazzits', function() { return [ @@ -142,7 +150,7 @@ moduleForModel('whazzit', 'model:whazzit with custom adapter', { }, teardown() { - if (DS.JSONAPIAdapter && adapter === DS.JSONAPIAdapter) { + if (DS.JSONAPIAdapter && Adapter === DS.JSONAPIAdapter) { server.shutdown(); } }, diff --git a/tests/unit/wait-test.js b/tests/unit/wait-test.js index e0c03245d..c5427a241 100644 --- a/tests/unit/wait-test.js +++ b/tests/unit/wait-test.js @@ -1,14 +1,24 @@ import Ember from 'ember'; +import $ from 'jquery'; // FYI - not present in all scenarios import { later, run } from '@ember/runloop'; import Component from '@ember/component'; -import $ from 'jquery'; - import { TestModuleForComponent } from 'ember-test-helpers'; import wait from 'ember-test-helpers/wait'; - import { module, test } from 'qunit'; import hbs from 'htmlbars-inline-precompile'; import Pretender from 'pretender'; +import { fireEvent } from '../helpers/events'; +import hasjQuery from '../helpers/has-jquery'; +import require from 'require'; + +function ajax(url) { + if (hasjQuery()) { + return $.ajax(url, { cache: false }); + } else { + let fetch = require('fetch').default; + return fetch(url).then(response => response.text()); + } +} module('wait helper tests', function(hooks) { hooks.beforeEach(function(assert) { @@ -64,7 +74,7 @@ module('wait helper tests', function(hooks) { click() { var component = this; - $.ajax('/whazzits', { cache: false }).then(function(data) { + ajax('/whazzits').then(function(data) { var value = component.get('internalValue'); run(component, 'set', 'internalValue', value + data); @@ -87,13 +97,13 @@ module('wait helper tests', function(hooks) { run(component, 'set', 'internalValue', 'Local Data!'); }, 10); - $.ajax('/whazzits', { cache: false }).then(function(data) { + ajax('/whazzits').then(function(data) { var value = component.get('internalValue'); run(component, 'set', 'internalValue', value + data); later(function() { - $.ajax('/whazzits', { cache: false }).then(function(data) { + ajax('/whazzits').then(function(data) { if (component.isDestroyed) { return; } @@ -174,36 +184,32 @@ module('wait helper tests', function(hooks) { }); test('it works when async exists in `init`', function(assert) { - var testContext = this; - this.render(hbs`{{x-test-1}}`); - return wait().then(function() { - assert.equal(testContext.$().text(), 'async value'); + return wait().then(() => { + assert.equal(this._element.textContent, 'async value'); }); }); test('it works when async exists in an event/action', function(assert) { - var testContext = this; - this.render(hbs`{{x-test-2}}`); - assert.equal(this.$().text(), 'initial value'); + assert.equal(this._element.textContent, 'initial value'); - this.$('div').click(); + fireEvent(this._element.querySelector('div'), 'click'); - return wait().then(function() { - assert.equal(testContext.$().text(), 'async value'); + return wait().then(() => { + assert.equal(this._element.textContent, 'async value'); }); }); test('it waits for AJAX requests to finish', function(assert) { this.render(hbs`{{x-test-3}}`); - this.$('div').click(); + fireEvent(this._element.querySelector('div'), 'click'); return wait().then(() => { - assert.equal(this.$().text(), 'Remote Data!'); + assert.equal(this._element.textContent, 'Remote Data!'); }); }); @@ -214,11 +220,11 @@ module('wait helper tests', function(hooks) { this.render(hbs`{{x-test-4}}`); - this.$('div').click(); + fireEvent(this._element.querySelector('div'), 'click'); return wait().then(function() { assert.equal( - testContext.$().text(), + testContext._element.textContent, 'Local Data!Remote Data!Remote Data!' ); }); @@ -229,30 +235,37 @@ module('wait helper tests', function(hooks) { this.render(hbs`{{x-test-4}}`); - this.$('div').click(); + fireEvent(this._element.querySelector('div'), 'click'); return wait({ waitForTimers: false }).then(function() { - assert.equal(testContext.$().text(), 'Local Data!Remote Data!'); + assert.equal(testContext._element.textContent, 'Local Data!Remote Data!'); }); }); - test('it can wait only for timers', function(assert) { - var testContext = this; + if (hasjQuery()) { + // in the wait utility we specific listen for artificial jQuery events + // to start/stop waiting, but when using ember-fetch those events are not + // emitted and instead test waiters are used + // + // therefore, this test is only valid when using jQuery.ajax + test('it can wait only for timers', function(assert) { + var testContext = this; - this.render(hbs`{{x-test-4}}`); + this.render(hbs`{{x-test-4}}`); - this.$('div').click(); + fireEvent(this._element.querySelector('div'), 'click'); - return wait({ waitForAJAX: false }).then(function() { - assert.equal(testContext.$().text(), 'Local Data!'); + return wait({ waitForAJAX: false }).then(function() { + assert.equal(testContext._element.textContent, 'Local Data!'); + }); }); - }); + } test('it waits for Ember test waiters', function(assert) { this.render(hbs`{{x-test-5}}`); return wait({ waitForTimers: false }).then(() => { - assert.equal(this.$().text(), 'async value'); + assert.equal(this._element.textContent, 'async value'); }); }); });