From afab45e88c7fe0428bca333b9ac2d52c458abac1 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sun, 20 Aug 2017 17:12:10 +0300 Subject: [PATCH 01/20] move to dom-native-helpers - use own jquery --- .../-private/execution_context/acceptance.js | 40 ++++++++-------- addon/-private/execution_context/helpers.js | 6 ++- .../-private/execution_context/integration.js | 48 ++++++++----------- addon/-private/helpers.js | 4 +- addon/-private/properties/visitable.js | 3 +- index.js | 3 ++ package.json | 2 + 7 files changed, 53 insertions(+), 53 deletions(-) diff --git a/addon/-private/execution_context/acceptance.js b/addon/-private/execution_context/acceptance.js index 3c13c00f..b74cedb9 100644 --- a/addon/-private/execution_context/acceptance.js +++ b/addon/-private/execution_context/acceptance.js @@ -1,3 +1,12 @@ +import $ from 'jquery'; + +import { + click, + focus, + triggerEvent, + visit +} from 'ember-native-dom-helpers'; + import { guardMultiple, buildSelector, @@ -29,19 +38,16 @@ AcceptanceExecutionContext.prototype = { }, visit(path) { - /* global visit */ visit(path); }, click(selector, container) { - /* global click */ - click(selector, container); + click($(selector, container)[0]); }, fillIn(selector, container, options, content) { - let $selection = find(selector, container || findClosestValue(this.pageObjectNode, 'testContainer')); + const [$selection] = $(selector, container || findClosestValue(this.pageObjectNode, 'testContainer')); - /* global focus */ focus($selection); fillElement($selection, content, { @@ -50,19 +56,19 @@ AcceptanceExecutionContext.prototype = { pageObjectKey: options.pageObjectKey }); - /* global triggerEvent */ - triggerEvent(selector, container, 'input'); - triggerEvent(selector, container, 'change'); + triggerEvent($selection, 'input'); + triggerEvent($selection, 'change'); }, triggerEvent(selector, container, eventName, eventOptions) { - /* global triggerEvent */ - triggerEvent(selector, container, eventName, eventOptions); + const element = $(selector, container); + + triggerEvent(element, eventName, eventOptions); }, assertElementExists(selector, options) { /* global find */ - let result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + let result = $(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); if (result.length === 0) { throwBetterError( @@ -75,26 +81,18 @@ AcceptanceExecutionContext.prototype = { }, find(selector, options) { - let result; - selector = buildSelector(this.pageObjectNode, selector, options); - /* global find */ - result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); - + let result = $(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); guardMultiple(result, selector, options.multiple); return result; }, findWithAssert(selector, options) { - let result; - selector = buildSelector(this.pageObjectNode, selector, options); - /* global find */ - result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); - + let result = $(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); if (result.length === 0) { throwBetterError( this.pageObjectNode, diff --git a/addon/-private/execution_context/helpers.js b/addon/-private/execution_context/helpers.js index 67ebf1c8..a00c3100 100644 --- a/addon/-private/execution_context/helpers.js +++ b/addon/-private/execution_context/helpers.js @@ -2,6 +2,8 @@ import { throwBetterError } from '../better-errors'; +import $ from 'jquery'; + /** * @private * @@ -17,7 +19,9 @@ import { * * @throws Will throw an error if called on a contenteditable element that has `contenteditable="false"` */ -export function fillElement($selection, content, { selector, pageObjectNode, pageObjectKey }) { +export function fillElement(selection, content, { selector, pageObjectNode, pageObjectKey }) { + const $selection = $(selection); + if ($selection.is('[contenteditable][contenteditable!="false"]')) { $selection.html(content); } else if ($selection.is('[contenteditable="false"]')) { diff --git a/addon/-private/execution_context/integration.js b/addon/-private/execution_context/integration.js index f8962fe0..c34f728c 100644 --- a/addon/-private/execution_context/integration.js +++ b/addon/-private/execution_context/integration.js @@ -1,4 +1,11 @@ import Ember from 'ember'; +import $ from 'jquery'; + +import { + click, + triggerEvent +} from 'ember-native-dom-helpers'; + import { guardMultiple, buildSelector, @@ -12,7 +19,7 @@ import { throwBetterError } from '../better-errors'; -const { $, run } = Ember; +const { run } = Ember; export default function IntegrationExecutionContext(pageObjectNode, testContext) { this.pageObjectNode = pageObjectNode; @@ -36,7 +43,9 @@ IntegrationExecutionContext.prototype = { visit: $.noop, click(selector, container) { - this.$(selector, container).click(); + this.$(selector, container).each((pos, el) => { + click(el); + }) }, fillIn(selector, container, options, content) { @@ -48,37 +57,30 @@ IntegrationExecutionContext.prototype = { pageObjectKey: options.pageObjectKey }); - $selection.trigger('input'); - $selection.change(); + $selection.each((pos, el) => { + triggerEvent(el, 'input'); + triggerEvent(el, 'change'); + }); }, $(selector, container) { if (container) { return $(selector, container); } else { - return this.testContext.$(selector); + return $(selector, this.testContext._element); } }, triggerEvent(selector, container, eventName, eventOptions) { let event = $.Event(eventName, eventOptions); - if (container) { - $(selector, container).trigger(event); - } else { - this.testContext.$(selector).trigger(event); - } + this.$(selector, container).trigger(event); }, assertElementExists(selector, options) { - let result; let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); - if (container) { - result = $(selector, container); - } else { - result = this.testContext.$(selector); - } + let result = this.$(selector, container); if (result.length === 0) { throwBetterError( @@ -91,16 +93,11 @@ IntegrationExecutionContext.prototype = { }, find(selector, options) { - let result; let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); selector = buildSelector(this.pageObjectNode, selector, options); - if (container) { - result = $(selector, container); - } else { - result = this.testContext.$(selector); - } + let result = this.$(selector, container); guardMultiple(result, selector, options.multiple); @@ -108,16 +105,11 @@ IntegrationExecutionContext.prototype = { }, findWithAssert(selector, options) { - let result; let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); selector = buildSelector(this.pageObjectNode, selector, options); - if (container) { - result = $(selector, container); - } else { - result = this.testContext.$(selector); - } + let result = this.$(selector, container); guardMultiple(result, selector, options.multiple); diff --git a/addon/-private/helpers.js b/addon/-private/helpers.js index 18b3aa4f..706adf86 100644 --- a/addon/-private/helpers.js +++ b/addon/-private/helpers.js @@ -1,7 +1,9 @@ import Ember from 'ember'; import Ceibo from 'ceibo'; -const { $, assert, get, isPresent } = Ember; +const { assert, get, isPresent } = Ember; + +import $ from 'jquery'; class Selector { constructor(node, scope, selector, filters) { diff --git a/addon/-private/properties/visitable.js b/addon/-private/properties/visitable.js index 67350341..1978fe02 100644 --- a/addon/-private/properties/visitable.js +++ b/addon/-private/properties/visitable.js @@ -1,8 +1,7 @@ -import Ember from 'ember'; import { assign } from '../helpers'; import { getExecutionContext } from '../execution_context'; -const { $ } = Ember; +import $ from 'jquery'; function fillInDynamicSegments(path, params) { return path.split('/').map(function(segment) { diff --git a/index.js b/index.js index d22aac1d..7bffc5ef 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,9 @@ module.exports = { enabled: this._shouldIncludeFiles(), import: ['index.js'] }; + }, + jquery: { + import: ['dist/jquery.js'], } } }, diff --git a/package.json b/package.json index 78e7e4ae..c531cbfa 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,9 @@ "ember-cli-test-info": "^1.0.0", "ember-cli-valid-component-name": "^1.0.0", "ember-cli-version-checker": "^1.2.0", + "ember-native-dom-helpers": "^0.5.3", "ember-test-helpers": "^0.6.3", + "jquery": "^3.2.1", "rsvp": "^3.2.1" }, "ember-addon": { From 715c2dd446ec925f1742b248af49c01581a6fab9 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sun, 20 Aug 2017 23:35:20 +0300 Subject: [PATCH 02/20] unify elements search --- .../-private/execution_context/acceptance.js | 28 ++++++++++++++----- .../-private/execution_context/integration.js | 8 ++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/addon/-private/execution_context/acceptance.js b/addon/-private/execution_context/acceptance.js index b74cedb9..3e1b9c71 100644 --- a/addon/-private/execution_context/acceptance.js +++ b/addon/-private/execution_context/acceptance.js @@ -42,11 +42,11 @@ AcceptanceExecutionContext.prototype = { }, click(selector, container) { - click($(selector, container)[0]); + click(this.$(selector, container)[0]); }, fillIn(selector, container, options, content) { - const [$selection] = $(selector, container || findClosestValue(this.pageObjectNode, 'testContainer')); + const [$selection] = this.$(selector, container || findClosestValue(this.pageObjectNode, 'testContainer')); focus($selection); @@ -61,14 +61,13 @@ AcceptanceExecutionContext.prototype = { }, triggerEvent(selector, container, eventName, eventOptions) { - const element = $(selector, container); + const element = this.$(selector, container); triggerEvent(element, eventName, eventOptions); }, assertElementExists(selector, options) { - /* global find */ - let result = $(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + let result = this.$(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); if (result.length === 0) { throwBetterError( @@ -83,16 +82,31 @@ AcceptanceExecutionContext.prototype = { find(selector, options) { selector = buildSelector(this.pageObjectNode, selector, options); - let result = $(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + let result = this.$(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); guardMultiple(result, selector, options.multiple); return result; }, + $(selector, container) { + if (container) { + return $(selector, container); + } else { + let testsContainer = this.testContext ? + this.testContext._element : + '#ember-testing'; + + return $(selector, testsContainer); + } + }, + findWithAssert(selector, options) { + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + selector = buildSelector(this.pageObjectNode, selector, options); - let result = $(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + let result = this.$(selector, container); + if (result.length === 0) { throwBetterError( this.pageObjectNode, diff --git a/addon/-private/execution_context/integration.js b/addon/-private/execution_context/integration.js index c34f728c..c3dc87fe 100644 --- a/addon/-private/execution_context/integration.js +++ b/addon/-private/execution_context/integration.js @@ -45,7 +45,7 @@ IntegrationExecutionContext.prototype = { click(selector, container) { this.$(selector, container).each((pos, el) => { click(el); - }) + }); }, fillIn(selector, container, options, content) { @@ -67,7 +67,11 @@ IntegrationExecutionContext.prototype = { if (container) { return $(selector, container); } else { - return $(selector, this.testContext._element); + let testsContainer = this.testContext ? + this.testContext._element : + '#ember-testing'; + + return $(selector, testsContainer); } }, From 9455cce7d32bcb66df3f56b5adcbf06a6d650a97 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Tue, 22 Aug 2017 01:58:15 +0300 Subject: [PATCH 03/20] unify contexts --- .../-private/execution_context/acceptance.js | 124 ++--------------- addon/-private/execution_context/context.js | 123 ++++++++++++++++ .../-private/execution_context/integration.js | 131 ++---------------- 3 files changed, 140 insertions(+), 238 deletions(-) create mode 100644 addon/-private/execution_context/context.js diff --git a/addon/-private/execution_context/acceptance.js b/addon/-private/execution_context/acceptance.js index 3e1b9c71..94bc87ac 100644 --- a/addon/-private/execution_context/acceptance.js +++ b/addon/-private/execution_context/acceptance.js @@ -1,123 +1,15 @@ -import $ from 'jquery'; - -import { - click, - focus, - triggerEvent, - visit -} from 'ember-native-dom-helpers'; - -import { - guardMultiple, - buildSelector, - findClosestValue -} from '../helpers'; -import { - fillElement -} from './helpers'; -import { - ELEMENT_NOT_FOUND, - throwBetterError -} from '../better-errors'; +import ExecutionContext from './context'; export default function AcceptanceExecutionContext(pageObjectNode) { - this.pageObjectNode = pageObjectNode; + ExecutionContext.call(this, pageObjectNode); } -AcceptanceExecutionContext.prototype = { - run(cb) { - return cb(this); - }, - - runAsync(cb) { - window.wait().then(() => { - cb(this); - }); - - return this.pageObjectNode; - }, - - visit(path) { - visit(path); - }, - - click(selector, container) { - click(this.$(selector, container)[0]); - }, - - fillIn(selector, container, options, content) { - const [$selection] = this.$(selector, container || findClosestValue(this.pageObjectNode, 'testContainer')); - - focus($selection); - - fillElement($selection, content, { - selector, - pageObjectNode: this.pageObjectNode, - pageObjectKey: options.pageObjectKey - }); - - triggerEvent($selection, 'input'); - triggerEvent($selection, 'change'); - }, - - triggerEvent(selector, container, eventName, eventOptions) { - const element = this.$(selector, container); - - triggerEvent(element, eventName, eventOptions); - }, - - assertElementExists(selector, options) { - let result = this.$(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); - - if (result.length === 0) { - throwBetterError( - this.pageObjectNode, - options.pageObjectKey, - ELEMENT_NOT_FOUND, - { selector } - ); - } - }, - - find(selector, options) { - selector = buildSelector(this.pageObjectNode, selector, options); - - let result = this.$(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); - guardMultiple(result, selector, options.multiple); - - return result; - }, - - $(selector, container) { - if (container) { - return $(selector, container); - } else { - let testsContainer = this.testContext ? - this.testContext._element : - '#ember-testing'; - - return $(selector, testsContainer); - } - }, - - findWithAssert(selector, options) { - let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); - - selector = buildSelector(this.pageObjectNode, selector, options); - - let result = this.$(selector, container); - - if (result.length === 0) { - throwBetterError( - this.pageObjectNode, - options.pageObjectKey, - ELEMENT_NOT_FOUND, - { selector } - ); - } +AcceptanceExecutionContext.prototype = Object.create(ExecutionContext.prototype); - guardMultiple(result, selector, options.multiple); +AcceptanceExecutionContext.prototype.runAsync = function(cb) { + window.wait().then(() => { + cb(this); + }); - return result; - } + return this.pageObjectNode; }; diff --git a/addon/-private/execution_context/context.js b/addon/-private/execution_context/context.js new file mode 100644 index 00000000..799954f5 --- /dev/null +++ b/addon/-private/execution_context/context.js @@ -0,0 +1,123 @@ +import $ from 'jquery'; + +import { + click, + triggerEvent, + visit +} from 'ember-native-dom-helpers'; + +import { + guardMultiple, + buildSelector, + findClosestValue +} from '../helpers'; +import { + fillElement +} from './helpers'; +import { + ELEMENT_NOT_FOUND, + throwBetterError +} from '../better-errors'; + +export default function ExecutionContext(pageObjectNode, testContext) { + this.pageObjectNode = pageObjectNode; + this.testContext = testContext; +} + +ExecutionContext.prototype = { + run(cb) { + return cb(this); + }, + + runAsync() { + throw new Error('not implemented'); + }, + + // Do nothing in integration test + visit: visit, + + click(selector, container) { + const [el] = this.$(selector, container); + click(el); + }, + + fillIn(selector, container, options, content) { + let [el] = this.$(selector, container); + + fillElement(el, content, { + selector, + pageObjectNode: this.pageObjectNode, + pageObjectKey: options.pageObjectKey + }); + + triggerEvent(el, 'input'); + triggerEvent(el, 'change'); + }, + + $(selector, container) { + if (container) { + return $(selector, container); + } else { + let testsContainer = this.testContext ? + this.testContext._element : + '#ember-testing'; + + return $(selector, testsContainer); + } + }, + + triggerEvent(selector, container, eventName, eventOptions) { + const element = this.$(selector, container); + + triggerEvent(element, eventName, eventOptions); + }, + + assertElementExists(selector, options) { + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + + let result = this.$(selector, container); + + if (result.length === 0) { + throwBetterError( + this.pageObjectNode, + options.pageObjectKey, + ELEMENT_NOT_FOUND, + { selector } + ); + } + }, + + find(selector, options) { + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + + selector = buildSelector(this.pageObjectNode, selector, options); + + let result = this.$(selector, container); + + guardMultiple(result, selector, options.multiple); + + return result; + }, + + findWithAssert(selector, options) { + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + + selector = buildSelector(this.pageObjectNode, selector, options); + + let result = this.$(selector, container); + + if (result.length === 0) { + throwBetterError( + this.pageObjectNode, + options.pageObjectKey, + ELEMENT_NOT_FOUND, + { selector } + ); + } + + guardMultiple(result, selector, options.multiple); + + return result; + } +}; + diff --git a/addon/-private/execution_context/integration.js b/addon/-private/execution_context/integration.js index c3dc87fe..682568d1 100644 --- a/addon/-private/execution_context/integration.js +++ b/addon/-private/execution_context/integration.js @@ -1,131 +1,18 @@ -import Ember from 'ember'; -import $ from 'jquery'; - -import { - click, - triggerEvent -} from 'ember-native-dom-helpers'; - -import { - guardMultiple, - buildSelector, - findClosestValue -} from '../helpers'; -import { - fillElement -} from './helpers'; -import { - ELEMENT_NOT_FOUND, - throwBetterError -} from '../better-errors'; +import ExecutionContext from './context'; +import Ember from 'ember'; const { run } = Ember; export default function IntegrationExecutionContext(pageObjectNode, testContext) { - this.pageObjectNode = pageObjectNode; - this.testContext = testContext; + ExecutionContext.call(this, pageObjectNode, testContext); } -IntegrationExecutionContext.prototype = { - run(cb) { - return cb(this); - }, - - runAsync(cb) { - run(() => { - cb(this); - }); - - return this.pageObjectNode; - }, - - // Do nothing in integration test - visit: $.noop, - - click(selector, container) { - this.$(selector, container).each((pos, el) => { - click(el); - }); - }, - - fillIn(selector, container, options, content) { - let $selection = this.$(selector, container); - - fillElement($selection, content, { - selector, - pageObjectNode: this.pageObjectNode, - pageObjectKey: options.pageObjectKey - }); - - $selection.each((pos, el) => { - triggerEvent(el, 'input'); - triggerEvent(el, 'change'); - }); - }, - - $(selector, container) { - if (container) { - return $(selector, container); - } else { - let testsContainer = this.testContext ? - this.testContext._element : - '#ember-testing'; - - return $(selector, testsContainer); - } - }, - - triggerEvent(selector, container, eventName, eventOptions) { - let event = $.Event(eventName, eventOptions); - - this.$(selector, container).trigger(event); - }, - - assertElementExists(selector, options) { - let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); - - let result = this.$(selector, container); - - if (result.length === 0) { - throwBetterError( - this.pageObjectNode, - options.pageObjectKey, - ELEMENT_NOT_FOUND, - { selector } - ); - } - }, - - find(selector, options) { - let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); - - selector = buildSelector(this.pageObjectNode, selector, options); - - let result = this.$(selector, container); - - guardMultiple(result, selector, options.multiple); - - return result; - }, - - findWithAssert(selector, options) { - let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); - - selector = buildSelector(this.pageObjectNode, selector, options); - - let result = this.$(selector, container); - - guardMultiple(result, selector, options.multiple); +IntegrationExecutionContext.prototype = Object.create(ExecutionContext.prototype); - if (result.length === 0) { - throwBetterError( - this.pageObjectNode, - options.pageObjectKey, - ELEMENT_NOT_FOUND, - { selector } - ); - } +IntegrationExecutionContext.prototype.runAsync = function(cb) { + run(() => { + cb(this); + }); - return result; - } + return this.pageObjectNode; }; From 5e8b1f2a182a8ef68d0e86c88b1d58e5a7bbc466 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Tue, 22 Aug 2017 22:48:54 +0300 Subject: [PATCH 04/20] add useNativeDOMHelpers --- .../execution_context/acceptance-native.js | 15 ++ .../-private/execution_context/acceptance.js | 112 +++++++++++++- .../execution_context/integration-native.js | 18 +++ .../-private/execution_context/integration.js | 139 ++++++++++++++++-- addon/extend.js | 12 +- 5 files changed, 277 insertions(+), 19 deletions(-) create mode 100644 addon/-private/execution_context/acceptance-native.js create mode 100644 addon/-private/execution_context/integration-native.js diff --git a/addon/-private/execution_context/acceptance-native.js b/addon/-private/execution_context/acceptance-native.js new file mode 100644 index 00000000..94bc87ac --- /dev/null +++ b/addon/-private/execution_context/acceptance-native.js @@ -0,0 +1,15 @@ +import ExecutionContext from './context'; + +export default function AcceptanceExecutionContext(pageObjectNode) { + ExecutionContext.call(this, pageObjectNode); +} + +AcceptanceExecutionContext.prototype = Object.create(ExecutionContext.prototype); + +AcceptanceExecutionContext.prototype.runAsync = function(cb) { + window.wait().then(() => { + cb(this); + }); + + return this.pageObjectNode; +}; diff --git a/addon/-private/execution_context/acceptance.js b/addon/-private/execution_context/acceptance.js index 94bc87ac..3c13c00f 100644 --- a/addon/-private/execution_context/acceptance.js +++ b/addon/-private/execution_context/acceptance.js @@ -1,15 +1,111 @@ -import ExecutionContext from './context'; +import { + guardMultiple, + buildSelector, + findClosestValue +} from '../helpers'; +import { + fillElement +} from './helpers'; +import { + ELEMENT_NOT_FOUND, + throwBetterError +} from '../better-errors'; export default function AcceptanceExecutionContext(pageObjectNode) { - ExecutionContext.call(this, pageObjectNode); + this.pageObjectNode = pageObjectNode; } -AcceptanceExecutionContext.prototype = Object.create(ExecutionContext.prototype); +AcceptanceExecutionContext.prototype = { + run(cb) { + return cb(this); + }, -AcceptanceExecutionContext.prototype.runAsync = function(cb) { - window.wait().then(() => { - cb(this); - }); + runAsync(cb) { + window.wait().then(() => { + cb(this); + }); - return this.pageObjectNode; + return this.pageObjectNode; + }, + + visit(path) { + /* global visit */ + visit(path); + }, + + click(selector, container) { + /* global click */ + click(selector, container); + }, + + fillIn(selector, container, options, content) { + let $selection = find(selector, container || findClosestValue(this.pageObjectNode, 'testContainer')); + + /* global focus */ + focus($selection); + + fillElement($selection, content, { + selector, + pageObjectNode: this.pageObjectNode, + pageObjectKey: options.pageObjectKey + }); + + /* global triggerEvent */ + triggerEvent(selector, container, 'input'); + triggerEvent(selector, container, 'change'); + }, + + triggerEvent(selector, container, eventName, eventOptions) { + /* global triggerEvent */ + triggerEvent(selector, container, eventName, eventOptions); + }, + + assertElementExists(selector, options) { + /* global find */ + let result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + + if (result.length === 0) { + throwBetterError( + this.pageObjectNode, + options.pageObjectKey, + ELEMENT_NOT_FOUND, + { selector } + ); + } + }, + + find(selector, options) { + let result; + + selector = buildSelector(this.pageObjectNode, selector, options); + + /* global find */ + result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + + guardMultiple(result, selector, options.multiple); + + return result; + }, + + findWithAssert(selector, options) { + let result; + + selector = buildSelector(this.pageObjectNode, selector, options); + + /* global find */ + result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + + if (result.length === 0) { + throwBetterError( + this.pageObjectNode, + options.pageObjectKey, + ELEMENT_NOT_FOUND, + { selector } + ); + } + + guardMultiple(result, selector, options.multiple); + + return result; + } }; diff --git a/addon/-private/execution_context/integration-native.js b/addon/-private/execution_context/integration-native.js new file mode 100644 index 00000000..682568d1 --- /dev/null +++ b/addon/-private/execution_context/integration-native.js @@ -0,0 +1,18 @@ +import ExecutionContext from './context'; + +import Ember from 'ember'; +const { run } = Ember; + +export default function IntegrationExecutionContext(pageObjectNode, testContext) { + ExecutionContext.call(this, pageObjectNode, testContext); +} + +IntegrationExecutionContext.prototype = Object.create(ExecutionContext.prototype); + +IntegrationExecutionContext.prototype.runAsync = function(cb) { + run(() => { + cb(this); + }); + + return this.pageObjectNode; +}; diff --git a/addon/-private/execution_context/integration.js b/addon/-private/execution_context/integration.js index 682568d1..36edbeb3 100644 --- a/addon/-private/execution_context/integration.js +++ b/addon/-private/execution_context/integration.js @@ -1,18 +1,137 @@ -import ExecutionContext from './context'; - import Ember from 'ember'; -const { run } = Ember; +import { + guardMultiple, + buildSelector, + findClosestValue +} from '../helpers'; +import { + fillElement +} from './helpers'; +import { + ELEMENT_NOT_FOUND, + throwBetterError +} from '../better-errors'; + +const { $, run } = Ember; export default function IntegrationExecutionContext(pageObjectNode, testContext) { - ExecutionContext.call(this, pageObjectNode, testContext); + this.pageObjectNode = pageObjectNode; + this.testContext = testContext; } -IntegrationExecutionContext.prototype = Object.create(ExecutionContext.prototype); +IntegrationExecutionContext.prototype = { + run(cb) { + return cb(this); + }, + + runAsync(cb) { + run(() => { + cb(this); + }); + + return this.pageObjectNode; + }, + + // Do nothing in integration test + visit() { + throw new Error('visit is not supported in integration mode'); + }, + + click(selector, container) { + this.$(selector, container).click(); + }, + + fillIn(selector, container, options, content) { + let $selection = this.$(selector, container); + + fillElement($selection, content, { + selector, + pageObjectNode: this.pageObjectNode, + pageObjectKey: options.pageObjectKey + }); + + $selection.trigger('input'); + $selection.change(); + }, + + $(selector, container) { + if (container) { + return $(selector, container); + } else { + return this.testContext.$(selector); + } + }, + + triggerEvent(selector, container, eventName, eventOptions) { + let event = $.Event(eventName, eventOptions); + + if (container) { + $(selector, container).trigger(event); + } else { + this.testContext.$(selector).trigger(event); + } + }, + + assertElementExists(selector, options) { + let result; + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + + if (container) { + result = $(selector, container); + } else { + result = this.testContext.$(selector); + } + + if (result.length === 0) { + throwBetterError( + this.pageObjectNode, + options.pageObjectKey, + ELEMENT_NOT_FOUND, + { selector } + ); + } + }, + + find(selector, options) { + let result; + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + + selector = buildSelector(this.pageObjectNode, selector, options); + + if (container) { + result = $(selector, container); + } else { + result = this.testContext.$(selector); + } + + guardMultiple(result, selector, options.multiple); + + return result; + }, + + findWithAssert(selector, options) { + let result; + let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); + + selector = buildSelector(this.pageObjectNode, selector, options); + + if (container) { + result = $(selector, container); + } else { + result = this.testContext.$(selector); + } + + guardMultiple(result, selector, options.multiple); -IntegrationExecutionContext.prototype.runAsync = function(cb) { - run(() => { - cb(this); - }); + if (result.length === 0) { + throwBetterError( + this.pageObjectNode, + options.pageObjectKey, + ELEMENT_NOT_FOUND, + { selector } + ); + } - return this.pageObjectNode; + return result; + } }; diff --git a/addon/extend.js b/addon/extend.js index 70eed79d..ef8feeb6 100644 --- a/addon/extend.js +++ b/addon/extend.js @@ -1,4 +1,14 @@ export { findElement } from './-private/extend/find-element'; export { findElementWithAssert } from './-private/extend/find-element-with-assert'; export { buildSelector, getContext, fullScope } from './-private/helpers'; -export { register as registerExecutionContext } from './-private/execution_context'; +import { register as registerExecutionContext } from './-private/execution_context'; + +import IntegrationNativeContext from './-private/execution_context/integration-native'; +import AcceptanceNativeContext from './-private/execution_context/acceptance-native'; + +function useNativeDOMHelpers() { + registerExecutionContext('integration', IntegrationNativeContext); + registerExecutionContext('acceptance', AcceptanceNativeContext); +} + +export { registerExecutionContext, useNativeDOMHelpers }; From 6a22b5553c6cdb373b76118464c3204076e320cf Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Thu, 21 Sep 2017 18:25:58 +0300 Subject: [PATCH 05/20] trigger first selected element --- addon/-private/execution_context/context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/-private/execution_context/context.js b/addon/-private/execution_context/context.js index 799954f5..cff3f2e2 100644 --- a/addon/-private/execution_context/context.js +++ b/addon/-private/execution_context/context.js @@ -67,7 +67,7 @@ ExecutionContext.prototype = { }, triggerEvent(selector, container, eventName, eventOptions) { - const element = this.$(selector, container); + const [element] = this.$(selector, container); triggerEvent(element, eventName, eventOptions); }, From ab28fabf542f5a73ac2d3694d156dd9a15338cc3 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Tue, 26 Sep 2017 13:35:58 +0300 Subject: [PATCH 06/20] conditional jquery import --- index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 7bffc5ef..6d10bd5f 100644 --- a/index.js +++ b/index.js @@ -12,8 +12,11 @@ module.exports = { import: ['index.js'] }; }, - jquery: { - import: ['dist/jquery.js'], + jquery: function() { + return { + enabled: this._shouldIncludeFiles(), + import: ['dist/jquery.js'], + } } } }, From f3b8748957805480e2c7f8abc8ae7494b0918349 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Wed, 27 Sep 2017 01:25:20 +0300 Subject: [PATCH 07/20] prepend jquery --- index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 6d10bd5f..ebef60ed 100644 --- a/index.js +++ b/index.js @@ -15,7 +15,8 @@ module.exports = { jquery: function() { return { enabled: this._shouldIncludeFiles(), - import: ['dist/jquery.js'], + vendor: ['dist/jquery.js'], + destDir: 'ecpo-jquery' } } } @@ -29,6 +30,12 @@ module.exports = { this.app = app; + if (this._shouldIncludeFiles()) { + this.import('vendor/ecpo-jquery/dist/jquery.js', { + prepend: true + }); + } + this._super.included.apply(this, arguments); }, From b0fabcbc49c2b1989338a9c0a485534e04022bc6 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sat, 30 Sep 2017 16:05:14 +0300 Subject: [PATCH 08/20] truly isolate jquery --- addon/-private/execution_context/context.js | 2 +- addon/-private/execution_context/helpers.js | 2 +- addon/-private/helpers.js | 2 +- addon/-private/properties/visitable.js | 2 +- index.js | 5 ++--- vendor/shims/-jquery.js | 10 ++++++++++ 6 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 vendor/shims/-jquery.js diff --git a/addon/-private/execution_context/context.js b/addon/-private/execution_context/context.js index cff3f2e2..3e4d2af8 100644 --- a/addon/-private/execution_context/context.js +++ b/addon/-private/execution_context/context.js @@ -1,4 +1,4 @@ -import $ from 'jquery'; +import $ from '-jquery'; import { click, diff --git a/addon/-private/execution_context/helpers.js b/addon/-private/execution_context/helpers.js index a00c3100..1841af1c 100644 --- a/addon/-private/execution_context/helpers.js +++ b/addon/-private/execution_context/helpers.js @@ -2,7 +2,7 @@ import { throwBetterError } from '../better-errors'; -import $ from 'jquery'; +import $ from '-jquery'; /** * @private diff --git a/addon/-private/helpers.js b/addon/-private/helpers.js index 706adf86..8f17b044 100644 --- a/addon/-private/helpers.js +++ b/addon/-private/helpers.js @@ -3,7 +3,7 @@ import Ceibo from 'ceibo'; const { assert, get, isPresent } = Ember; -import $ from 'jquery'; +import $ from '-jquery'; class Selector { constructor(node, scope, selector, filters) { diff --git a/addon/-private/properties/visitable.js b/addon/-private/properties/visitable.js index 1978fe02..f58e0d31 100644 --- a/addon/-private/properties/visitable.js +++ b/addon/-private/properties/visitable.js @@ -1,7 +1,7 @@ import { assign } from '../helpers'; import { getExecutionContext } from '../execution_context'; -import $ from 'jquery'; +import $ from '-jquery'; function fillInDynamicSegments(path, params) { return path.split('/').map(function(segment) { diff --git a/index.js b/index.js index ebef60ed..c4e8b750 100644 --- a/index.js +++ b/index.js @@ -31,9 +31,8 @@ module.exports = { this.app = app; if (this._shouldIncludeFiles()) { - this.import('vendor/ecpo-jquery/dist/jquery.js', { - prepend: true - }); + this.import('vendor/shims/-jquery.js', { prepend: true }); + this.import('vendor/ecpo-jquery/dist/jquery.js', { prepend: true }); } this._super.included.apply(this, arguments); diff --git a/vendor/shims/-jquery.js b/vendor/shims/-jquery.js new file mode 100644 index 00000000..80312b3d --- /dev/null +++ b/vendor/shims/-jquery.js @@ -0,0 +1,10 @@ +(function() { + function vendorModule() { + 'use strict'; + + var jq = self['$'].noConflict(); + return { 'default': jq }; + } + + define('-jquery', [], vendorModule); +})(); From 6e3b06db54358ba40488895250faf2bb3b5c31c1 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sat, 30 Sep 2017 16:05:34 +0300 Subject: [PATCH 09/20] bump ember-cli-node-assets --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c531cbfa..c77cf303 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "ember-cli-babel": "^5.1.7", "ember-cli-get-component-path-option": "^1.0.0", "ember-cli-is-package-missing": "^1.0.0", - "ember-cli-node-assets": "^0.1.2", + "ember-cli-node-assets": "^0.2.2", "ember-cli-normalize-entity-name": "^1.0.0", "ember-cli-path-utils": "^1.0.0", "ember-cli-string-utils": "^1.0.0", From 2f35680cffb0b016eddef6fe4347ee1d78405770 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Tue, 3 Oct 2017 23:53:43 +0300 Subject: [PATCH 10/20] work-around deprecated keyCode property of KeyboardEvent --- addon/-private/execution_context/context.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addon/-private/execution_context/context.js b/addon/-private/execution_context/context.js index 3e4d2af8..78b1056a 100644 --- a/addon/-private/execution_context/context.js +++ b/addon/-private/execution_context/context.js @@ -69,6 +69,14 @@ ExecutionContext.prototype = { triggerEvent(selector, container, eventName, eventOptions) { const [element] = this.$(selector, container); + // `keyCode` is a deprecated property. + // @see: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode + // Due to this deprecation `ember-native-dom-helpers` doesn't accept `keyCode` as a `KeyboardEvent` option. + if (typeof eventOptions.key === 'undefined' && typeof eventOptions.keyCode !== 'undefined') { + eventOptions.key = eventOptions.keyCode.toString(); + delete eventOptions.keyCode; + } + triggerEvent(element, eventName, eventOptions); }, From 144c4febef40df967bc6af99161912fca9ddc74d Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Tue, 3 Oct 2017 22:17:30 +0300 Subject: [PATCH 11/20] add native-context to acceptance tests --- addon/extend.js | 13 +++++++--- tests/helpers/module-for-acceptance.js | 34 +++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/addon/extend.js b/addon/extend.js index ef8feeb6..0fa58750 100644 --- a/addon/extend.js +++ b/addon/extend.js @@ -5,10 +5,17 @@ import { register as registerExecutionContext } from './-private/execution_conte import IntegrationNativeContext from './-private/execution_context/integration-native'; import AcceptanceNativeContext from './-private/execution_context/acceptance-native'; +import IntegrationClassicContext from './-private/execution_context/integration'; +import AcceptanceClassicContext from './-private/execution_context/acceptance'; -function useNativeDOMHelpers() { - registerExecutionContext('integration', IntegrationNativeContext); - registerExecutionContext('acceptance', AcceptanceNativeContext); +function useNativeDOMHelpers(flag = true) { + if (flag) { + registerExecutionContext('integration', IntegrationNativeContext); + registerExecutionContext('acceptance', AcceptanceNativeContext); + } else { + registerExecutionContext('integration', IntegrationClassicContext); + registerExecutionContext('acceptance', AcceptanceClassicContext); + } } export { registerExecutionContext, useNativeDOMHelpers }; diff --git a/tests/helpers/module-for-acceptance.js b/tests/helpers/module-for-acceptance.js index 76996fd0..eaa20493 100644 --- a/tests/helpers/module-for-acceptance.js +++ b/tests/helpers/module-for-acceptance.js @@ -2,22 +2,34 @@ import { module } from 'qunit'; import Ember from 'ember'; import startApp from '../helpers/start-app'; import destroyApp from '../helpers/destroy-app'; +import { useNativeDOMHelpers } from 'ember-cli-page-object/extend'; const { RSVP: { Promise } } = Ember; export default function(name, options = {}) { - module(name, { - beforeEach() { - this.application = startApp(); + [false, true].forEach(_useNativeDOMHelpers => { + let moduleName = name; + if (_useNativeDOMHelpers) { + moduleName += ' [native-dom-helpers]'; + } - if (options.beforeEach) { - return options.beforeEach.apply(this, arguments); - } - }, + module(moduleName, { + beforeEach() { + this.application = startApp(); - afterEach() { - let afterEach = options.afterEach && options.afterEach.apply(this, arguments); - return Promise.resolve(afterEach).then(() => destroyApp(this.application)); - } + useNativeDOMHelpers(_useNativeDOMHelpers); + + if (options.beforeEach) { + return options.beforeEach.apply(this, arguments); + } + }, + + afterEach() { + useNativeDOMHelpers(false); + + let afterEach = options.afterEach && options.afterEach.apply(this, arguments); + return Promise.resolve(afterEach).then(() => destroyApp(this.application)); + } + }); }); } From 16fd45bccdd17c1a8b7ffcd8ae83f262e36e7c0a Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Tue, 29 Aug 2017 21:51:00 +0300 Subject: [PATCH 12/20] update tests to work with native-dom-helpers --- tests/helpers/properties.js | 65 ++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/tests/helpers/properties.js b/tests/helpers/properties.js index adfaa2ca..f84e7924 100644 --- a/tests/helpers/properties.js +++ b/tests/helpers/properties.js @@ -1,37 +1,56 @@ import AcceptanceExecutionContext from 'ember-cli-page-object/-private/execution_context/acceptance'; import IntegrationExecutionContext from 'ember-cli-page-object/-private/execution_context/integration'; import { AcceptanceAdapter, moduleForAcceptance, testForAcceptance } from './properties/acceptance-adapter'; + import { IntegrationAdapter, moduleForIntegration, testForIntegration } from './properties/integration-adapter'; +import { useNativeDOMHelpers } from 'ember-cli-page-object/extend'; + export function moduleForProperty(name, cbOrOptions, cb) { let options = cb ? cbOrOptions : {}; cb = cb || cbOrOptions; - // Generate acceptance tests - moduleForAcceptance(`Acceptance mode | Property | ${name}`, { - beforeEach() { - this.adapter = new AcceptanceAdapter(AcceptanceExecutionContext); - }, + [true, false].forEach(_useNativeDOMHelpers => { + // Generate acceptance tests - afterEach() { - this.adapter.revert(); + let moduleNamePrefix = 'Acceptance mode '; + if (_useNativeDOMHelpers) { + moduleNamePrefix += ' [native-dom-helpers]'; } - }); - cb(testForAcceptance, 'acceptance'); - - if (options.acceptanceOnly) { - return; - } - - // Generate integration tests - moduleForIntegration('html-render', `Integration mode | Property | ${name}`, { - integration: true, - beforeEach() { - this.adapter = new IntegrationAdapter(IntegrationExecutionContext); - }, - afterEach() { - this.adapter.revert(); + moduleForAcceptance(`${moduleNamePrefix} | Property | ${name}`, { + beforeEach() { + useNativeDOMHelpers(_useNativeDOMHelpers); + + // if (!_useNativeDOMHelpers) { + this.adapter = new AcceptanceAdapter(AcceptanceExecutionContext); + // } + }, + + afterEach() { + useNativeDOMHelpers(false); + this.adapter.revert(); + } + }); + cb(testForAcceptance, 'acceptance'); + + if (options.acceptanceOnly) { + return; } + + // Generate integration tests + moduleForIntegration('html-render', `Integration mode | Property | ${name}`, { + integration: true, + beforeEach() { + useNativeDOMHelpers(_useNativeDOMHelpers); + // if (!_useNativeDOMHelpers) { + this.adapter = new IntegrationAdapter(IntegrationExecutionContext); + // } + }, + afterEach() { + useNativeDOMHelpers(false); + this.adapter.revert(); + } + }); + cb(testForIntegration, 'integration'); }); - cb(testForIntegration, 'integration'); } From 50d8d33d91c291121a7682bb83128f7d02f7b93e Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Wed, 4 Oct 2017 22:27:36 +0300 Subject: [PATCH 13/20] don't destruct jquery collection --- addon/-private/execution_context/context.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/-private/execution_context/context.js b/addon/-private/execution_context/context.js index 78b1056a..034d6f3a 100644 --- a/addon/-private/execution_context/context.js +++ b/addon/-private/execution_context/context.js @@ -37,12 +37,12 @@ ExecutionContext.prototype = { visit: visit, click(selector, container) { - const [el] = this.$(selector, container); + const el = this.$(selector, container)[0]; click(el); }, fillIn(selector, container, options, content) { - let [el] = this.$(selector, container); + let el = this.$(selector, container)[0]; fillElement(el, content, { selector, @@ -67,7 +67,7 @@ ExecutionContext.prototype = { }, triggerEvent(selector, container, eventName, eventOptions) { - const [element] = this.$(selector, container); + const element = this.$(selector, container)[0]; // `keyCode` is a deprecated property. // @see: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode From 01d28c6645250fad0eb08a8a71b68103f259f99d Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Wed, 4 Oct 2017 23:58:05 +0300 Subject: [PATCH 14/20] isolate jquery truly truly --- vendor/shims/-jquery.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vendor/shims/-jquery.js b/vendor/shims/-jquery.js index 80312b3d..9c7b172c 100644 --- a/vendor/shims/-jquery.js +++ b/vendor/shims/-jquery.js @@ -1,8 +1,9 @@ (function() { + var jq = self['$'].noConflict(); + function vendorModule() { 'use strict'; - var jq = self['$'].noConflict(); return { 'default': jq }; } From a56074e47385e86ccac62771735d14a42047b420 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Mon, 9 Oct 2017 21:48:22 +0300 Subject: [PATCH 15/20] rename contexts from native to native-dom --- addon/-private/execution_context.js | 8 ++++---- ...ptance-native.js => acceptance-native-dom.js} | 10 +++++++++- ...ation-native.js => integration-native-dom.js} | 2 +- addon/-private/execution_context/integration.js | 1 - .../{context.js => native-dom-context.js} | 8 +++----- addon/extend.js | 16 ++++++++-------- tests/helpers/properties.js | 9 +++------ 7 files changed, 28 insertions(+), 26 deletions(-) rename addon/-private/execution_context/{acceptance-native.js => acceptance-native-dom.js} (63%) rename addon/-private/execution_context/{integration-native.js => integration-native-dom.js} (88%) rename addon/-private/execution_context/{context.js => native-dom-context.js} (95%) diff --git a/addon/-private/execution_context.js b/addon/-private/execution_context.js index fb425eeb..9165df45 100644 --- a/addon/-private/execution_context.js +++ b/addon/-private/execution_context.js @@ -1,10 +1,10 @@ import { getContext } from './helpers'; -import AcceptanceExecutionContext from './execution_context/acceptance'; -import IntegrationExecutionContext from './execution_context/integration'; +import AcceptanceEmberExecutionContext from './execution_context/acceptance'; +import IntegrationEmberExecutionContext from './execution_context/integration'; const executioncontexts = { - acceptance: AcceptanceExecutionContext, - integration: IntegrationExecutionContext + acceptance: AcceptanceEmberExecutionContext, + integration: IntegrationEmberExecutionContext }; /* diff --git a/addon/-private/execution_context/acceptance-native.js b/addon/-private/execution_context/acceptance-native-dom.js similarity index 63% rename from addon/-private/execution_context/acceptance-native.js rename to addon/-private/execution_context/acceptance-native-dom.js index 94bc87ac..3b037ac5 100644 --- a/addon/-private/execution_context/acceptance-native.js +++ b/addon/-private/execution_context/acceptance-native-dom.js @@ -1,4 +1,8 @@ -import ExecutionContext from './context'; +import ExecutionContext from './native-dom-context'; + +import { + visit +} from 'ember-native-dom-helpers'; export default function AcceptanceExecutionContext(pageObjectNode) { ExecutionContext.call(this, pageObjectNode); @@ -6,6 +10,10 @@ export default function AcceptanceExecutionContext(pageObjectNode) { AcceptanceExecutionContext.prototype = Object.create(ExecutionContext.prototype); +AcceptanceExecutionContext.prototype.visit = function() { + return visit(...arguments); +}; + AcceptanceExecutionContext.prototype.runAsync = function(cb) { window.wait().then(() => { cb(this); diff --git a/addon/-private/execution_context/integration-native.js b/addon/-private/execution_context/integration-native-dom.js similarity index 88% rename from addon/-private/execution_context/integration-native.js rename to addon/-private/execution_context/integration-native-dom.js index 682568d1..879f79a0 100644 --- a/addon/-private/execution_context/integration-native.js +++ b/addon/-private/execution_context/integration-native-dom.js @@ -1,4 +1,4 @@ -import ExecutionContext from './context'; +import ExecutionContext from './native-dom-context'; import Ember from 'ember'; const { run } = Ember; diff --git a/addon/-private/execution_context/integration.js b/addon/-private/execution_context/integration.js index 36edbeb3..f9ee8648 100644 --- a/addon/-private/execution_context/integration.js +++ b/addon/-private/execution_context/integration.js @@ -32,7 +32,6 @@ IntegrationExecutionContext.prototype = { return this.pageObjectNode; }, - // Do nothing in integration test visit() { throw new Error('visit is not supported in integration mode'); }, diff --git a/addon/-private/execution_context/context.js b/addon/-private/execution_context/native-dom-context.js similarity index 95% rename from addon/-private/execution_context/context.js rename to addon/-private/execution_context/native-dom-context.js index 034d6f3a..ea535f14 100644 --- a/addon/-private/execution_context/context.js +++ b/addon/-private/execution_context/native-dom-context.js @@ -2,8 +2,7 @@ import $ from '-jquery'; import { click, - triggerEvent, - visit + triggerEvent } from 'ember-native-dom-helpers'; import { @@ -33,9 +32,6 @@ ExecutionContext.prototype = { throw new Error('not implemented'); }, - // Do nothing in integration test - visit: visit, - click(selector, container) { const el = this.$(selector, container)[0]; click(el); @@ -58,6 +54,8 @@ ExecutionContext.prototype = { if (container) { return $(selector, container); } else { + // @todo: we should fixed usage of private `_element` + // after https://github.com/emberjs/ember-test-helpers/issues/184 is resolved let testsContainer = this.testContext ? this.testContext._element : '#ember-testing'; diff --git a/addon/extend.js b/addon/extend.js index 0fa58750..eb1928a9 100644 --- a/addon/extend.js +++ b/addon/extend.js @@ -3,18 +3,18 @@ export { findElementWithAssert } from './-private/extend/find-element-with-asser export { buildSelector, getContext, fullScope } from './-private/helpers'; import { register as registerExecutionContext } from './-private/execution_context'; -import IntegrationNativeContext from './-private/execution_context/integration-native'; -import AcceptanceNativeContext from './-private/execution_context/acceptance-native'; -import IntegrationClassicContext from './-private/execution_context/integration'; -import AcceptanceClassicContext from './-private/execution_context/acceptance'; +import IntegrationNativeDOMContext from './-private/execution_context/integration-native-dom'; +import AcceptanceNativeDOMContext from './-private/execution_context/acceptance-native-dom'; +import IntegrationEmberContext from './-private/execution_context/integration'; +import AcceptanceEmberContext from './-private/execution_context/acceptance'; function useNativeDOMHelpers(flag = true) { if (flag) { - registerExecutionContext('integration', IntegrationNativeContext); - registerExecutionContext('acceptance', AcceptanceNativeContext); + registerExecutionContext('integration', IntegrationNativeDOMContext); + registerExecutionContext('acceptance', AcceptanceNativeDOMContext); } else { - registerExecutionContext('integration', IntegrationClassicContext); - registerExecutionContext('acceptance', AcceptanceClassicContext); + registerExecutionContext('integration', IntegrationEmberContext); + registerExecutionContext('acceptance', AcceptanceEmberContext); } } diff --git a/tests/helpers/properties.js b/tests/helpers/properties.js index f84e7924..3f9a21e3 100644 --- a/tests/helpers/properties.js +++ b/tests/helpers/properties.js @@ -21,9 +21,7 @@ export function moduleForProperty(name, cbOrOptions, cb) { beforeEach() { useNativeDOMHelpers(_useNativeDOMHelpers); - // if (!_useNativeDOMHelpers) { - this.adapter = new AcceptanceAdapter(AcceptanceExecutionContext); - // } + this.adapter = new AcceptanceAdapter(AcceptanceExecutionContext); }, afterEach() { @@ -42,9 +40,8 @@ export function moduleForProperty(name, cbOrOptions, cb) { integration: true, beforeEach() { useNativeDOMHelpers(_useNativeDOMHelpers); - // if (!_useNativeDOMHelpers) { - this.adapter = new IntegrationAdapter(IntegrationExecutionContext); - // } + + this.adapter = new IntegrationAdapter(IntegrationExecutionContext); }, afterEach() { useNativeDOMHelpers(false); From f158437de3cbd1fc4c8bff41fd6e353eeb361273 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Mon, 9 Oct 2017 22:00:33 +0300 Subject: [PATCH 16/20] Delete global jQuery reference --- vendor/shims/-jquery.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vendor/shims/-jquery.js b/vendor/shims/-jquery.js index 9c7b172c..debbbeca 100644 --- a/vendor/shims/-jquery.js +++ b/vendor/shims/-jquery.js @@ -1,5 +1,12 @@ +// `-jquery` vendor shim supposed to isolate +// `ember-cli-page-object`'s `jquery` from the rest of application. +// +// It's important to include this shim right after our own `jquery` is included. +// This way we ensure nothing catches our own `jquery` +// and we can safely dispose it from the global `window`. (function() { var jq = self['$'].noConflict(); + delete self['jQuery']; function vendorModule() { 'use strict'; From 56efa221dcb6111daa845c93c004619dc648b8d9 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sun, 15 Oct 2017 15:30:26 +0300 Subject: [PATCH 17/20] properly handle ember-native-dom keyEvent --- .../-private/execution_context/native-dom-context.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/addon/-private/execution_context/native-dom-context.js b/addon/-private/execution_context/native-dom-context.js index ea535f14..03419469 100644 --- a/addon/-private/execution_context/native-dom-context.js +++ b/addon/-private/execution_context/native-dom-context.js @@ -2,7 +2,8 @@ import $ from '-jquery'; import { click, - triggerEvent + triggerEvent, + keyEvent } from 'ember-native-dom-helpers'; import { @@ -18,6 +19,8 @@ import { throwBetterError } from '../better-errors'; +const KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup']; + export default function ExecutionContext(pageObjectNode, testContext) { this.pageObjectNode = pageObjectNode; this.testContext = testContext; @@ -75,7 +78,11 @@ ExecutionContext.prototype = { delete eventOptions.keyCode; } - triggerEvent(element, eventName, eventOptions); + if (KEYBOARD_EVENT_TYPES.indexOf(eventName) > -1) { + keyEvent(element, eventName, eventOptions.key, eventOptions); + } else { + triggerEvent(element, eventName, eventOptions); + } }, assertElementExists(selector, options) { From 1eaafc7f8f80b16e7d7d5a8e14c6101d07a7b449 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sun, 15 Oct 2017 16:13:00 +0300 Subject: [PATCH 18/20] do nothing on visit in integration contexts --- addon/-private/execution_context/integration-native-dom.js | 2 ++ addon/-private/execution_context/integration.js | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/-private/execution_context/integration-native-dom.js b/addon/-private/execution_context/integration-native-dom.js index 879f79a0..9aabdce1 100644 --- a/addon/-private/execution_context/integration-native-dom.js +++ b/addon/-private/execution_context/integration-native-dom.js @@ -9,6 +9,8 @@ export default function IntegrationExecutionContext(pageObjectNode, testContext) IntegrationExecutionContext.prototype = Object.create(ExecutionContext.prototype); +IntegrationExecutionContext.prototype.visit = function() {}; + IntegrationExecutionContext.prototype.runAsync = function(cb) { run(() => { cb(this); diff --git a/addon/-private/execution_context/integration.js b/addon/-private/execution_context/integration.js index f9ee8648..5a21cb79 100644 --- a/addon/-private/execution_context/integration.js +++ b/addon/-private/execution_context/integration.js @@ -32,9 +32,7 @@ IntegrationExecutionContext.prototype = { return this.pageObjectNode; }, - visit() { - throw new Error('visit is not supported in integration mode'); - }, + visit() {}, click(selector, container) { this.$(selector, container).click(); From 11dded4fc1b6050af4b6ae0d062fce404d95bdfe Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Wed, 25 Oct 2017 22:45:25 +0300 Subject: [PATCH 19/20] add support for multiple fillIn --- .../execution_context/native-dom-context.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/addon/-private/execution_context/native-dom-context.js b/addon/-private/execution_context/native-dom-context.js index 03419469..49388620 100644 --- a/addon/-private/execution_context/native-dom-context.js +++ b/addon/-private/execution_context/native-dom-context.js @@ -41,16 +41,18 @@ ExecutionContext.prototype = { }, fillIn(selector, container, options, content) { - let el = this.$(selector, container)[0]; + let elements = this.$(selector, container).toArray(); - fillElement(el, content, { - selector, - pageObjectNode: this.pageObjectNode, - pageObjectKey: options.pageObjectKey - }); + elements.forEach((el) => { + fillElement(el, content, { + selector, + pageObjectNode: this.pageObjectNode, + pageObjectKey: options.pageObjectKey + }); - triggerEvent(el, 'input'); - triggerEvent(el, 'change'); + triggerEvent(el, 'input'); + triggerEvent(el, 'change'); + }); }, $(selector, container) { From 253f2d1d34cbe4e44b17f9463eef34013a00220e Mon Sep 17 00:00:00 2001 From: Ruslan Grabovoy Date: Sun, 5 Nov 2017 16:41:07 +0200 Subject: [PATCH 20/20] split jquery shim into global and amd steps --- index.js | 27 +++++++++++++++++++++++++-- vendor/shims/-jquery.js | 18 ------------------ vendor/shims/ecpo-jquery-global.js | 9 +++++++++ vendor/shims/ecpo-jquery.js | 13 +++++++++++++ 4 files changed, 47 insertions(+), 20 deletions(-) delete mode 100644 vendor/shims/-jquery.js create mode 100644 vendor/shims/ecpo-jquery-global.js create mode 100644 vendor/shims/ecpo-jquery.js diff --git a/index.js b/index.js index c4e8b750..5a8ddd8d 100644 --- a/index.js +++ b/index.js @@ -31,13 +31,36 @@ module.exports = { this.app = app; if (this._shouldIncludeFiles()) { - this.import('vendor/shims/-jquery.js', { prepend: true }); - this.import('vendor/ecpo-jquery/dist/jquery.js', { prepend: true }); + this.importJquery(); } this._super.included.apply(this, arguments); }, + /* + * Import an amd '-jquery' shim which is used by ember-cli-page-object internally + * + * We don't want ember-cli-page-object's jquery ocassionaly leak into a real application. + * The following combo of shims supposed to isolate `ember-cli-page-object`'s `jquery` + * from the rest of application and expose internal version via amd module. + */ + importJquery: function() { + // jquery itself is included in the very beggining of vendor.js. + // At this point we don't have `define()` defined so we can't create an amd shim here. + // + // However we have to store reference to jquery and dispose it from the window + // in order to prevent its leakage to the application. + this.import('vendor/shims/ecpo-jquery-global.js', { + prepend: true + }); + this.import('vendor/ecpo-jquery/dist/jquery.js', { + prepend: true + }); + + // finally define an amd shim for our internal jquery version + this.import('vendor/shims/ecpo-jquery.js'); + }, + treeFor: function(/*name*/) { if (!this._shouldIncludeFiles()) { return; diff --git a/vendor/shims/-jquery.js b/vendor/shims/-jquery.js deleted file mode 100644 index debbbeca..00000000 --- a/vendor/shims/-jquery.js +++ /dev/null @@ -1,18 +0,0 @@ -// `-jquery` vendor shim supposed to isolate -// `ember-cli-page-object`'s `jquery` from the rest of application. -// -// It's important to include this shim right after our own `jquery` is included. -// This way we ensure nothing catches our own `jquery` -// and we can safely dispose it from the global `window`. -(function() { - var jq = self['$'].noConflict(); - delete self['jQuery']; - - function vendorModule() { - 'use strict'; - - return { 'default': jq }; - } - - define('-jquery', [], vendorModule); -})(); diff --git a/vendor/shims/ecpo-jquery-global.js b/vendor/shims/ecpo-jquery-global.js new file mode 100644 index 00000000..eed60bae --- /dev/null +++ b/vendor/shims/ecpo-jquery-global.js @@ -0,0 +1,9 @@ +// Temporary store our own jquery version in a global variable for the further definition of amd module. +// We can't define amd module here cause we don't have `define()` in the very beginning of vendor.js +// +// It's important to include this shim right after our own `jquery` is included. +// This way we ensure nothing catches our own `jquery` and we can safely dispose it from the global `window`. +(function() { + window.__ecpoJQuery__ = self['$'].noConflict(); + delete self['jQuery']; +})(); diff --git a/vendor/shims/ecpo-jquery.js b/vendor/shims/ecpo-jquery.js new file mode 100644 index 00000000..62d0744b --- /dev/null +++ b/vendor/shims/ecpo-jquery.js @@ -0,0 +1,13 @@ +// Define an amd '-jquery' shim which is used by ember-cli-page-object internally +(function() { + var jquery = window.__ecpoJQuery__; + delete window.__ecpoJQuery__; + + function vendorModule() { + 'use strict'; + + return { 'default': jquery }; + } + + define('-jquery', [], vendorModule); +})();