Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[experiment] Explore user actions #460

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3ad2fcd
cache execution_context
ro0gr Nov 11, 2019
fdf2f0d
Make execution context aware of the current action promise
ro0gr Nov 12, 2019
46bf062
Allow passing of an Error instance to the throwBetterError
Feb 17, 2019
7ea4ef1
[Needs Tests] Implement action utility
Jan 25, 2019
d7e660e
Add negative test case for fillable with clue
Feb 24, 2019
ab0879a
ExecutionContext: fillable
ro0gr Oct 27, 2019
0305adc
Restore fillable
ro0gr Nov 15, 2019
2ca4bab
ExectuionContext: click
ro0gr Oct 27, 2019
883b570
fixup! clickOnText
ro0gr Nov 14, 2019
2dac5b4
ExecutionContext: triggerEvent
ro0gr Oct 27, 2019
0380e26
ExecutionContext: remove assertElementExists
ro0gr Oct 27, 2019
22290ba
ExecurionContext: focus/blur
ro0gr Oct 28, 2019
b7194ee
move visitable to run
ro0gr Oct 28, 2019
668b902
remove code unused anymore
ro0gr Oct 29, 2019
23eb286
ExecutionContext: Extract find logic
ro0gr Nov 6, 2019
a393966
read execution context from chainable
ro0gr Nov 14, 2019
89282b1
maintain execution context only inside run
ro0gr Nov 15, 2019
9926b18
make sure run passes chained node
ro0gr Nov 15, 2019
aa8ada4
fallback to executionContext key
ro0gr Nov 15, 2019
bc33875
add sync invocation errors
ro0gr Nov 16, 2019
b18258f
imporove tests
ro0gr Nov 16, 2019
124a23d
Move visitable to action
ro0gr Nov 16, 2019
3947a21
Make sure a proper context applied before DOM helper
ro0gr Nov 16, 2019
310c09b
revert duplicating integration context in a chainedNode
ro0gr Nov 16, 2019
92c6492
cleanup RFC268 actions test waiters
ro0gr Nov 16, 2019
59e7934
remove ensuring if chainable in then
ro0gr Nov 16, 2019
54d5677
revert execution_context arg rename
ro0gr Nov 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions addon-test-support/-private/better-errors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import EmberError from '@ember/error';
import Ceibo from 'ceibo';
import { getRoot } from './helpers';

export const ELEMENT_NOT_FOUND = 'Element not found.';

