Skip to content

Commit

Permalink
Merge pull request #339 from ro0gr/native-dom
Browse files Browse the repository at this point in the history
Add native-dom mode
  • Loading branch information
san650 authored Nov 15, 2017
2 parents 8030784 + fdadc85 commit c2b3fc0
Show file tree
Hide file tree
Showing 14 changed files with 332 additions and 40 deletions.
25 changes: 25 additions & 0 deletions addon/-private/execution_context/acceptance-native-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import ExecutionContext from './native-events-context';
import wait from 'ember-test-helpers/wait';

import {
visit
} from 'ember-native-dom-helpers';

export default function AcceptanceNativeEventsExecutionContext(pageObjectNode) {
ExecutionContext.call(this, pageObjectNode);
}

AcceptanceNativeEventsExecutionContext.prototype = Object.create(ExecutionContext.prototype);

AcceptanceNativeEventsExecutionContext.prototype.visit = function() {
return visit(...arguments);
};

AcceptanceNativeEventsExecutionContext.prototype.runAsync = function(cb) {
(window.wait || wait)().then(() => {
cb(this);
});

return this.pageObjectNode;
};

6 changes: 5 additions & 1 deletion addon/-private/execution_context/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
throwBetterError
} from '../better-errors';

import $ from '-jquery';

/**
* @private
*
Expand All @@ -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"]')) {
Expand Down
21 changes: 21 additions & 0 deletions addon/-private/execution_context/integration-native-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import ExecutionContext from './native-events-context';

import Ember from 'ember';
const { run } = Ember;

export default function IntegrationNativeEventsExecutionContext(pageObjectNode, testContext) {
ExecutionContext.call(this, pageObjectNode, testContext);
}

IntegrationNativeEventsExecutionContext.prototype = Object.create(ExecutionContext.prototype);

IntegrationNativeEventsExecutionContext.prototype.visit = function() {};

IntegrationNativeEventsExecutionContext.prototype.runAsync = function(cb) {
run(() => {
cb(this);
});

return this.pageObjectNode;
};

3 changes: 1 addition & 2 deletions addon/-private/execution_context/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ IntegrationExecutionContext.prototype = {
return this.pageObjectNode;
},

// Do nothing in integration test
visit: $.noop,
visit() {},

click(selector, container) {
this.$(selector, container).click();
Expand Down
138 changes: 138 additions & 0 deletions addon/-private/execution_context/native-events-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import $ from '-jquery';

import {
click,
triggerEvent,
keyEvent
} from 'ember-native-dom-helpers';

import {
guardMultiple,
buildSelector,
findClosestValue
} from '../helpers';
import {
fillElement
} from './helpers';
import {
ELEMENT_NOT_FOUND,
throwBetterError
} from '../better-errors';

const KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup'];

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');
},

click(selector, container) {
const el = this.$(selector, container)[0];
click(el);
},

fillIn(selector, container, options, content) {
let elements = this.$(selector, container).toArray();

elements.forEach((el) => {
fillElement(el, content, {
selector,
pageObjectNode: this.pageObjectNode,
pageObjectKey: options.pageObjectKey
});

triggerEvent(el, 'input');
triggerEvent(el, 'change');
});
},

$(selector, container) {
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';

return $(selector, testsContainer);
}
},

triggerEvent(selector, container, eventName, eventOptions) {
const element = this.$(selector, container)[0];

// `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;
}

if (KEYBOARD_EVENT_TYPES.indexOf(eventName) > -1) {
keyEvent(element, eventName, eventOptions.key, eventOptions);
} else {
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;
}
};

4 changes: 3 additions & 1 deletion addon/-private/helpers.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
3 changes: 1 addition & 2 deletions addon/-private/properties/visitable.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
19 changes: 18 additions & 1 deletion addon/extend.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
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 IntegrationNativeEventsContext from './-private/execution_context/integration-native-events';
import AcceptanceNativeEventsContext from './-private/execution_context/acceptance-native-events';
import IntegrationEmberContext from './-private/execution_context/integration';
import AcceptanceEmberContext from './-private/execution_context/acceptance';

function useNativeEvents(flag = true) {
if (flag) {
registerExecutionContext('integration', IntegrationNativeEventsContext);
registerExecutionContext('acceptance', AcceptanceNativeEventsContext);
} else {
registerExecutionContext('integration', IntegrationEmberContext);
registerExecutionContext('acceptance', AcceptanceEmberContext);
}
}

export { registerExecutionContext, useNativeEvents };
35 changes: 35 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,51 @@ module.exports = {
enabled: this._shouldIncludeFiles(),
import: ['index.js']
};
},
jquery: function() {
return {
enabled: this._shouldIncludeFiles(),
vendor: ['dist/jquery.js'],
destDir: 'ecpo-jquery'
}
}
}
},

included: function() {
this.app = this._findHost();

if (this._shouldIncludeFiles()) {
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;
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@
"ember-cli-babel": "^6.6.0",
"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",
"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"
},
"devDependencies": {
Expand Down
34 changes: 23 additions & 11 deletions tests/helpers/module-for-acceptance.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@ import { module } from 'qunit';
import { resolve } from 'rsvp';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';
import { useNativeEvents } from 'ember-cli-page-object/extend';

export default function(name, options = {}) {
module(name, {
beforeEach() {
this.application = startApp();
[false, true].forEach(_useNativeEvents => {
let moduleName = name;
if (_useNativeEvents) {
moduleName += ' [native-events]';
}

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 resolve(afterEach).then(() => destroyApp(this.application));
}
useNativeEvents(_useNativeEvents);

if (options.beforeEach) {
return options.beforeEach.apply(this, arguments);
}
},

afterEach() {
useNativeEvents(false);

let afterEach = options.afterEach && options.afterEach.apply(this, arguments);
return resolve(afterEach).then(() => destroyApp(this.application));
}
});
});
}
Loading

0 comments on commit c2b3fc0

Please sign in to comment.