All files / test-support create.js

0% Statements 0/0
0% Branches 0/0
0% Functions 0/0
0% Lines 0/0

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200                                                                                                                                                                                                                                                                                                                                                                                                               
import Ceibo from 'ceibo';
import { render, setContext, removeContext } from './context';
import { assign } from './helpers';
import { visitable } from './properties/visitable';
import dsl from './dsl';
 
//
// When running RFC268 tests, we have to play some tricks to support chaining.
// RFC268 helpers don't wait for things to settle by defaut, but return a
// promise that will resolve when everything settles. So this means
//
// page.clickOn('.foo');
// page.clickOn('.bar');
//
// will not wait after either of the clicks, whereas
//
// await page.clickOn('.foo');
// await page.clickOn('.bar');
//
// will wait after each of them. However, to preserve chaining behavior,
//
// page
//   .clickOn('.foo')
//   .clickOn('.bar');
//
// would need to wait between the clicks. However, if `clickOn()` just returned
// `page` this would be impossible because then it would be exactly the same as
// the first example, which must not wait between clicks.
//
// So the solution is to return something other than `page` from,
// `page.clickOn('.foo')`, but something that behaves just like `page` except
// waits for things to settle before invoking any async methods.
//
// To accomplish this, when building our Ceibo tree, we build a mirror copy of
// it (the "chained tree"). Anytime a chainable method is invoked, instead of
// returning the node whose method was invoked, we can return its mirror node in
// the chained tree. Then, anytime an async method is invoked on that node
// (meaning we are in a chaining scenario), the execution context can recognize
// it as a chained node and wait before invoking the target method.
//
 
// See https://github.com/san650/ceibo#examples for more info on how Ceibo
// builders work.
 
// This builder builds the primary tree
function buildObject(node, blueprintKey, blueprint, defaultBuilder) {
  blueprint = assign(assign({}, dsl), blueprint);
 
  return defaultBuilder(node, blueprintKey, blueprint, defaultBuilder);
}
 
// This builder builds the chained tree
function buildChainObject(node, blueprintKey, blueprint, defaultBuilder) {
  blueprint = assign({}, blueprint);
 
  return buildObject(node, blueprintKey, blueprint, defaultBuilder);
}
 
/**
 * Creates a new PageObject.
 *
 * By default, the resulting PageObject will respond to:
 *
 * - **Actions**: click, clickOn, fillIn, select
 * - **Predicates**: contains, isHidden, isPresent, isVisible
 * - **Queries**: text
 *
 * `definition` can include a key `context`, which is an
 * optional integration test `this` context.
 *
 * If a context is passed, it is used by actions, queries, etc.,
 * as the `this` in `this.$()`.
 *
 * If no context is passed, the global Ember acceptence test
 * helpers are used.
 *
 * @example
 *
 * // <div class="title">My title</div>
 *
 * import PageObject, { text } from 'ember-cli-page-object';
 *
 * const page = PageObject.create({
 *   title: text('.title')
 * });
 *
 * assert.equal(page.title, 'My title');
 *
 * @example
 *
 * // <div id="my-page">
 * //   My super text
 * //   <button>Press Me</button>
 * // </div>
 *
 * const page = PageObject.create({
 *   scope: '#my-page'
 * });
 *
 * assert.equal(page.text, 'My super text');
 * assert.ok(page.contains('super'));
 * assert.ok(page.isPresent);
 * assert.ok(page.isVisible);
 * assert.notOk(page.isHidden);
 * assert.equal(page.value, 'my input value');
 *
 * // clicks div#my-page
 * page.click();
 *
 * // clicks button
 * page.clickOn('Press Me');
 *
 * // fills an input
 * page.fillIn('name', 'John Doe');
 *
 * // selects an option
 * page.select('country', 'Uruguay');
 *
 * @example Defining path
 *
 * const usersPage = PageObject.create('/users');
 *
 * // visits user page
 * usersPage.visit();
 *
 * const userTasksPage = PageObject.create('/users/tasks', {
 *  tasks: collection({
 *    itemScope: '.tasks li',
 *    item: {}
 *  });
 * });
 *
 * // get user's tasks
 * userTasksPage.visit();
 * userTasksPage.tasks().count
 *
 * @public
 *
 * @param {Object} definition - PageObject definition
 * @param {Object} [definition.context] - A test's `this` context
 * @param {Object} options - [private] Ceibo options. Do not use!
 * @return {PageObject}
 */
export function create(definitionOrUrl, definitionOrOptions, optionsOrNothing) {
  let definition;
  let url;
  let options;
 
  if (typeof (definitionOrUrl) === 'string') {
    url = definitionOrUrl;
    definition = definitionOrOptions || {};
    options = optionsOrNothing || {};
  } else {
    url = false;
    definition = definitionOrUrl;
    options = definitionOrOptions || {};
  }
 
  definition = assign({}, definition);
 
  if (url) {
    definition.visit = visitable(url);
  }
 
  let { context } = definition;
  delete definition.context;
 
  // Build the chained tree
  let chainedBuilder = {
    object: buildChainObject
  };
  let chainedTree = Ceibo.create(definition, assign({ builder: chainedBuilder }, options));
 
  // Attach it to the root in the definition of the primary tree
  definition._chainedTree = {
    isDescriptor: true,
 
    get() {
      return chainedTree;
    }
  };
 
  // Build the primary tree
  let builder = {
    object: buildObject
  };
 
  let page = Ceibo.create(definition, assign({ builder }, options));
 
  if (page) {
    page.render = render;
    page.setContext = setContext;
    page.removeContext = removeContext;
 
    page.setContext(context);
  }
 
  return page;
}