mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 09:23:13 +01:00
Merge pull request #146 from ascribe/AD-1519-visual-regression-cli
AD-1519 Visual regression tests
This commit is contained in:
commit
3c6dea3585
8
.gitignore
vendored
8
.gitignore
vendored
@ -17,9 +17,13 @@ pids
|
||||
logs
|
||||
results
|
||||
|
||||
node_modules/*
|
||||
build/*
|
||||
|
||||
build
|
||||
gemini-coverage/*
|
||||
gemini-report/*
|
||||
test/gemini/screenshots/*
|
||||
|
||||
node_modules/*
|
||||
|
||||
.DS_Store
|
||||
.env
|
||||
|
76
README.md
76
README.md
@ -1,17 +1,18 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Onion is the web client for Ascribe. The idea is to have a well documented,
|
||||
easy to test, easy to hack, JavaScript application.
|
||||
Onion is the web client for Ascribe. The idea is to have a well documented, modern, easy to test, easy to hack, JavaScript application.
|
||||
|
||||
The code is JavaScript ECMA 6.
|
||||
The code is JavaScript 2015 / ECMAScript 6.
|
||||
|
||||
|
||||
Getting started
|
||||
===============
|
||||
|
||||
Install some nice extension for Chrom(e|ium):
|
||||
|
||||
- [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)
|
||||
- [Alt Developer Tools](https://github.com/goatslacker/alt-devtool)
|
||||
|
||||
```bash
|
||||
git clone git@github.com:ascribe/onion.git
|
||||
@ -37,17 +38,34 @@ Additionally, to work on the white labeling functionality, you need to edit your
|
||||
|
||||
JavaScript Code Conventions
|
||||
===========================
|
||||
|
||||
For this project, we're using:
|
||||
|
||||
* 4 Spaces
|
||||
* We use ES6
|
||||
* ES6
|
||||
* We don't use ES6's class declaration for React components because it does not support Mixins as well as Autobinding ([Blog post about it](http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding))
|
||||
* We don't use camel case for file naming but in everything Javascript related
|
||||
* We use `let` instead of `var`: [SA Post](http://stackoverflow.com/questions/762011/javascript-let-keyword-vs-var-keyword)
|
||||
* We don't use Javascript's `Date` object, as its interface introduced bugs previously and we're including `momentjs` for other dependencies anyways
|
||||
* We use `momentjs` instead of Javascript's `Date` object, as the native `Date` interface previously introduced bugs and we're including `momentjs` for other dependencies anyway
|
||||
|
||||
Make sure to check out the [style guide](https://github.com/ascribe/javascript).
|
||||
|
||||
Linting
|
||||
-------
|
||||
|
||||
We use [ESLint](https://github.com/eslint/eslint) with our own [custom ruleset](.eslintrc).
|
||||
|
||||
|
||||
SCSS Code Conventions
|
||||
=====================
|
||||
|
||||
Install [lint-scss](https://github.com/brigade/scss-lint), check the [editor integration docs](https://github.com/brigade/scss-lint#editor-integration) to integrate the lint in your editor.
|
||||
|
||||
Some interesting links:
|
||||
* [Improving Sass code quality on theguardian.com](https://www.theguardian.com/info/developer-blog/2014/may/13/improving-sass-code-quality-on-theguardiancom)
|
||||
|
||||
|
||||
Branch names
|
||||
=====================
|
||||
============
|
||||
|
||||
To allow Github and JIRA to track branches while still allowing us to switch branches quickly using a ticket's number (and keep our peace of mind), we have the following rules for naming branches:
|
||||
|
||||
@ -61,22 +79,21 @@ AD-<JIRA-ticket-id>-brief-and-sane-description-of-the-ticket
|
||||
|
||||
where `brief-and-sane-description-of-the-ticket` does not need to equal to the issue or ticket's title.
|
||||
|
||||
|
||||
Example
|
||||
-------------
|
||||
-------
|
||||
|
||||
**JIRA ticket name:** `AD-1242 - Frontend caching for simple endpoints to measure perceived page load <more useless information>`
|
||||
|
||||
**Github branch name:** `AD-1242-caching-solution-for-stores`
|
||||
|
||||
SCSS Code Conventions
|
||||
=====================
|
||||
Install [lint-scss](https://github.com/brigade/scss-lint), check the [editor integration docs](https://github.com/brigade/scss-lint#editor-integration) to integrate the lint in your editor.
|
||||
|
||||
Some interesting links:
|
||||
* [Improving Sass code quality on theguardian.com](https://www.theguardian.com/info/developer-blog/2014/may/13/improving-sass-code-quality-on-theguardiancom)
|
||||
|
||||
|
||||
Testing
|
||||
===============
|
||||
=======
|
||||
|
||||
Unit Testing
|
||||
------------
|
||||
|
||||
We're using Facebook's jest to do testing as it integrates nicely with react.js as well.
|
||||
|
||||
Tests are always created per directory by creating a `__tests__` folder. To test a specific file, a `<file_name>_tests.js` file needs to be created.
|
||||
@ -86,7 +103,24 @@ This is due to the fact that jest's function mocking and ES6 module syntax are [
|
||||
|
||||
Therefore, to require a module in your test file, you need to use CommonJS's `require` syntax. Except for this, all tests can be written in ES6 syntax.
|
||||
|
||||
## Workflow
|
||||
Visual Regression Testing
|
||||
-------------------------
|
||||
|
||||
We're using [Gemini](https://github.com/gemini-testing/gemini) for visual regression tests because it supports both PhantomJS2 and SauceLabs.
|
||||
|
||||
See the [helper docs](test/gemini/README.md) for information on installing Gemini, its dependencies, and running and writing tests.
|
||||
|
||||
Integration Testing
|
||||
-------------------
|
||||
|
||||
We're using [Sauce Labs](https://saucelabs.com/home) with [WD.js](https://github.com/admc/wd) for integration testing across browser grids with Selenium.
|
||||
|
||||
See the [helper docs](test/integration/README.md) for information on each part of the test stack and how to run and write tests.
|
||||
|
||||
|
||||
Workflow
|
||||
========
|
||||
|
||||
Generally, when you're runing `gulp serve`, all tests are being run.
|
||||
If you want to test exclusively (without having the obnoxious ES6Linter warnings), you can just run `gulp jest:watch`.
|
||||
|
||||
@ -137,9 +171,16 @@ A: Easily by starting the your gulp process with the following command:
|
||||
ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve
|
||||
```
|
||||
|
||||
Or, by adding these two your environment variables:
|
||||
```
|
||||
ONION_BASE_URL='/'
|
||||
ONION_SERVER_URL='http://localhost.com:8000/'
|
||||
```
|
||||
|
||||
Q: I want to know all dependencies that get bundled into the live build.
|
||||
A: ```browserify -e js/app.js --list > webapp-dependencies.txt```
|
||||
|
||||
|
||||
Reading list
|
||||
============
|
||||
|
||||
@ -152,7 +193,6 @@ Start here
|
||||
- [alt.js](http://alt.js.org/)
|
||||
- [alt.js readme](https://github.com/goatslacker/alt)
|
||||
|
||||
|
||||
Moar stuff
|
||||
----------
|
||||
|
||||
|
@ -97,7 +97,8 @@ gulp.task('browser-sync', function() {
|
||||
proxy: 'http://localhost:4000',
|
||||
port: 3000,
|
||||
open: false, // does not open the browser-window anymore (handled manually)
|
||||
ghostMode: false
|
||||
ghostMode: false,
|
||||
notify: false // stop showing the browsersync pop up
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -84,6 +84,7 @@ let PieceListToolbarFilterWidget = React.createClass({
|
||||
if (this.props.filterParams && this.props.filterParams.length) {
|
||||
return (
|
||||
<DropdownButton
|
||||
id="ascribe-piece-list-toolbar-filter-widget-dropdown"
|
||||
pullRight={true}
|
||||
title={filterIcon}
|
||||
className="ascribe-piece-list-toolbar-filter-widget">
|
||||
|
@ -45,7 +45,7 @@ let PieceListToolbarOrderWidget = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let filterIcon = (
|
||||
let orderIcon = (
|
||||
<span>
|
||||
<span className="ascribe-icon icon-ascribe-sort" aria-hidden="true"></span>
|
||||
<span style={this.isOrderActive()}>·</span>
|
||||
@ -55,9 +55,10 @@ let PieceListToolbarOrderWidget = React.createClass({
|
||||
if (this.props.orderParams && this.props.orderParams.length) {
|
||||
return (
|
||||
<DropdownButton
|
||||
id="ascribe-piece-list-toolbar-order-widget-dropdown"
|
||||
pullRight={true}
|
||||
title={filterIcon}
|
||||
className="ascribe-piece-list-toolbar-filter-widget">
|
||||
className="ascribe-piece-list-toolbar-filter-widget"
|
||||
title={orderIcon}>
|
||||
<li style={{'textAlign': 'center'}}>
|
||||
<em>{getLangText('Sort by')}:</em>
|
||||
</li>
|
||||
|
@ -27,7 +27,7 @@ let CoaVerifyContainer = React.createClass({
|
||||
|
||||
return (
|
||||
<div className="ascribe-login-wrapper">
|
||||
<br/>
|
||||
<br />
|
||||
<div className="ascribe-login-text ascribe-login-header">
|
||||
{getLangText('Verify your Certificate of Authenticity')}
|
||||
</div>
|
||||
@ -60,9 +60,8 @@ let CoaVerifyForm = React.createClass({
|
||||
},
|
||||
|
||||
handleSuccess(response){
|
||||
let notification = null;
|
||||
if (response.verdict) {
|
||||
notification = new GlobalNotificationModel(getLangText('Certificate of Authenticity successfully verified'), 'success');
|
||||
const notification = new GlobalNotificationModel(getLangText('Certificate of Authenticity successfully verified'), 'success');
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
}
|
||||
},
|
||||
@ -71,7 +70,6 @@ let CoaVerifyForm = React.createClass({
|
||||
const { message, signature } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
url={ApiUrls.coa_verify}
|
||||
handleSuccess={this.handleSuccess}
|
||||
@ -80,7 +78,8 @@ let CoaVerifyForm = React.createClass({
|
||||
type="submit"
|
||||
className="btn btn-default btn-wide">
|
||||
{getLangText('Verify your Certificate of Authenticity')}
|
||||
</button>}
|
||||
</button>
|
||||
}
|
||||
spinner={
|
||||
<span className="btn btn-default btn-wide btn-spinner">
|
||||
<AscribeSpinner color="dark-blue" size="md" />
|
||||
@ -94,8 +93,7 @@ let CoaVerifyForm = React.createClass({
|
||||
placeholder={getLangText('Copy paste the message on the bottom of your Certificate of Authenticity')}
|
||||
autoComplete="on"
|
||||
defaultValue={message}
|
||||
name="username"
|
||||
required/>
|
||||
required />
|
||||
</Property>
|
||||
<Property
|
||||
name='signature'
|
||||
@ -106,11 +104,10 @@ let CoaVerifyForm = React.createClass({
|
||||
rows={3}
|
||||
placeholder={getLangText('Copy paste the signature on the bottom of your Certificate of Authenticity')}
|
||||
defaultValue={signature}
|
||||
required/>
|
||||
required />
|
||||
</Property>
|
||||
<hr />
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -173,6 +173,7 @@ let Header = React.createClass({
|
||||
account = (
|
||||
<DropdownButton
|
||||
ref='dropdownbutton'
|
||||
id="nav-route-user-dropdown"
|
||||
eventKey="1"
|
||||
title={currentUser.username}>
|
||||
<LinkContainer
|
||||
|
@ -126,6 +126,7 @@ let HeaderNotifications = React.createClass({
|
||||
<Nav navbar right>
|
||||
<DropdownButton
|
||||
ref='dropdownbutton'
|
||||
id="header-notification-dropdown"
|
||||
eventKey="1"
|
||||
title={
|
||||
<span>
|
||||
|
@ -30,6 +30,7 @@ let NavRoutesLinksLink = React.createClass({
|
||||
return (
|
||||
<DropdownButton
|
||||
disabled={disabled}
|
||||
id={`nav-route-${headerTitle.toLowerCase()}-dropdown`}
|
||||
title={headerTitle}>
|
||||
{children}
|
||||
</DropdownButton>
|
||||
|
@ -57,7 +57,7 @@ const ROUTES = {
|
||||
headerTitle='COLLECTION'/>
|
||||
<Route path='pieces/:pieceId' component={SluicePieceContainer} />
|
||||
<Route path='editions/:editionId' component={EditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
),
|
||||
@ -97,7 +97,7 @@ const ROUTES = {
|
||||
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPSettingsContainer)}/>
|
||||
<Route path='pieces/:pieceId' component={SPPieceContainer} />
|
||||
<Route path='editions/:editionId' component={EditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
)
|
||||
|
@ -78,7 +78,7 @@ let ROUTES = {
|
||||
headerTitle='COLLECTION'
|
||||
disableOn='noPieces' />
|
||||
<Route path='editions/:editionId' component={EditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='pieces/:pieceId' component={CylandPieceContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
@ -114,7 +114,7 @@ let ROUTES = {
|
||||
disableOn='noPieces' />
|
||||
<Route path='pieces/:pieceId' component={PieceContainer} />
|
||||
<Route path='editions/:editionId' component={EditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
),
|
||||
@ -159,7 +159,7 @@ let ROUTES = {
|
||||
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvContractNotifications)} />
|
||||
<Route path='pieces/:pieceId' component={IkonotvPieceContainer} />
|
||||
<Route path='editions/:editionId' component={EditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
),
|
||||
@ -196,7 +196,7 @@ let ROUTES = {
|
||||
disableOn='noPieces' />
|
||||
<Route path='pieces/:pieceId' component={MarketPieceContainer} />
|
||||
<Route path='editions/:editionId' component={MarketEditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
),
|
||||
@ -233,7 +233,7 @@ let ROUTES = {
|
||||
disableOn='noPieces' />
|
||||
<Route path='pieces/:pieceId' component={MarketPieceContainer} />
|
||||
<Route path='editions/:editionId' component={MarketEditionContainer} />
|
||||
<Route path='verify' component={CoaVerifyContainer} />
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
)
|
||||
|
20
package.json
20
package.json
@ -12,8 +12,22 @@
|
||||
"postinstall": "npm run build",
|
||||
"build": "gulp build --production",
|
||||
"start": "node server.js",
|
||||
"test": "mocha",
|
||||
"tunnel": "node test/tunnel.js"
|
||||
"test": "npm run sauce-test",
|
||||
"sauce-test": "mocha ./test/integration/tests/",
|
||||
"tunnel": "node ./test/integration/tunnel.js",
|
||||
"vi-clean": "rm -rf ./gemini-report",
|
||||
"vi-phantom": "phantomjs --webdriver=4444",
|
||||
"vi-update": "gemini update -c ./test/gemini/.gemini.yml",
|
||||
"vi-test": "npm run vi-test:base || true",
|
||||
"vi-test:base": "npm run vi-clean && gemini test -c ./test/gemini/.gemini.yml --reporter html --reporter vflat",
|
||||
"vi-test:all": "npm run vi-test",
|
||||
"vi-test:main": "npm run vi-test:base -- --browser MainDesktop --browser MainMobile || true",
|
||||
"vi-test:whitelabel": "GEMINI_BROWSERS='CcDesktop, CcMobile, CylandDesktop, CylandMobile, IkonotvDesktop, IkonotvMobile, LumenusDesktop, LumenusMobile, 23viviDesktop, 23viviMobile' npm run vi-test:base || true",
|
||||
"vi-test:cc": "npm run vi-test:base -- --browser CcDesktop --browser CcMobile",
|
||||
"vi-test:cyland": "npm run vi-test:base -- --browser CylandDesktop --browser CylandMobile || true",
|
||||
"vi-test:ikonotv": "npm run vi-test:base -- --browser IkonotvDesktop --browser IkonotvMobile || true",
|
||||
"vi-test:lumenus": "npm run vi-test:base -- --browser LumenusDesktop --browser LumenusMobile || true",
|
||||
"vi-test:23vivi": "npm run vi-test:base -- --browser 23viviDesktop --browser 23viviMobile || true"
|
||||
},
|
||||
"browser": {
|
||||
"fineUploader": "./js/components/ascribe_uploader/vendor/s3.fine-uploader.js"
|
||||
@ -42,8 +56,10 @@
|
||||
"chai-as-promised": "^5.1.0",
|
||||
"colors": "^1.1.2",
|
||||
"dotenv": "^1.2.0",
|
||||
"gemini": "^2.1.0",
|
||||
"jest-cli": "^0.4.0",
|
||||
"mocha": "^2.3.4",
|
||||
"phantomjs2": "^2.0.2",
|
||||
"sauce-connect-launcher": "^0.13.0",
|
||||
"wd": "^0.4.0"
|
||||
},
|
||||
|
133
test/gemini/.gemini.yml
Normal file
133
test/gemini/.gemini.yml
Normal file
@ -0,0 +1,133 @@
|
||||
rootUrl: http://localhost.com:3000/
|
||||
sessionsPerBrowser: 1
|
||||
|
||||
browsers:
|
||||
MainDesktop:
|
||||
rootUrl: http://localhost.com:3000/
|
||||
screenshotsDir: './screenshots/main-desktop'
|
||||
windowSize: 1900x1080
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
MainMobile:
|
||||
rootUrl: http://localhost.com:3000/
|
||||
screenshotsDir: './screenshots/main-mobile'
|
||||
windowSize: 600x1056
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
CcDesktop:
|
||||
rootUrl: http://cc.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/cc-desktop'
|
||||
windowSize: 1900x1080
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
CcMobile:
|
||||
rootUrl: http://cc.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/cc-mobile'
|
||||
windowSize: 600x1056
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
CylandDesktop:
|
||||
rootUrl: http://cyland.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/cyland-desktop'
|
||||
windowSize: 1900x1080
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
CylandMobile:
|
||||
rootUrl: http://cyland.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/cyland-mobile'
|
||||
windowSize: 600x1056
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
IkonotvDesktop:
|
||||
rootUrl: http://ikonotv.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/ikonotv-desktop'
|
||||
windowSize: 1900x1080
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
IkonotvMobile:
|
||||
rootUrl: http://ikonotv.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/ikonotv-mobile'
|
||||
windowSize: 600x1056
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
LumenusDesktop:
|
||||
rootUrl: http://lumenus.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/lumenus-desktop'
|
||||
windowSize: 1900x1080
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
LumenusMobile:
|
||||
rootUrl: http://lumenus.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/lumenus-mobile'
|
||||
windowSize: 600x1056
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
23viviDesktop:
|
||||
rootUrl: http://23vivi.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/23vivi-desktop'
|
||||
windowSize: 1900x1080
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
23viviMobile:
|
||||
rootUrl: http://23vivi.localhost.com:3000/
|
||||
screenshotsDir: './screenshots/23vivi-mobile'
|
||||
windowSize: 600x1056
|
||||
desiredCapabilities:
|
||||
browserName: phantomjs
|
||||
|
||||
sets:
|
||||
main:
|
||||
files:
|
||||
- tests/main
|
||||
browsers:
|
||||
- MainDesktop
|
||||
- MainMobile
|
||||
cc:
|
||||
files:
|
||||
- tests/whitelabel/shared
|
||||
browsers:
|
||||
- CcDesktop
|
||||
- CcMobile
|
||||
|
||||
cyland:
|
||||
files:
|
||||
- tests/whitelabel/shared
|
||||
- tests/whitelabel/cyland
|
||||
browsers:
|
||||
- CylandDesktop
|
||||
- CylandMobile
|
||||
|
||||
ikonotv:
|
||||
files:
|
||||
- tests/whitelabel/shared
|
||||
- tests/whitelabel/ikonotv
|
||||
browsers:
|
||||
- IkonotvDesktop
|
||||
- IkonotvMobile
|
||||
|
||||
lumenus:
|
||||
files:
|
||||
- tests/whitelabel/shared
|
||||
- tests/whitelabel/lumenus
|
||||
browsers:
|
||||
- LumenusDesktop
|
||||
- LumenusMobile
|
||||
|
||||
23vivi:
|
||||
files:
|
||||
- tests/whitelabel/shared
|
||||
- tests/whitelabel/23vivi
|
||||
browsers:
|
||||
- 23viviDesktop
|
||||
- 23viviMobile
|
208
test/gemini/README.md
Normal file
208
test/gemini/README.md
Normal file
@ -0,0 +1,208 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
When in doubt, see [Gemini](https://github.com/gemini-testing/gemini) and [their
|
||||
docs](https://github.com/gemini-testing/gemini/tree/master/doc) for more information as well as configuration options.
|
||||
|
||||
Contents
|
||||
========
|
||||
|
||||
1. [Installation](#installation)
|
||||
1. [Running Tests](#running-tests)
|
||||
1. [Gemini Usage and Writing Tests](#gemini-usage-and-writing-tests)
|
||||
1. [PhantomJS](#phantomjs)
|
||||
1. [TODO](#todo)
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
First make sure that you're using NodeJS 5.0+ as the tests are written using ES6 syntax.
|
||||
|
||||
Then, install [PhantomJS2](https://www.npmjs.com/package/phantomjs2):
|
||||
|
||||
```bash
|
||||
# Until phantomjs2 is updated for the new 2.1 version of PhantomJS, use the following (go to https://bitbucket.org/ariya/phantomjs/downloads to find a build for your OS)
|
||||
npm install -g phantomjs2 --phantomjs_downloadurl=https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-macosx.zip
|
||||
npm install --save-dev phantomjs2 --phantomjs_downloadurl=https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-macosx.zip
|
||||
|
||||
# If using OSX, you may have to install upx and decompress the binary downloaded by npm manually:
|
||||
brew install upx
|
||||
|
||||
# Navigate to the binary, ie. /Users/Brett/.nvm/versions/node/v5.4.0/lib/node_modules/phantomjs2/lib/phantom/bin/phantomjs
|
||||
upx -d phantomjs
|
||||
|
||||
```
|
||||
|
||||
Finally, [install Gemini globally and locally with npm](https://github.com/gemini-testing/gemini/blob/master/README.md#installation).
|
||||
|
||||
|
||||
Running Tests
|
||||
=============
|
||||
|
||||
Run PhantomJS:
|
||||
|
||||
```bash
|
||||
npm run vi-phantom
|
||||
```
|
||||
|
||||
And then run Gemini tests:
|
||||
|
||||
```bash
|
||||
npm run vi-test
|
||||
|
||||
# Run only main tests
|
||||
npm run vi-test:main
|
||||
|
||||
# Run only whitelabel tests
|
||||
npm run vi-test:whitelabel
|
||||
|
||||
# Run only specific whitelabel tests
|
||||
npm run vi-test:cyland
|
||||
```
|
||||
|
||||
If you've made changes and want them to be the new baseline (ie. it's a correct change--**make sure** to test there are
|
||||
no regressions first!), use
|
||||
|
||||
```bash
|
||||
npm run vi-update
|
||||
|
||||
# Update just the main app for desktop and mobile
|
||||
npm run vi-update -- --browser MainDesktop --browser MainMobile
|
||||
```
|
||||
|
||||
|
||||
Gemini Usage and Writing Tests
|
||||
==============================
|
||||
|
||||
While Gemini itself is easy to use on simple, static pages, there are some nice to knows when dealing with a single page
|
||||
app like ours (where much of it is behind an authentication barrier as well).
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
See [the docs](https://github.com/gemini-testing/gemini/blob/master/doc/commands.md) on the commands that are available.
|
||||
`npm run vi-*` is set up with some of these commands, but you may want to build your own or learn about some of the
|
||||
other functions.
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
Authentication presents a tricky problem with Gemini, since we can't inject any cookies or even run a start up script
|
||||
through the browser before letting Gemini hook in. The solution is to script the log in process through Gemini, and put
|
||||
waits for the log in to succeed, before testing parts of the app that require the authentication.
|
||||
|
||||
Browser Session States
|
||||
----------------------
|
||||
|
||||
Gemini will start a new instance of the browser for each browser configuration defined in the .gemini.yml file when
|
||||
Gemini's launched (ie. `gemini update`, `gemini test`, etc).
|
||||
|
||||
Although each new suite will cause the testing browser to be refreshed, the above means that cookies and other
|
||||
persistent state will be kept across suites for a browser across all runs, even if the suites are from different files.
|
||||
|
||||
**What this comes down to is**: once you've logged in, you'll stay logged in until you decide to log out or the running
|
||||
instance of Gemini ends. In general practice, it's a good idea to clear the state of the app at the end of each suite of
|
||||
tests by logging out.
|
||||
|
||||
(**Note**: Persistent storage, such as local storage, has not been explicitly tested as to whether they are kept, but as
|
||||
the cookies are cleared each time, this seems unlikely)
|
||||
|
||||
Test Reporting
|
||||
--------------
|
||||
|
||||
Using the `--reporter html` flag with Gemini will produce a webpage with the test's results in `onion/gemini-report`
|
||||
that will show the old, new, and diff images. Using this is highly recommended (and fun!) and is used by default in `npm
|
||||
run vi-test`.
|
||||
|
||||
Writing Tests
|
||||
-------------
|
||||
|
||||
See [the docs](https://github.com/gemini-testing/gemini/blob/master/doc/tests.md), and the [section on the available
|
||||
actions](https://github.com/gemini-testing/gemini/blob/master/doc/tests.md#available-actions) for what scripted actions
|
||||
are available.
|
||||
|
||||
Our tests are located in `onion/test/gemini/tests/`. For now, the tests use the environment defined in
|
||||
`onion/test/gemini/tests/environment.js` for which user, piece, and edition to run tests against. In the future, it'd be
|
||||
nice if we had some db scripts that we could use to populate a test db for these regression tests.
|
||||
|
||||
**It would also be nice if we kept the whitelabels up to date, so if you add one, please also test (at least) its landing
|
||||
page.**
|
||||
|
||||
Some useful tips:
|
||||
* The `find()` method in the callbacks is equivalent to `document.querySelector`; it will only return the first
|
||||
element found that matches the selector. Use pseudo classes like `nth-of-type()`, `nth-child()`, and etc. to select
|
||||
later elements.
|
||||
* Nested suites inherit from their parent suites' configurations, but will **override** their inherited configuration
|
||||
if another is specified. For example, if `parentSuite` had a `.before()` method, all children of `parentSuite` would
|
||||
run its `.before()`, but if any of the children specified their own `.before()`, those children would **not** run
|
||||
`parentSuite`'s `.before()`.
|
||||
* Gemini takes a screenshot of the minimum bounding rect for all specified selectors, so this means you can't take a
|
||||
screenshot of two items far away from each other without the rest being considered (ie. trying to get the header and
|
||||
footer)
|
||||
* Unfortunately, `setCaptureElements` and `ignoreElements` will only apply for the first element found matching those
|
||||
selectors.
|
||||
|
||||
PhantomJS
|
||||
=========
|
||||
|
||||
[PhantomJS](http://phantomjs.org/) is a headless browser that allows us to run tests and take screenshots without
|
||||
needing a browser.
|
||||
|
||||
Its second version (PhantomJS2) uses a much more recent version of Webkit, and is a big reason why Gemini (as opposed to
|
||||
other utilities, ie. PhantomCSS) was chosen. Due to the large number of breaking changes introduced between PhantomJS
|
||||
1.9 to 2.0, a large number of tools (ie. CasperJS) are, at the time of writing, lacking support for 2.0.
|
||||
|
||||
While you don't need to know too much about PhantomJS to use and write Gemini tests, there are still a number of useful
|
||||
things to know about.
|
||||
|
||||
Useful features
|
||||
---------------
|
||||
|
||||
You can find the full list of CLI commands in the [documentation](http://phantomjs.org/api/command-line.html).
|
||||
|
||||
Flags that are of particular interest to us:
|
||||
* `--webdriver=4444`: sets the webdriver port to be 4444, the default webdriver port that Gemini expects.
|
||||
* `--ignore-ssl-errors=true`: ignores any SSL errors that may occur. Particular useful when hooking up the tests to
|
||||
staging, as the certificate we use is self-signed.
|
||||
* `--ssl-protocol=any`: allows any ssl protocol to be used. May be useful when `--ignore-ssl-errors=true` doesn't work.
|
||||
* '--remote-debugger-port`: allows for remote debugging the running PhantomJS instance. More on this later.
|
||||
|
||||
Troubleshooting and Debugging
|
||||
-----------------------------
|
||||
|
||||
Remote debugging is possible with PhantomJS using the `--remote-debugger-port` option. See the [troubleshooting
|
||||
docs](http://phantomjs.org/troubleshooting.html).
|
||||
|
||||
To begin using it, add `debugger;` statements to the file being run by `phantomjs`, and access the port number specified
|
||||
after `--remote-debugger-port` on localhost:
|
||||
|
||||
```bash
|
||||
phantomjs --remote-debugger-port=9000 debug.js
|
||||
```
|
||||
|
||||
PhantomJS will start and then immediately breakpoint. Go to http://localhost:9000/webkit/inspector/inspector.html?page=1
|
||||
and then to its console tab. Go to your first breakpoint (the first `debugger;` statement executed) by running `__run()`
|
||||
in the console tab. Subsequent breakpoints can be reached by successively running `__run()` in that same console tab.
|
||||
|
||||
At each breakpoint, you can to http://localhost:9000 on a new browser tab and click on one of the links to go to the
|
||||
current execution state of that breakpoint on the page you're on.
|
||||
|
||||
---
|
||||
|
||||
To simplify triaging simple issues and test if everything is working, The repo had a short test script that can be run
|
||||
with PhantomJS to check if it can access the web app and log in. Find `onion/test/phantomjs/launch_app_and_login.js` in
|
||||
the repo's history, restore it, and then run:
|
||||
|
||||
```bash
|
||||
# In root /onion folder
|
||||
phantomjs test/phantomjs/launch_app_and_login.js
|
||||
```
|
||||
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
* Write scripts to automate creation of test users (and modify tests to accomodate)
|
||||
* Set scripts with rootUrls pointing to staging / live using environment variables
|
||||
* Set up with Sauce Labs
|
35
test/gemini/tests/environment.js
Normal file
35
test/gemini/tests/environment.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const MAIN_USER = {
|
||||
email: 'dimi@mailinator.com',
|
||||
password: '0000000000'
|
||||
};
|
||||
const MAIN_PIECE_ID = '12374';
|
||||
const MAIN_EDITION_ID = '14gw9x3VA9oJaxp4cHaAuK2bvJzvEj4Xvc';
|
||||
|
||||
const TIMEOUTS = {
|
||||
SHORT: 3000,
|
||||
NORMAL: 5000,
|
||||
LONG: 10000,
|
||||
SUPER_DUPER_EXTRA_LONG: 30000
|
||||
};
|
||||
|
||||
console.log('================== Test environment ==================\n');
|
||||
console.log('Main user:');
|
||||
console.log(` Email: ${MAIN_USER.email}`);
|
||||
console.log(` Password: ${MAIN_USER.password}\n`);
|
||||
console.log(`Main piece: ${MAIN_PIECE_ID}`);
|
||||
console.log(`Main edition: ${MAIN_EDITION_ID}\n`);
|
||||
console.log('Timeouts:');
|
||||
console.log(` Short: ${TIMEOUTS.SHORT}`);
|
||||
console.log(` Normal: ${TIMEOUTS.NORMAL}\n`);
|
||||
console.log(` Long: ${TIMEOUTS.LONG}\n`);
|
||||
console.log(` Super super extra long: ${TIMEOUTS.SUPER_DUPER_EXTRA_LONG}\n`);
|
||||
console.log('========================================================\n');
|
||||
|
||||
module.exports = {
|
||||
MAIN_USER,
|
||||
MAIN_PIECE_ID,
|
||||
MAIN_EDITION_ID,
|
||||
TIMEOUTS
|
||||
};
|
218
test/gemini/tests/main/authenticated.js
Normal file
218
test/gemini/tests/main/authenticated.js
Normal file
@ -0,0 +1,218 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../environment');
|
||||
const MAIN_USER = environment.MAIN_USER;
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Suite of tests against routes that require the user to be authenticated.
|
||||
*/
|
||||
gemini.suite('Authenticated', (suite) => {
|
||||
suite
|
||||
.setUrl('/collection')
|
||||
.setCaptureElements('.ascribe-body')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins unless that suite
|
||||
// also defines a `.before()`
|
||||
// FIXME: use a more generic class for this, like just '.app',
|
||||
// when we can use this file with the whitelabels
|
||||
actions.waitForElementToShow('.ascribe-default-app', TIMEOUTS.NORMAL);
|
||||
});
|
||||
|
||||
// Suite just to log us in before any other suites run
|
||||
gemini.suite('Login', (loginSuite) => {
|
||||
loginSuite
|
||||
.setUrl('/login')
|
||||
.ignoreElements('.ascribe-body')
|
||||
.capture('logged in', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
|
||||
actions.sendKeys(find('.ascribe-login-wrapper input[name=email]'), MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-login-wrapper input[name=password]'), MAIN_USER.password);
|
||||
actions.click(find('.ascribe-login-wrapper button[type=submit]'));
|
||||
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Header-desktop', (headerSuite) => {
|
||||
headerSuite
|
||||
.setCaptureElements('nav.navbar .container')
|
||||
// Ignore Cyland's logo as it's a gif
|
||||
.ignoreElements('.client--cyland img.img-brand')
|
||||
.skip(/Mobile/)
|
||||
.before((actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('desktop header');
|
||||
|
||||
gemini.suite('User dropdown', (headerUserSuite) => {
|
||||
headerUserSuite
|
||||
.setCaptureElements('#nav-route-user-dropdown ~ .dropdown-menu')
|
||||
.capture('expanded user dropdown', (actions, find) => {
|
||||
actions.click(find('#nav-route-user-dropdown'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Notification dropdown', (headerNotificationSuite) => {
|
||||
headerNotificationSuite
|
||||
.setCaptureElements('#header-notification-dropdown ~ .dropdown-menu')
|
||||
.capture('expanded notifications dropdown', (actions, find) => {
|
||||
actions.click(find('#header-notification-dropdown'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test for the collapsed header in mobile
|
||||
gemini.suite('Header-mobile', (headerMobileSuite) => {
|
||||
headerMobileSuite
|
||||
.setCaptureElements('nav.navbar .container')
|
||||
// Ignore Cyland's logo as it's a gif
|
||||
.ignoreElements('.client--cyland img.img-brand')
|
||||
.skip(/Desktop/)
|
||||
.before((actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('mobile header')
|
||||
.capture('expanded mobile header', (actions, find) => {
|
||||
actions.click(find('nav.navbar .navbar-toggle'));
|
||||
// Wait for the header to expand
|
||||
actions.wait(500);
|
||||
})
|
||||
.capture('expanded user dropdown', (actions, find) => {
|
||||
actions.click(find('#nav-route-user-dropdown'));
|
||||
})
|
||||
.capture('expanded notifications dropdown', (actions, find) => {
|
||||
actions.click(find('#header-notification-dropdown'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Collection', (collectionSuite) => {
|
||||
collectionSuite
|
||||
.setCaptureElements('.ascribe-accordion-list')
|
||||
.before((actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
// Wait for the images to load
|
||||
// FIXME: unfortuntately gemini doesn't support ignoring multiple elements from a single selector
|
||||
// so we're forced to wait and hope that the images will all finish loading after 5s.
|
||||
// We could also change the thumbnails with JS, but setting up a test user is probably easier.
|
||||
actions.wait(TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('collection')
|
||||
.capture('expanded edition in collection', (actions, find) => {
|
||||
actions.click(find('.ascribe-accordion-list-item .ascribe-accordion-list-item-edition-widget'));
|
||||
// Wait for editions to load
|
||||
actions.waitForElementToShow('.ascribe-accordion-list-item-table', TIMEOUTS.LONG);
|
||||
})
|
||||
|
||||
gemini.suite('Collection placeholder', (collectionPlaceholderSuite) => {
|
||||
collectionPlaceholderSuite
|
||||
.setCaptureElements('.ascribe-accordion-list-placeholder')
|
||||
.capture('collection empty search', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-piece-list-toolbar .search-bar input[type="text"]'), 'no search result');
|
||||
actions.waitForElementToShow('.ascribe-accordion-list-placeholder', TIMEOUTS.NORMAL);
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('PieceListBulkModal', (pieceListBulkModalSuite) => {
|
||||
pieceListBulkModalSuite
|
||||
.setCaptureElements('.piece-list-bulk-modal')
|
||||
.capture('items selected', (actions, find) => {
|
||||
actions.click(find('.ascribe-accordion-list-item .ascribe-accordion-list-item-edition-widget'));
|
||||
// Wait for editions to load
|
||||
actions.waitForElementToShow('.ascribe-accordion-list-item-table', TIMEOUTS.NORMAL);
|
||||
|
||||
actions.click('.ascribe-table thead tr input[type="checkbox"]');
|
||||
actions.waitForElementToShow('.piece-list-bulk-modal');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('PieceListToolbar', (pieceListToolbarSuite) => {
|
||||
pieceListToolbarSuite
|
||||
.setCaptureElements('.ascribe-piece-list-toolbar')
|
||||
.capture('piece list toolbar')
|
||||
.capture('piece list toolbar search filled', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-piece-list-toolbar .search-bar input[type="text"]'), 'search text');
|
||||
actions.waitForElementToShow('.ascribe-piece-list-toolbar .search-bar .icon-ascribe-search', TIMEOUTS.NORMAL);
|
||||
})
|
||||
|
||||
gemini.suite('Order widget dropdown', (pieceListToolbarOrderWidgetSuite) => {
|
||||
pieceListToolbarOrderWidgetSuite
|
||||
.setCaptureElements('#ascribe-piece-list-toolbar-order-widget-dropdown',
|
||||
'#ascribe-piece-list-toolbar-order-widget-dropdown ~ .dropdown-menu')
|
||||
.capture('expanded order dropdown', (actions, find) => {
|
||||
actions.click(find('#ascribe-piece-list-toolbar-order-widget-dropdown'));
|
||||
|
||||
// Wait as the dropdown screenshot still includes the collection in the background
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Filter widget dropdown', (pieceListToolbarFilterWidgetSuite) => {
|
||||
pieceListToolbarFilterWidgetSuite
|
||||
.setCaptureElements('#ascribe-piece-list-toolbar-filter-widget-dropdown',
|
||||
'#ascribe-piece-list-toolbar-filter-widget-dropdown ~ .dropdown-menu')
|
||||
.capture('expanded filter dropdown', (actions, find) => {
|
||||
actions.click(find('#ascribe-piece-list-toolbar-filter-widget-dropdown'));
|
||||
|
||||
// Wait as the dropdown screenshot still includes the collection in the background
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Register work', (registerSuite) => {
|
||||
registerSuite
|
||||
.setUrl('/register_piece')
|
||||
.capture('register work', (actions, find) => {
|
||||
// The uploader options are only rendered after the user is fetched, so
|
||||
// we have to wait for it here
|
||||
actions.waitForElementToShow('.file-drag-and-drop-dialog .present-options', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('register work filled', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name="artist_name"]'), 'artist name');
|
||||
actions.sendKeys(find('.ascribe-form input[name="title"]'), 'title');
|
||||
actions.sendKeys(find('.ascribe-form input[name="date_created"]'), 'date created');
|
||||
})
|
||||
.capture('register work filled with editions', (actions, find) => {
|
||||
actions.click(find('.ascribe-form input[type="checkbox"] ~ .checkbox'));
|
||||
actions.wait(500);
|
||||
actions.sendKeys(find('.ascribe-form input[name="num_editions"]'), '50');
|
||||
});
|
||||
|
||||
gemini.suite('Register work hash', (registerHashSuite) => {
|
||||
registerHashSuite
|
||||
.setUrl('/register_piece?method=hash')
|
||||
.capture('register work hash method');
|
||||
});
|
||||
|
||||
gemini.suite('Register work upload', (registerUploadSuite) => {
|
||||
registerUploadSuite
|
||||
.setUrl('/register_piece?method=upload')
|
||||
.capture('register work upload method');
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('User settings', (userSettingsSuite) => {
|
||||
userSettingsSuite
|
||||
.setUrl('/settings')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins unless that suite
|
||||
// also defines a `.before()`
|
||||
actions.waitForElementToShow('.settings-container', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('user settings');
|
||||
});
|
||||
|
||||
// Suite just to log out after suites have run
|
||||
gemini.suite('Log out', (logoutSuite) => {
|
||||
logoutSuite
|
||||
.setUrl('/logout')
|
||||
.ignoreElements('.ascribe-body')
|
||||
.capture('logout', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-login-wrapper', TIMEOUTS.LONG);
|
||||
});
|
||||
});
|
||||
});
|
148
test/gemini/tests/main/basic.js
Normal file
148
test/gemini/tests/main/basic.js
Normal file
@ -0,0 +1,148 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../environment');
|
||||
const MAIN_USER = environment.MAIN_USER;
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Basic suite of tests against routes that do not require the user to be authenticated.
|
||||
*/
|
||||
gemini.suite('Basic', (suite) => {
|
||||
suite
|
||||
.setUrl('/login')
|
||||
.setCaptureElements('.ascribe-body')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins unless that suite
|
||||
// also defines a `.before()`
|
||||
// FIXME: use a more generic class for this, like just '.ascribe-app'
|
||||
actions.waitForElementToShow('.ascribe-default-app', TIMEOUTS.NORMAL);
|
||||
});
|
||||
|
||||
gemini.suite('Header-desktop', (headerSuite) => {
|
||||
headerSuite
|
||||
.setCaptureElements('nav.navbar .container')
|
||||
.skip(/Mobile/)
|
||||
.capture('desktop header', (actions, find) => {
|
||||
actions.waitForElementToShow('nav.navbar .container', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('hover on active item', (actions, find) => {
|
||||
const activeItem = find('nav.navbar li.active');
|
||||
actions.mouseMove(activeItem);
|
||||
})
|
||||
.capture('hover on inactive item', (actions, find) => {
|
||||
const inactiveItem = find('nav.navbar li:not(.active)');
|
||||
actions.mouseMove(inactiveItem);
|
||||
});
|
||||
});
|
||||
|
||||
// Test for the collapsed header in mobile
|
||||
gemini.suite('Header-mobile', (headerMobileSuite) => {
|
||||
headerMobileSuite
|
||||
.setCaptureElements('nav.navbar .container')
|
||||
.skip(/Desktop/)
|
||||
.capture('mobile header', (actions, find) => {
|
||||
actions.waitForElementToShow('nav.navbar .container', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('expanded mobile header', (actions, find) => {
|
||||
actions.click(find('nav.navbar .navbar-toggle'));
|
||||
// Wait for the header to expand
|
||||
actions.wait(500);
|
||||
})
|
||||
.capture('hover on expanded mobile header item', (actions, find) => {
|
||||
actions.mouseMove(find('nav.navbar li'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Footer', (footerSuite) => {
|
||||
footerSuite
|
||||
.setCaptureElements('.ascribe-footer')
|
||||
.capture('footer', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-footer', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('hover on footer item', (actions, find) => {
|
||||
const footerItem = find('.ascribe-footer a:not(.social)');
|
||||
actions.mouseMove(footerItem);
|
||||
})
|
||||
.capture('hover on footer social item', (actions, find) => {
|
||||
const footerSocialItem = find('.ascribe-footer a.social')
|
||||
actions.mouseMove(footerSocialItem);
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Login', (loginSuite) => {
|
||||
loginSuite
|
||||
.capture('login', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('hover on login submit', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-form button[type=submit]'));
|
||||
})
|
||||
.capture('hover on sign up link', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-login-text a[href="/signup"]'));
|
||||
})
|
||||
.capture('login form filled with focus', (actions, find) => {
|
||||
const emailInput = find('.ascribe-form input[name=email]');
|
||||
|
||||
// Remove hover from sign up link
|
||||
actions.click(emailInput);
|
||||
|
||||
actions.sendKeys(emailInput, MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password]'), MAIN_USER.password);
|
||||
})
|
||||
.capture('login form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-form-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Sign up', (signUpSuite) => {
|
||||
signUpSuite
|
||||
.setUrl('/signup')
|
||||
.capture('sign up', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('sign up form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name=email]'), MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password]'), MAIN_USER.password);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password_confirm]'), MAIN_USER.password);
|
||||
})
|
||||
.capture('sign up form filled with check', (actions, find) => {
|
||||
actions.click(find('.ascribe-form input[type="checkbox"] ~ .checkbox'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Password reset', (passwordResetSuite) => {
|
||||
passwordResetSuite
|
||||
.setUrl('/password_reset')
|
||||
.capture('password reset', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('password reset form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name="email"]'), MAIN_USER.email);
|
||||
})
|
||||
.capture('password reset form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-form-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Coa verify', (coaVerifySuite) => {
|
||||
coaVerifySuite
|
||||
.setUrl('/coa_verify')
|
||||
.capture('coa verify', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('coa verify form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name="message"]'), 'sample text');
|
||||
actions.sendKeys(find('.ascribe-form .ascribe-property-wrapper:nth-of-type(2) textarea'), 'sample signature');
|
||||
})
|
||||
.capture('coa verify form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-login-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Not found', (notFoundSuite) => {
|
||||
notFoundSuite
|
||||
.setUrl('/not_found_page')
|
||||
.capture('not found page');
|
||||
});
|
||||
});
|
134
test/gemini/tests/main/detail.js
Normal file
134
test/gemini/tests/main/detail.js
Normal file
@ -0,0 +1,134 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../environment');
|
||||
const MAIN_USER = environment.MAIN_USER;
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
const pieceUrl = `/pieces/${environment.MAIN_PIECE_ID}`;
|
||||
const editionUrl = `/editions/${environment.MAIN_EDITION_ID}`;
|
||||
|
||||
/**
|
||||
* Suite of tests against the piece and edition routes.
|
||||
* Tests include accessing the piece / edition as the owner or as another user
|
||||
* (we can just use an anonymous user in this case).
|
||||
*/
|
||||
gemini.suite('Work detail', (suite) => {
|
||||
suite
|
||||
.setCaptureElements('.ascribe-body')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins unless that suite
|
||||
// also defines a `.before()`
|
||||
// FIXME: use a more generic class for this, like just '.app',
|
||||
// when we can use this file with the whitelabels
|
||||
actions.waitForElementToShow('.ascribe-default-app', TIMEOUTS.NORMAL);
|
||||
|
||||
// Wait for the social media buttons to appear
|
||||
actions.waitForElementToShow('.ascribe-social-button-list .fb-share-button iframe', TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
|
||||
actions.waitForElementToShow('.ascribe-social-button-list .twitter-share-button', TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
|
||||
actions.waitForElementToShow('.ascribe-media-player', TIMEOUTS.LONG);
|
||||
});
|
||||
|
||||
gemini.suite('Basic piece', (basicPieceSuite) => {
|
||||
basicPieceSuite
|
||||
.setUrl(pieceUrl)
|
||||
.capture('basic piece')
|
||||
|
||||
gemini.suite('Shmui', (shmuiSuite) => {
|
||||
shmuiSuite.
|
||||
setCaptureElements('.shmui-wrap')
|
||||
.capture('shmui', (actions, find) => {
|
||||
actions.click(find('.ascribe-media-player'));
|
||||
actions.waitForElementToShow('.shmui-wrap:not(.loading)', TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
|
||||
// Wait for the transition to end
|
||||
actions.wait(1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Basic edition', (basicEditionSuite) => {
|
||||
basicEditionSuite
|
||||
.setUrl(editionUrl)
|
||||
.capture('basic edition');
|
||||
});
|
||||
|
||||
// Suite just to log us in before any other suites run
|
||||
gemini.suite('Login', (loginSuite) => {
|
||||
loginSuite
|
||||
.setUrl('/login')
|
||||
.ignoreElements('.ascribe-body')
|
||||
.before((actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-default-app', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('logged in', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-login-wrapper input[name=email]'), MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-login-wrapper input[name=password]'), MAIN_USER.password);
|
||||
actions.click(find('.ascribe-login-wrapper button[type=submit]'));
|
||||
|
||||
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Authorized piece', (authorizedPieceSuite) => {
|
||||
authorizedPieceSuite
|
||||
.setUrl(pieceUrl)
|
||||
.capture('authorized piece');
|
||||
});
|
||||
|
||||
gemini.suite('Authorized edition', (authorizedEditionSuite) => {
|
||||
authorizedEditionSuite
|
||||
.setUrl(editionUrl)
|
||||
.capture('authorized edition')
|
||||
});
|
||||
|
||||
gemini.suite('Detail action buttons', (detailActionButtonSuite) => {
|
||||
detailActionButtonSuite
|
||||
.setUrl(editionUrl)
|
||||
.capture('hover on action button', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-detail-property .ascribe-button-list button.btn-default'));
|
||||
})
|
||||
.capture('hover on delete button', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-detail-property .ascribe-button-list button.btn-tertiary'));
|
||||
})
|
||||
.capture('hover on info button', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-detail-property .ascribe-button-list button.glyphicon-question-sign'));
|
||||
})
|
||||
.capture('expand info text', (actions, find) => {
|
||||
actions.click(find('.ascribe-detail-property .ascribe-button-list button.glyphicon-question-sign'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Action form modal', (actionFormModalSuite) => {
|
||||
actionFormModalSuite
|
||||
.setUrl(editionUrl)
|
||||
.setCaptureElements('.modal-dialog')
|
||||
.capture('open email form', (actions, find) => {
|
||||
// Add class names to make the action buttons easier to select
|
||||
actions.executeJS(function (window) {
|
||||
var actionButtons = window.document.querySelectorAll('.ascribe-detail-property .ascribe-button-list button.btn-default');
|
||||
for (var ii = 0; ii < actionButtons.length; ++ii) {
|
||||
if (actionButtons[ii].textContent) {
|
||||
actionButtons[ii].className += ' ascribe-action-button-' + actionButtons[ii].textContent.toLowerCase();
|
||||
}
|
||||
}
|
||||
});
|
||||
actions.click(find('.ascribe-detail-property .ascribe-button-list button.ascribe-action-button-email'));
|
||||
|
||||
// Wait for transition
|
||||
actions.wait(1000);
|
||||
});
|
||||
});
|
||||
|
||||
// Suite just to log out after suites have run
|
||||
gemini.suite('Log out', (logoutSuite) => {
|
||||
logoutSuite
|
||||
.setUrl('/logout')
|
||||
.ignoreElements('.ascribe-body')
|
||||
.before((actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-default-app', TIMEOUTS.NORMAL);
|
||||
})
|
||||
.capture('logout', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-login-wrapper', TIMEOUTS.LONG);
|
||||
});
|
||||
});
|
||||
});
|
29
test/gemini/tests/whitelabel/23vivi/23vivi.js
Normal file
29
test/gemini/tests/whitelabel/23vivi/23vivi.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../../environment');
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Suite of tests against 23vivi specific routes
|
||||
*/
|
||||
gemini.suite('23vivi', (suite) => {
|
||||
suite
|
||||
//TODO: maybe this should be changed to .ascribe-body once the PR that does this is merged
|
||||
.setCaptureElements('.ascribe-wallet-app')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins
|
||||
actions.waitForElementToShow('.ascribe-wallet-app', TIMEOUTS.NORMAL);
|
||||
});
|
||||
|
||||
gemini.suite('Landing', (landingSuite) => {
|
||||
landingSuite
|
||||
.setUrl('/')
|
||||
.capture('landing', (actions, find) => {
|
||||
// Wait for the logo to appear
|
||||
actions.waitForElementToShow('.vivi23-landing--header-logo', TIMEOUTS.LONG);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add more tests for market specific pages after authentication
|
||||
});
|
30
test/gemini/tests/whitelabel/cyland/cyland.js
Normal file
30
test/gemini/tests/whitelabel/cyland/cyland.js
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../../environment');
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Suite of tests against Cyland specific routes
|
||||
*/
|
||||
gemini.suite('Cyland', (suite) => {
|
||||
suite
|
||||
//TODO: maybe this should be changed to .ascribe-body once the PR that does this is merged
|
||||
.setCaptureElements('.ascribe-wallet-app')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins
|
||||
actions.waitForElementToShow('.ascribe-wallet-app', TIMEOUTS.NORMAL);
|
||||
});
|
||||
|
||||
gemini.suite('Landing', (landingSuite) => {
|
||||
landingSuite
|
||||
.setUrl('/')
|
||||
// Ignore Cyland's logo as it's a gif
|
||||
.ignoreElements('.cyland-landing img')
|
||||
.capture('landing', (actions, find) => {
|
||||
actions.waitForElementToShow('.cyland-landing img', TIMEOUTS.LONG);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add more tests for cyland specific pages after authentication
|
||||
});
|
98
test/gemini/tests/whitelabel/ikonotv/ikonotv.js
Normal file
98
test/gemini/tests/whitelabel/ikonotv/ikonotv.js
Normal file
@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../../environment');
|
||||
const MAIN_USER = environment.MAIN_USER;
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Suite of tests against Cyland specific routes
|
||||
*/
|
||||
gemini.suite('Ikonotv', (suite) => {
|
||||
suite
|
||||
//TODO: maybe this should be changed to .ascribe-body once the PR that does this is merged
|
||||
.setCaptureElements('.ascribe-wallet-app')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins
|
||||
actions.waitForElementToShow('.ascribe-wallet-app', TIMEOUTS.NORMAL);
|
||||
});
|
||||
|
||||
gemini.suite('Landing', (landingSuite) => {
|
||||
landingSuite
|
||||
.setUrl('/')
|
||||
// Gemini complains if we try to capture the entire app for Ikonotv's landing page for some reason
|
||||
.setCaptureElements('.ikonotv-landing')
|
||||
.setTolerance(5)
|
||||
.capture('landing', (actions, find) => {
|
||||
// Stop background animation
|
||||
actions.executeJS(function (window) {
|
||||
var landingBackground = window.document.querySelector('.client--ikonotv .route--landing');
|
||||
landingBackground.style.animation = 'none';
|
||||
landingBackground.style.webkitAnimation = 'none';
|
||||
});
|
||||
|
||||
// Wait for logo to appear
|
||||
actions.waitForElementToShow('.ikonotv-landing header img', TIMEOUTS.LONG);
|
||||
});
|
||||
});
|
||||
|
||||
// Ikono needs its own set of tests for some pre-authorization pages to wait for
|
||||
// its logo to appear
|
||||
gemini.suite('Ikonotv basic', (suite) => {
|
||||
suite
|
||||
.setCaptureElements('.ascribe-wallet-app')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins unless that suite
|
||||
// also defines a `.before()`
|
||||
// FIXME: use a more generic class for this, like just '.app',
|
||||
// when we can use this file with the whitelabels
|
||||
actions.waitForElementToShow('.ascribe-wallet-app', TIMEOUTS.NORMAL);
|
||||
|
||||
// Wait for the forms to appear
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
|
||||
// Just use a dumb wait because the logo is set as a background image
|
||||
actions.wait(TIMEOUTS.SHORT);
|
||||
});
|
||||
|
||||
gemini.suite('Login', (loginSuite) => {
|
||||
loginSuite
|
||||
.setUrl('/login')
|
||||
.capture('login')
|
||||
.capture('hover on login submit', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-form button[type=submit]'));
|
||||
})
|
||||
.capture('hover on sign up link', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-login-text a[href="/signup"]'));
|
||||
})
|
||||
.capture('login form filled with focus', (actions, find) => {
|
||||
const emailInput = find('.ascribe-form input[name=email]');
|
||||
|
||||
// Remove hover from sign up link
|
||||
actions.click(emailInput);
|
||||
|
||||
actions.sendKeys(emailInput, MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password]'), MAIN_USER.password);
|
||||
})
|
||||
.capture('login form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-form-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Sign up', (signUpSuite) => {
|
||||
signUpSuite
|
||||
.setUrl('/signup')
|
||||
.capture('sign up')
|
||||
.capture('sign up form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name=email]'), MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password]'), MAIN_USER.password);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password_confirm]'), MAIN_USER.password);
|
||||
})
|
||||
.capture('sign up form filled with check', (actions, find) => {
|
||||
actions.click(find('.ascribe-form input[type="checkbox"] ~ .checkbox'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add more tests for ikonotv specific pages after authentication
|
||||
});
|
29
test/gemini/tests/whitelabel/lumenus/lumenus.js
Normal file
29
test/gemini/tests/whitelabel/lumenus/lumenus.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../../environment');
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Suite of tests against lumenus specific routes
|
||||
*/
|
||||
gemini.suite('Lumenus', (suite) => {
|
||||
suite
|
||||
//TODO: maybe this should be changed to .ascribe-body once the PR that does this is merged
|
||||
.setCaptureElements('.ascribe-wallet-app')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins
|
||||
actions.waitForElementToShow('.ascribe-wallet-app', TIMEOUTS.NORMAL);
|
||||
});
|
||||
|
||||
gemini.suite('Landing', (landingSuite) => {
|
||||
landingSuite
|
||||
.setUrl('/')
|
||||
.capture('landing', (actions, find) => {
|
||||
// Wait for the logo to appear
|
||||
actions.waitForElementToShow('.wp-landing-wrapper img', TIMEOUTS.LONG);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add more tests for market specific pages after authentication
|
||||
});
|
115
test/gemini/tests/whitelabel/shared/whitelabel_basic.js
Normal file
115
test/gemini/tests/whitelabel/shared/whitelabel_basic.js
Normal file
@ -0,0 +1,115 @@
|
||||
'use strict';
|
||||
|
||||
const gemini = require('gemini');
|
||||
const environment = require('../../environment');
|
||||
const MAIN_USER = environment.MAIN_USER;
|
||||
const TIMEOUTS = environment.TIMEOUTS;
|
||||
|
||||
/**
|
||||
* Basic suite of tests against whitelabel routes that do not require authentication.
|
||||
*/
|
||||
gemini.suite('Whitelabel basic', (suite) => {
|
||||
suite
|
||||
.setCaptureElements('.ascribe-wallet-app > .container')
|
||||
.before((actions, find) => {
|
||||
// This will be called before every nested suite begins unless that suite
|
||||
// also defines a `.before()`
|
||||
// FIXME: use a more generic class for this, like just '.ascribe-app'
|
||||
actions.waitForElementToShow('.ascribe-wallet-app', TIMEOUTS.NORMAL);
|
||||
|
||||
// Use a dumb wait in case we're still waiting for other assets, like fonts, to load
|
||||
actions.wait(1000);
|
||||
});
|
||||
|
||||
gemini.suite('Login', (loginSuite) => {
|
||||
loginSuite
|
||||
.setUrl('/login')
|
||||
// See Ikono
|
||||
.skip(/Ikono/)
|
||||
.capture('login', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
// For some reason, the screenshots seem to keep catching the whitelabel login form
|
||||
// on a refresh and without fonts loaded (maybe because they're the first tests run
|
||||
// and the cache isn't hot yet?).
|
||||
// Let's wait a bit and hope they load.
|
||||
actions.wait(TIMEOUTS.SHORT);
|
||||
})
|
||||
.capture('hover on login submit', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-form button[type=submit]'));
|
||||
})
|
||||
.capture('hover on sign up link', (actions, find) => {
|
||||
actions.mouseMove(find('.ascribe-login-text a[href="/signup"]'));
|
||||
})
|
||||
.capture('login form filled with focus', (actions, find) => {
|
||||
const emailInput = find('.ascribe-form input[name=email]');
|
||||
|
||||
// Remove hover from sign up link
|
||||
actions.click(emailInput);
|
||||
|
||||
actions.sendKeys(emailInput, MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password]'), MAIN_USER.password);
|
||||
})
|
||||
.capture('login form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-form-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Sign up', (signUpSuite) => {
|
||||
signUpSuite
|
||||
.setUrl('/signup')
|
||||
// See Ikono
|
||||
.skip(/Ikono/)
|
||||
.capture('sign up', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
// Wait in case the form reloads due to other assets loading
|
||||
actions.wait(500);
|
||||
})
|
||||
.capture('sign up form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name=email]'), MAIN_USER.email);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password]'), MAIN_USER.password);
|
||||
actions.sendKeys(find('.ascribe-form input[name=password_confirm]'), MAIN_USER.password);
|
||||
})
|
||||
.capture('sign up form filled with check', (actions, find) => {
|
||||
actions.click(find('.ascribe-form input[type="checkbox"] ~ .checkbox'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Password reset', (passwordResetSuite) => {
|
||||
passwordResetSuite
|
||||
.setUrl('/password_reset')
|
||||
.capture('password reset', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
// Wait in case the form reloads due to other assets loading
|
||||
actions.wait(500);
|
||||
})
|
||||
.capture('password reset form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name="email"]'), MAIN_USER.email);
|
||||
})
|
||||
.capture('password reset form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-form-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Coa verify', (coaVerifySuite) => {
|
||||
coaVerifySuite
|
||||
.setUrl('/coa_verify')
|
||||
.capture('coa verify', (actions, find) => {
|
||||
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
|
||||
// Wait in case the form reloads due to other assets loading
|
||||
actions.wait(500);
|
||||
})
|
||||
.capture('coa verify form filled with focus', (actions, find) => {
|
||||
actions.sendKeys(find('.ascribe-form input[name="message"]'), 'sample text');
|
||||
actions.sendKeys(find('.ascribe-form .ascribe-property-wrapper:nth-of-type(2) textarea'), 'sample signature');
|
||||
})
|
||||
.capture('coa verify form filled', (actions, find) => {
|
||||
actions.click(find('.ascribe-login-header'));
|
||||
});
|
||||
});
|
||||
|
||||
gemini.suite('Not found', (notFoundSuite) => {
|
||||
notFoundSuite
|
||||
.setUrl('/not_found_page')
|
||||
.capture('not found page');
|
||||
});
|
||||
});
|
@ -5,7 +5,7 @@ const wd = require('wd');
|
||||
const asserters = wd.asserters; // Commonly used asserters for async waits in the browser
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
const config = require('./config.js');
|
||||
const config = require('../config.js');
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chai.should();
|
Loading…
Reference in New Issue
Block a user