Skip to content

Commit

Permalink
support composition of page objects within definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
mistahenry committed Dec 10, 2018
1 parent 7fa5913 commit 15f9f51
Show file tree
Hide file tree
Showing 5 changed files with 489 additions and 34 deletions.
21 changes: 21 additions & 0 deletions addon-test-support/-private/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,24 @@ export function getProperty(object, pathToProp) {

return typeof value === 'function' ? value.bind(propOwner) : value;
}

export function isPageObject(property){
if(property && typeof(property) === 'object'){
let meta = Ceibo.meta(property);
return (meta && meta.__poDef__)
} else{
return false;
}
}

export function getPageObjectDefinition(node){
if(!isPageObject(node)){
throw new Error('cannot get the page object definition from a node that is not a page object');
}else{
return Ceibo.meta(node).__poDef__;
}
}

export function storePageObjectDefinition(node, definition){
Ceibo.meta(node).__poDef__ = definition;
}
40 changes: 27 additions & 13 deletions addon-test-support/create.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Ceibo from 'ceibo';
import { render, setContext, removeContext } from './-private/context';
import { assign } from './-private/helpers';
import { assign, getPageObjectDefinition, isPageObject, storePageObjectDefinition } from './-private/helpers';
import { visitable } from './properties/visitable';
import dsl from './-private/dsl';

Expand Down Expand Up @@ -44,16 +44,29 @@ import dsl from './-private/dsl';

// This builder builds the primary tree
function buildObject(node, blueprintKey, blueprint, defaultBuilder) {
blueprint = assign(assign({}, dsl), blueprint);
let definition;

return defaultBuilder(node, blueprintKey, blueprint, defaultBuilder);
}
// to allow page objects to exist in definitions, we store the definition that
// created the page object, allowing us to substitute a page object with its
// definition during creation
if (isPageObject(blueprint)) {
definition = getPageObjectDefinition(blueprint);
} else {
definition = blueprint;
}
let blueprintToStore = assign({}, definition);
//the _chainedTree is an implementation detail that shouldn't make it into the stored
if(blueprintToStore._chainedTree){
delete blueprintToStore._chainedTree;
}
blueprint = assign({}, dsl, definition);

// This builder builds the chained tree
function buildChainObject(node, blueprintKey, blueprint, defaultBuilder) {
blueprint = assign({}, blueprint);
const [ instance, blueprintToApply ] = defaultBuilder(node, blueprintKey, blueprint, defaultBuilder);

return buildObject(node, blueprintKey, blueprint, defaultBuilder);
// persist definition once we have an instance
storePageObjectDefinition(instance, blueprintToStore);

return [ instance, blueprintToApply ];
}

/**
Expand Down Expand Up @@ -156,18 +169,19 @@ export function create(definitionOrUrl, definitionOrOptions, optionsOrNothing) {
options = definitionOrOptions || {};
}

definition = assign({}, definition);
let { context } = definition;
// in the instance where the definition is a page object, we must use the stored definition directly
// or else we will fire off the Ceibo created getters which will error
definition = assign({}, isPageObject(definition) ? getPageObjectDefinition(definition) : definition);
delete definition.context;

if (url) {
definition.visit = visitable(url);
}

let { context } = definition;
delete definition.context;

// Build the chained tree
let chainedBuilder = {
object: buildChainObject
object: buildObject
};
let chainedTree = Ceibo.create(definition, assign({ builder: chainedBuilder }, options));

Expand Down
44 changes: 23 additions & 21 deletions addon-test-support/properties/collection/main.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* global Symbol */
import { A } from '@ember/array';
import { buildSelector, assign } from '../../-private/helpers';
import { buildSelector, assign, isPageObject, getPageObjectDefinition } from '../../-private/helpers';
import { create } from '../../create';
import { count } from '../count';
import Ceibo from 'ceibo';
Expand Down Expand Up @@ -91,35 +91,37 @@ if (typeof (Symbol) !== 'undefined' && Symbol.iterator) {
}
}

function proxyIt(instance) {
return new window.Proxy(instance, {
get: function(target, name) {
if (typeof(name) === 'number' || typeof(name) === 'string') {
let index = parseInt(name, 10);
function proxyIfSupported(instance) {
if (window.Proxy) {
return new window.Proxy(instance, {
get: function (target, name) {
if (typeof (name) === 'number' || typeof (name) === 'string') {
let index = parseInt(name, 10);

if (!isNaN(index)) {
return target.objectAt(index);
if (!isNaN(index)) {
return target.objectAt(index);
}
}
}

return target[name];
}
});
return target[name];
}
});
} else {
return instance;
}
}

export function collection(scope, definition) {

if(isPageObject(definition)){
//extract the stored definition from the page object
definition = getPageObjectDefinition(definition);
}
let descriptor = {
isDescriptor: true,

setup(node, key) {
// Set the value on the descriptor so that it will be picked up and applied by Ceibo.
// This does mutate the descriptor, but because `setup` is always called before the
// value is assigned we are guaranteed to get a new, unique Collection instance each time.
descriptor.value = new Collection(scope, definition, node, key);

if (window.Proxy) {
descriptor.value = proxyIt(descriptor.value);
}
get(key) {
return proxyIfSupported(new Collection(scope, definition, this, key));
}
};

Expand Down
60 changes: 60 additions & 0 deletions tests/acceptance/composition-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { test } from 'qunit';
import moduleForAcceptance from '../helpers/module-for-acceptance';

import {
create,
collection,
visitable,
text,
clickable,
value,
fillable,
clickOnText
} from 'ember-cli-page-object';

let keyboard = create({
scope: '.keyboard',
numbers: collection(".numbers button", {
text: text(),
click: clickable()
}),
operators: collection(".operators button", {
text: text()
}),
clickOn: clickOnText('.numbers'),
sum: clickable('button', { scope: '.operators', at: 0 }),
equal: clickable('button', { scope: '.operators', at: 2 })
});

let screenPage = create({
value: value('.screen input'),
fillValue: fillable('.screen input')
});
let page = create({
visit: visitable('/calculator'),
keys: keyboard,
screen: screenPage
});

moduleForAcceptance('Acceptance | composition');

test('allows compose', async function(assert) {
await page
.visit()
.keys
.clickOn('1')
.clickOn('2')
.sum();
//click 3
await page.keys.numbers.objectAt(2).click()
//click =
await page.keys.operators.objectAt(3).click();
assert.equal(page.screen.value, '15');

await page.screen.fillValue('45')
//click 6
await page.keys.numbers.objectAt(5).click();

assert.equal(page.screen.value, '456');
});

Loading

0 comments on commit 15f9f51

Please sign in to comment.