Expand All @@ -8,25 +9,35 @@ export const ELEMENT_NOT_FOUND = 'Element not found.';
*
* @param {Ceibo} node PageObject node containing the property that triggered the error
* @param {string} key Key of PageObject property tht triggered the error
* @param {string} msg Error message
* @param {Error|string} err Error or error text
* @param {Object} options
* @param {string} options.selector Selector of element targeted by PageObject property
* @return {Ember.Error}
*/
export function throwBetterError(node, key, msg, { selector } = {}) {
let path = [key];
export function throwBetterError(node, key, err, { selector } = {}) {
const executionContext = getRoot(node).__execution_context__;
const _key = key || (executionContext && executionContext.key);

let fullErrorMessage = typeof err === Error ? err.message : err.toString();

let path = [];
let current;

for (current = node; current; current = Ceibo.parent(current)) {
path.unshift(Ceibo.meta(current).key);
}

path[0] = 'page';
if (_key && _key.trim().length > 0) {
path.push(_key);
}

let fullErrorMessage = `${msg}\n\nPageObject: '${path.join('.')}'`;
if (path.length > 0) {
fullErrorMessage += `\n\nPageObject: '${path.join('.')}'`;
}

if (selector) {
fullErrorMessage = `${fullErrorMessage}\n Selector: '${selector}'`;
if (typeof selector === 'string' && selector.trim().length > 0) {
fullErrorMessage += `\n Selector: '${selector}'`;
}

console.error(fullErrorMessage);
Expand Down
11 changes: 6 additions & 5 deletions addon-test-support/-private/dsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import { isPresent } from '../properties/is-present';
import { isVisible } from '../properties/is-visible';
import { text } from '../properties/text';
import { value } from '../properties/value';

import { getRoot } from './helpers';

const thenDescriptor = {
isDescriptor: true,
value() {
const root = getRoot(this);
const chainedRoot = root._chainedTree || root;
get() {
return function() {
// this is a chainable node instance
const { _promise } = getRoot(this).__execution_context__;

return chainedRoot._promise.then(...arguments);
return _promise.then(...arguments);
}
}
};

Expand Down
116 changes: 24 additions & 92 deletions addon-test-support/-private/execution_context/acceptance.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import { run } from '../action';
import {
guardMultiple,
buildSelector,
findClosestValue
} from '../helpers';
import run from '../run';
import $ from '-jquery';
import {
fillElement,
assertFocusable
} from './helpers';
import {
ELEMENT_NOT_FOUND,
throwBetterError
} from '../better-errors';

export default function AcceptanceExecutionContext(pageObjectNode) {
this.pageObjectNode = pageObjectNode;
}

AcceptanceExecutionContext.prototype = {
get testContainer() {
// @todo: fix usage of private `_element`
return this.testContext ?
this.testContext._element :
'#ember-testing';
},

andThen(cb) {
return window.wait().then(() => {
cb(this);
Expand All @@ -33,103 +32,36 @@ AcceptanceExecutionContext.prototype = {
visit(path);
},

click(selector, container) {
click(element) {
/* global click */
click(selector, container);
click(element);
},

fillIn(selector, container, options, content) {
let $selection = find(selector, container || findClosestValue(this.pageObjectNode, 'testContainer'));

fillIn(element, content) {
/* global focus */
focus($selection);
focus(element);

fillElement($selection, content, {
selector,
pageObjectNode: this.pageObjectNode,
pageObjectKey: options.pageObjectKey
});
fillElement(element, content);

/* global triggerEvent */
triggerEvent(selector, container, 'input');
triggerEvent(selector, container, 'change');
triggerEvent(element, 'input');
triggerEvent(element, 'change');
},

triggerEvent(selector, container, options, eventName, eventOptions) {
triggerEvent(element, eventName, eventOptions) {
/* global triggerEvent */
triggerEvent(selector, container, eventName, eventOptions);
triggerEvent(element, eventName, eventOptions);
},

focus(selector, options) {
let $selection = this.findWithAssert(selector, options);

assertFocusable($selection[0], {
selector,
pageObjectNode: this.pageObjectNode,
pageObjectKey: options.pageObjectKey
});

$selection.focus();
},

blur(selector, options) {
let $selection = this.findWithAssert(selector, options);

assertFocusable($selection[0], {
selector,
pageObjectNode: this.pageObjectNode,
pageObjectKey: options.pageObjectKey
});
focus(element) {
assertFocusable(element);

$selection.blur();
$(element).focus();
},

assertElementExists(selector, options) {
/* global find */
let result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'));
blur(element) {
assertFocusable(element);

if (result.length === 0) {
throwBetterError(
this.pageObjectNode,
options.pageObjectKey,
ELEMENT_NOT_FOUND,
{ selector }
);
}
$(element).blur();
},

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;
}
};
24 changes: 4 additions & 20 deletions addon-test-support/-private/execution_context/helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import {
throwBetterError
} from '../better-errors';

import $ from '-jquery';

/**
Expand All @@ -19,19 +15,13 @@ import $ from '-jquery';
*
* @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) {
const $selection = $(selection);

if ($selection.is('[contenteditable][contenteditable!="false"]')) {
$selection.html(content);
} else if ($selection.is('[contenteditable="false"]')) {
throwBetterError(
pageObjectNode,
pageObjectKey,
'Element cannot be filled because it has `contenteditable="false"`.', {
selector
}
);
throw new Error('Element cannot be filled because it has `contenteditable="false"`.');
} else {
$selection.val(content);
}
Expand All @@ -44,7 +34,7 @@ export function fillElement(selection, content, { selector, pageObjectNode, page
*
* @param {Element} element - the element to check
*/
export function assertFocusable(element, { selector, pageObjectNode, pageObjectKey }) {
export function assertFocusable(element) {
let $element = $(element);

let error;
Expand All @@ -60,12 +50,6 @@ export function assertFocusable(element, { selector, pageObjectNode, pageObjectK
}

if (error) {
throwBetterError(
pageObjectNode,
pageObjectKey,
`Element is not focusable because it is ${error}`, {
selector
}
);
throw new Error(`Element is not focusable because it is ${error}`);
}
}
Loading