Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ember-cli-page-object

Package Overview
Dependencies
Maintainers
1
Versions
85
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ember-cli-page-object - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

test-support/page-object/attribute.js

63

DOCUMENTATION.md

@@ -18,2 +18,3 @@ # Page Object

* [`.clickable`](#clickable)
* [`.clickOnText`](#clickontext)
* [`.fillable`](#fillable)

@@ -63,9 +64,9 @@ * [`.visitable`](#visitable)

```
```js
var page = PO.build({
title: PO.text('.title)
title: PO.text('.title')
});
```
The following is a comprehensive documentation of the available PO attribute
The following is a comprehensive documentation of the available `PO` attribute
helpers.

@@ -308,2 +309,41 @@

### `.clickOnText`
Creates an action to click on an element by text. The text is case sensitive.
Attribute signature
```js
PO.clickOnText(selector, [, scope: ''])
```
Examples
```html
<button class="btn">Create</button>
<button class="btn">Cancel</button>
```
```js
var page = PO.build({
click: clickOnText('.btn')
});
page.click("Create");
andThen(function() {
// ...
});
page.click("Cancel");
andThen(function() {
// ...
});
```
> A string of text to look for. It's case sensitive.
> The text must have matching case to be selected.
> gwill match elements with the desired text block:
### `.fillable`

@@ -514,1 +554,18 @@

```
You can define components implicity by creating a plain object with attributes on it
```js
var page = PO.build({
visit: PO.visitable('/user/create'),
title: PO.text('h1'),
form: {
firstName: PO.fillable('#firstName'),
lastName: PO.fillable('#lastName'),
submit: PO.clickable('button')
}
});
```
Note that if the plain object doesn't have attributes defined, the object is returned as is.

2

package.json
{
"name": "ember-cli-page-object",
"version": "0.1.0",
"version": "0.2.0",
"description": "Helper functions to implement the Page Object pattern in your tests",

@@ -5,0 +5,0 @@ "directories": {

# Ember Page Objects
Represent the screens of your web app as a series of objects.
Represent the screens of your web app as a series of objects. This ember-cli
addon ease the construction of these objects on your acceptance tests.
## References
## What is a Page Object?
* [Page Objects](https://code.google.com/p/selenium/wiki/PageObjects) - Selenium wiki
* [PageObject](http://martinfowler.com/bliki/PageObject.html) - Martin Fowler
An excerpt from the Selenium Wiki
> Within your web app's UI there are areas that your tests interact with. A Page Object simply models these as objects within the test code. This reduces the amount of duplicated code and means that if the UI changes, the fix need only be applied in one place.
The pattern was first introduced by the Selenium
You can find more information about this design pattern here:
* [Page Objects - Selenium wiki](https://code.google.com/p/selenium/wiki/PageObjects)
* [PageObject - Martin Fowler](http://martinfowler.com/bliki/PageObject.html)
## Usage
First add the npm package to your ember-cli project
Install the npm package on your ember-cli project

@@ -18,2 +24,4 @@ ```sh

then import the page-object helper
```js

@@ -24,26 +32,12 @@ import PO from '../page-object';

The previous example assumes that your test file is one level deep under
`tests/` folder. i.e. `tests/unit/my-unit-test.js`.
`tests/` folder. i.e. `tests/acceptance/my-acceptance-test.js`.
Then you can start building your page objects as follows:
```js
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from '../helpers/start-app';
import PO from '../page-object';
var application;
module('An Integration test', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
var login = PO.build({
visit: PO.visitable('/login'),
userName: PO.fillable('#username'),
password: PO.fillable('#password'),
submit: PO.clickable('#login'),
visit: PO.visitable('/login'),
userName: PO.fillable('#username'),
password: PO.fillable('#password'),
submit: PO.clickable('#login'),
errorMessage: PO.text('.message')

@@ -65,3 +59,3 @@ });

Support for tables and collections
Built-in support for defining tables and collections:

@@ -82,18 +76,2 @@ ```html

```js
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from '../helpers/start-app';
import PO from '../page-object';
var application;
module('Users', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
var page = PO.build({

@@ -107,3 +85,3 @@ visit: PO.visitable('/users'),

firstName: PO.text('td:nth-of-type(1)'),
lastName: PO.text('td:nth-of-type(2)')
lastName: PO.text('td:nth-of-type(2)')
}

@@ -126,4 +104,23 @@ })

Check [DOCUMENTATION](./DOCUMENTATION.md) for more information.
You can use ES6 destructuring to declutter even more your page definition:
```js
var { visitable, collection, text } = PO;
var page = PO.build({
visit: visitable('/users'),
users: collection({
itemScope: '#users tr',
item: {
firstName: text('td:nth-of-type(1)'),
lastName: text('td:nth-of-type(2)')
}
})
});
```
Check the [DOCUMENTATION](./DOCUMENTATION.md) for more information.
## Development

@@ -130,0 +127,0 @@

@@ -1,34 +0,41 @@

import { build } from './page-object/build';
import { hasClass, notHasClass, isVisible, isHidden } from './page-object/predicates';
import { attribute, count, text, value } from './page-object/queries';
import { clickable, fillable, visitable } from './page-object/actions';
import {
build,
componentAttribute
} from './page-object/build';
import {
hasClassAttribute,
notHasClassAttribute,
isVisibleAttribute,
isHiddenAttribute
} from './page-object/predicates';
import {
attributeAttribute,
countAttribute,
textAttribute,
valueAttribute
} from './page-object/queries';
import {
clickableAttribute,
clickOnTextAttribute,
fillableAttribute,
visitableAttribute
} from './page-object/actions';
import { collection } from './page-object/collection';
function component(definition) {
return {
build: function(/*key, parent*/) {
let component = build(definition);
return function() {
return component;
};
}
};
}
export default {
attribute,
attribute: attributeAttribute,
build,
clickable,
clickable: clickableAttribute,
clickOnText: clickOnTextAttribute,
collection,
component,
count,
fillable,
hasClass,
isHidden,
isVisible,
notHasClass,
text,
value,
visitable
component: componentAttribute,
count: countAttribute,
fillable: fillableAttribute,
hasClass: hasClassAttribute,
isHidden: isHiddenAttribute,
isVisible: isVisibleAttribute,
notHasClass: notHasClassAttribute,
text: textAttribute,
value: valueAttribute,
visitable: visitableAttribute
};
/* global visit, fillIn, click */
import { qualifySelector } from './helpers';
import Attribute from './attribute';
function action(fn) {
return function(selector, options = {}) {
return {
build: function(key, page) {
return function(...args) {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector);
function visitable() {
this.page.lastPromise = visit(this.path);
page.lastPromise = fn(qualifiedSelector, ...args);
return this.page;
}
return page;
};
}
};
};
function clickable() {
this.page.lastPromise = click(this.qualifiedSelector());
return this.page;
}
export function visitable(path) {
return {
build: function(key, page) {
return function() {
page.lastPromise = visit(path);
function fillable(text) {
this.page.lastPromise = fillIn(this.qualifiedSelector(), text);
return page;
};
}
};
return this.page;
}
export var fillable = action((selector, text) => fillIn(selector, text));
export var clickable = action((selector) => click(selector));
function clickOnText(text) {
// Suppose that we have something like `<form><button>Submit</button></form>`
// In this case <form> and <button> elements contains "Submit" text, so, we'll
// want to __always__ click on the __last__ element that contains the text.
let selector = this.qualifiedSelector(`:contains("${text}"):last`);
// function clickableByText(selector, scope) {
// var qualifiedSelector = qualifySelector(scope, selector);
//
// return function(text) {
// return click('%@ :contains("%@"):last'.fmt(qualifiedSelector, text));
// };
// }
this.page.lastPromise = click(selector);
return this.page;
}
export function visitableAttribute(path) {
return new Attribute(visitable, null, null, { path });
}
export function clickableAttribute(selector, options = {}) {
return new Attribute(clickable, selector, options);
}
export function fillableAttribute(selector, options = {}) {
return new Attribute(fillable, selector, options);
}
export function clickOnTextAttribute(selector, options = {}) {
return new Attribute(clickOnText, selector, options);
}
function Component() {
}
function isAttribute(candidate) {
return $.isFunction(candidate.buildPageObjectAttribute);
}
function peekForAttributes(parent) {
let keys = Object.keys(parent);
for(let i = 0; i < keys.length; i++) {
if (isAttribute(parent[keys[i]])) {
return true;
}
}
return false;
}
function buildComponentIfNeeded(candidate, key, parent) {
if ($.isPlainObject(candidate) && peekForAttributes(candidate)) {
return componentAttribute(candidate).buildPageObjectAttribute(key, parent);
}
return candidate;
}
export function componentAttribute(definition) {
return {
buildPageObjectAttribute: function(/*key, parent*/) {
let component = build(definition);
return function() {
return component;
};
}
};
}
export function build(definition) {

@@ -11,3 +47,7 @@ let component = new Component(),

component[key] = (attr.build) ? attr.build(key, component) : attr;
if (isAttribute(attr)) {
component[key] = attr.buildPageObjectAttribute(key, component);
} else {
component[key] = buildComponentIfNeeded(attr, key, component);
}
});

@@ -14,0 +54,0 @@

import Ember from 'ember';
import { build } from './build';
import { count } from './queries';
import { countAttribute } from './queries';

@@ -13,3 +13,3 @@ let extend = Ember.$.extend;

return {
build: function(/*key, page*/) {
buildPageObjectAttribute: function(/*key, page*/) {
let itemComponent,

@@ -27,3 +27,3 @@ itemScope,

if (definition.count === undefined) {
definition.count = count(itemScope);
definition.count = countAttribute(itemScope);
}

@@ -30,0 +30,0 @@

/* global find, findWithAssert */
import { qualifySelector } from './helpers';
import Attribute from './attribute';
export function hasClass(cssClass, selector, options = {}) {
return {
build: function(key, page) {
return function() {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector),
element = findWithAssert(qualifiedSelector);
function hasClass() {
return this.elementOrRaise().hasClass(this.cssClass);
}
return element.hasClass(cssClass);
};
}
};
function notHasClass() {
return !this.elementOrRaise().hasClass(this.cssClass);
}
export function notHasClass(cssClass, selector, options = {}) {
return {
build: function(key, page) {
return function() {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector),
element = findWithAssert(qualifiedSelector);
function isVisible() {
return this.elementOrRaise().is(':visible');
}
return !element.hasClass(cssClass);
};
}
};
function isHidden() {
let element = this.element();
return (element.length > 0) ? element.is(':hidden') : true;
}
export function isVisible(selector, options = {}) {
return {
build: function(key, page) {
return function() {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector),
element = findWithAssert(qualifiedSelector);
export function notHasClassAttribute(cssClass, selector, options = {}) {
return new Attribute(notHasClass, selector, options, { cssClass });
}
return element.is(':visible');
};
}
};
export function hasClassAttribute(cssClass, selector, options = {}) {
return new Attribute(hasClass, selector, options, { cssClass });
}
export function isHidden(selector, options = {}) {
return {
build: function(key, page) {
return function() {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector),
element = find(qualifiedSelector);
export function isVisibleAttribute(selector, options = {}) {
return new Attribute(isVisible, selector, options);
}
return (element.length > 0) ? element.is(':hidden') : true;
};
}
};
export function isHiddenAttribute(selector, options = {}) {
return new Attribute(isHidden, selector, options);
}

@@ -1,43 +0,34 @@

/* global findWithAssert */
import { trim } from './helpers';
import Attribute from './attribute';
import { qualifySelector, trim } from './helpers';
function attribute() {
return this.elementOrRaise().attr(this.attributeName);
}
export function attribute(attributeName, selector, options = {}) {
return {
build: function(key, page) {
return function(...args) {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector),
element = findWithAssert(qualifiedSelector);
function count() {
return this.element().length;
}
return element.attr(attributeName);
};
}
};
function text() {
return trim(this.elementOrRaise().text())
}
function query(fn, useFind = false) {
return function(selector, options = {}) {
return {
build: function(key, page) {
return function(...args) {
let qualifiedSelector = qualifySelector(options.scope || page.scope, selector),
element;
function value() {
return this.elementOrRaise().val();
}
element = (useFind) ? find(qualifiedSelector) : findWithAssert(qualifiedSelector);
export function attributeAttribute(attributeName, selector, options = {}) {
return new Attribute(attribute, selector, options, { attributeName });
}
return fn(element, ...args);
};
}
};
};
export function countAttribute(selector, options = {}) {
return new Attribute(count, selector, options);
}
const count = query(elements => elements.length, true),
text = query(element => trim(element.text())),
value = query(element => element.val());
export function textAttribute(selector, options = {}) {
return new Attribute(text, selector, options);
}
export {
count,
text,
value
};
export function valueAttribute(selector, options = {}) {
return new Attribute(value, selector, options);
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc