chore(e2e): remove old e2e testing

This commit is contained in:
Adam Bradley
2018-09-20 13:20:35 -05:00
parent 43c481e3f3
commit 6b87ead2dc
5 changed files with 0 additions and 516 deletions

View File

@ -1,122 +0,0 @@
# End-To-End Testing Scripts
This document describes the process of installing the dependencies for, running, and writing end-to-end tests for ionic core. Your working directory is assumed to be `core`.
---
## Dependencies
Before you proceed, make sure you're running [**Node 7.6.0+**](https://nodejs.org/en/download/):
```sh
node -v # Should be >= 7.6.0
```
And that you've installed all packages:
```sh
npm install
```
## Running The Tests
To run the end-to-end tests:
```
npm run e2e
```
## Writing an End-To-End Test
To create an end-to-end test, you'll need to create a directory containing your test page and the tests themselves. Create a directory in your component's `test` directory. That directory should contain an `index.html` and an `e2e.js` file. So, if I were writing a test called `basic` for the `button` component:
```
button
└── test/
└── basic/
├── e2e.js
└── index.html
```
In your `e2e.js` file, you can group tests together using [Mocha](https://mochajs.org/)'s `describe` function:
```js
describe('button: basic', () => {
// Write tests here.
});
```
To register a test, use the `register` method from `scripts/e2e`. The `register` function takes two arguments, a description and a callback. The callback is passed the test [driver](https://www.npmjs.com/package/selenium-webdriver) as its only argument. For async actions, simply return a Promise from your callback.
```js
const { register } = require('../../../../../scripts/e2e');
describe('button: basic', () => {
register('my test', driver => {
// Use the driver here.
});
});
```
The most basic, and most common, test simply navigates to your page to ensure it renders properly. For more complicated cases, you may need to extend the `Page` class provided by the e2e module.
### Simple Navigation Tests
To write a simple navigation test, you can use the `navigate` function from the e2e module:
```js
const { register, navigate } = require('../../../../../scripts/e2e');
describe('button: basic', () => {
register('navigates', navigate('http://localhost:3333/src/components/button/test/basic'));
});
```
### Extending The `Page` Class
For more complicated tests, you may need to extend the `Page` class:
```js
const { register, Page } = require('../../../../scripts/e2e');;
class ButtonTestPage extends Page {
constructor(driver) {
super(driver, 'http://localhost:3333/src/components/button/test/basic');
}
someMethod() {
// ...
}
}
describe('button: basic', () => {
register('some test', driver => {
const page = new ButtonTestPage(driver);
return page.someMethod();
});
});
```
## Snapshot
You can also take snapshots of each end-to-end test to check for visual regressions. You'll need to export the `IONIC_SNAPSHOT_KEY` environment variable to upload to the snapshot app. Ask someone from Ionic for the key.
**Snapshot compares a base snapshot made on MacOS with a retina screen. (2560x1600) It does not work for Windows, Linux, or non-retina Macs.**
To take snapshots:
```bash
npm run snapshot
```
To take snapshots of a specific folder:
```bash
npm run snapshot -- -f=toast
```
## TODO
- [ ] Move this script up a directory and use for all packages?
- [ ] Turn off animations and then adjust the wait time accordingly
- [ ] Adjustments will likely be needed when the Snapshot tool has better reporting, for example the tool will likely have `start` and `finish` methods (or some such thing)

View File

@ -1,205 +0,0 @@
'use strict';
const fs = require('fs');
const glob = require('glob');
const Mocha = require('mocha');
const path = require('path');
const webdriver = require('selenium-webdriver');
const argv = require('yargs').argv;
const Page = require('./page');
const Snapshot = require('./snapshot');
const platforms = ['md', 'ios'];
let driver;
let snapshot;
let specIndex = 0;
let takeScreenshots = false;
let folder = null;
function startDevServer() {
const server = require('@stencil/dev-server/dist'); // TODO: fix after stencil-dev-server PR #16 is merged
const cmdArgs = [
'--config',
path.join(__dirname, '../../stencil.config.js'),
'--no-open'
];
return server.run(cmdArgs);
}
function generateTestId() {
let chars = 'abcdefghjkmnpqrstuvwxyz';
let id = chars.charAt(Math.floor(Math.random() * chars.length));
chars += '0123456789';
while (id.length < 3) {
id += chars.charAt(Math.floor(Math.random() * chars.length));
}
return id;
}
function getTestFiles() {
return new Promise((resolve, reject) => {
let src = path.join(__dirname, '../../src/**/e2e.js');
if (folder) {
src = path.join(__dirname, `../../src/**/${folder}/**/e2e.js`);
}
glob(src, (err, files) => {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
}
function startDriver() {
// setting chrome options to start the browser without an info bar
let chromeCapabilities = webdriver.Capabilities.chrome();
const chromeOptions = {
// 'args': ['--disable-infobars']
};
chromeCapabilities.set('chromeOptions', chromeOptions);
return new webdriver.Builder()
.withCapabilities(chromeCapabilities)
.forBrowser('chrome')
.build();
}
function processCommandLine() {
if (argv.snapshot) {
takeScreenshots = true;
}
if (argv.f || argv.folder) {
folder = argv.f || argv.folder;
}
}
function registerE2ETest(desc, tst) {
// NOTE: Do not use an arrow function here because: https://mochajs.org/#arrow-functions
it(desc, async function() {
await tst(driver, this);
if (takeScreenshots) {
await snapshot.takeScreenshot(driver, {
name: this.test.fullTitle().replace(/(^[\w-]+\/[\w-]+)/, '$1:'),
specIndex: specIndex++
});
}
});
}
function getTotalTests(suite) {
let ttl = suite.tests.length;
suite.suites.forEach(s => (ttl += getTotalTests(s)));
return ttl;
}
async function run() {
// TODO look into removing chrome startup from the timeout
const mocha = new Mocha({
timeout: 5000,
slow: 2000
});
driver = startDriver();
processCommandLine();
const devServer = await startDevServer();
const files = await getTestFiles();
files.forEach(f => mocha.addFile(f));
snapshot = await mochaLoadFiles(mocha);
const failures = await mochaRun(mocha);
if (takeScreenshots) {
snapshot.finish();
}
devServer.close();
await driver.quit();
if (failures) {
throw new Error(failures);
}
}
function mochaRun(mocha) {
return new Promise((resolve, reject) => {
mocha.run(function(failures) {
resolve(failures);
});
});
}
function mochaLoadFiles(mocha) {
return new Promise((resolve, reject) => {
mocha.loadFiles(() => {
specIndex = 0;
snapshot = new Snapshot({
groupId: 'ionic-core',
appId: 'snapshots',
testId: generateTestId(),
domain: 'ionic-snapshot-go.appspot.com',
// domain: 'localhost:8080',
sleepBetweenSpecs: 750,
totalSpecs: getTotalTests(mocha.suite),
platformDefaults: {
browser: 'chrome',
platform: 'linux',
params: {
platform_id: 'chrome_400x800',
platform_index: 0,
platform_count: 1,
width: 400,
height: 814
}
},
accessKey: process.env.IONIC_SNAPSHOT_KEY
});
resolve(snapshot);
});
});
}
function parseSemver(str) {
return /(\d+)\.(\d+)\.(\d+)/
.exec(str)
.slice(1)
.map(Number);
}
function validateNodeVersion(version) {
const [major, minor] = parseSemver(version);
if (major < 7 || (major === 7 && minor < 6)) {
throw new Error(
'Running the end-to-end tests requires Node version 7.6.0 or higher.'
);
}
}
// Invoke run() only if executed directly i.e. `node ./scripts/e2e`
if (require.main === module) {
validateNodeVersion(process.version);
run()
.then(() => {})
.catch(err => {
console.log(err);
// fail with non-zero status code
process.exit(1);
});
}
module.exports = {
Page,
platforms: platforms,
register: registerE2ETest,
run: run
};

View File

@ -1,25 +0,0 @@
const webdriver = require('selenium-webdriver');
const By = webdriver.By;
const until = webdriver.until;
module.exports = class E2ETestPage {
constructor(driver, url) {
this.url = url;
this.driver = driver;
}
async navigate(tagName = '') {
this.driver.navigate().to(this.url);
this.driver.manage().timeouts().implicitlyWait(10000);
await this.driver.wait(until.elementLocated(By.css(`.hydrated`)));
const tag = tagName || '.hydrated';
return await this.driver.wait(until.elementIsVisible(this.driver.findElement(By.css(tag))));
}
async present(clickTarget, options) {
await this.navigate(clickTarget);
this.driver.findElement(By.css(clickTarget)).click();
await this.driver.wait(until.elementLocated(By.css(options.waitFor)));
return await this.driver.wait(until.elementIsVisible(this.driver.findElement(By.css(options.waitFor))));
}
}

View File

@ -1,137 +0,0 @@
'use strict';
const request = require('request');
class Snapshot {
constructor(options) {
this.appId = (options && options.appId) || 'test_app';
this.domain = (options && options.domain) || 'localhost:8080';
this.groupId = (options && options.groupId) || 'test_group';
this.sleepTime = (options && options.sleepBetweenSpecs) || 500;
this.totalSpecs = options && options.totalSpecs;
this.accessKey = options && options.accessKey;
this.platformId =
options && options.platformDefaults && options.platformDefaults.params && options.platformDefaults.params.platform_id;
this.platformIndex =
options && options.platformDefaults && options.platformDefaults.params && options.platformDefaults.params.platform_index;
this.platformCount =
options && options.platformDefaults && options.platformDefaults.params && options.platformDefaults.params.platform_count;
this.width =
(options && options.platformDefaults && options.platformDefaults.params && options.platformDefaults.params.width) || -1;
this.height =
(options && options.platformDefaults && options.platformDefaults.params && options.platformDefaults.params.height) || -1;
this.testId = (options && options.testId);
this.queue = [];
this.highestMismatch = 0;
this.mismatches = [];
this.results = {};
}
async finish() {
console.log('waiting for uploads to complete');
await Promise.all(this.queue);
console.log(`done processing ${this.queue.length} screenshots`);
console.log(`${this.mismatches.length} snapshots had significant mismatches`);
console.log(`Test Id: ${this.testId}`);
console.log(`URL: https://${this.domain}/${this.groupId}/${this.appId}/${this.testId}`);
}
async takeScreenshot(driver, options) {
this._resizeWindow(driver);
await this._allowForAnnimation();
const screenshot = await this._takeScreenshot(driver, options);
return this._post(screenshot);
}
_allowForAnnimation() {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve();
}, this.sleepTime);
});
}
_post(screenshot) {
const p = new Promise((resolve, reject) => {
request.post(`http://${this.domain}/screenshot`, { form: screenshot }, (error, res, body) => {
if (error) {
console.error(error);
} else if (res.statusCode > 400) {
console.log('error posting screenshot:', response.statusCode, body);
} else {
const data = JSON.parse(body);
this.highestMismatch = Math.max(this.highestMismatch, data.Mismatch);
const resultKey = (data.Mismatch * 1000 + 1000000 + '').split('.')[0] + '-' + screenshot.spec_index;
this.results[resultKey] = {
index: screenshot.spec_index,
name: screenshot.description,
mismatch: Math.round(data.Mismatch * 100) / 100,
compareUrl: data.CompareUrl,
screenshotUrl: data.ScreenshotUrl
};
if (data.IsMismatch) {
this.mismatches.push(resultKey);
}
}
resolve(res);
});
});
this.queue.push(p);
return Promise.resolve(true);
}
_resizeWindow(driver) {
return driver
.manage()
.window()
.setSize(this.width, this.height);
}
_getQueryString(field, url) {
var reg = new RegExp( '[?&]' + field + '=([^&#]*)', 'i' );
var string = reg.exec(url);
return string ? string[1] : null;
};
async _takeScreenshot(driver, options) {
const capabilities = await driver.getCapabilities();
const png = await driver.takeScreenshot();
let url = await driver.getCurrentUrl();
// TODO remove the modified url/description once we're happy with the comparison to v3
let platform = 'android';
if (url.indexOf('ionic:mode') > -1) {
platform = this._getQueryString('ionic:mode', url);
}
let replacedUrl = url.replace('3333', '8876').replace('src/components', '/e2e').replace('test/', '').replace(`?ionic:mode=${platform}`, '');
url = replacedUrl + `/index.html?ionic:mode=${platform}&ionicOverlayCreatedDiff=0&snapshot=true`;
let description = options.name.replace(': ', `: ${platform} `) + '.';
return Promise.resolve({
app_id: this.appId,
group_id: this.groupId,
description: description,
spec_index: options.specIndex,
total_specs: this.totalSpecs,
test_id: this.testId,
url: url,
png_base64: png,
height: this.height,
width: this.width,
platform_count: this.platformCount,
platform_id: this.platformId,
platform_index: this.platformIndex,
browser: capabilities.get('browserName'),
platform: capabilities.get('platform'),
version: capabilities.get('version'),
access_key: this.accessKey
});
}
}
module.exports = Snapshot;

View File

@ -1,27 +0,0 @@
const { By, until } = require('selenium-webdriver');
async function getElement(driver, selector) {
driver.wait(until.elementLocated(By.css(selector)));
const element = driver.findElement(By.css(selector));
await driver.wait(until.elementIsVisible(driver.findElement(By.css(selector))));
return element;
}
async function waitAndGetElementById(driver, selector) {
driver.wait(until.elementLocated(By.id(selector)));
const element = driver.findElement(By.id(selector));
await driver.wait(until.elementIsVisible(driver.findElement(By.id(selector))));
return element;
}
function waitForTransition(duration) {
return new Promise(resolve => {
setTimeout(resolve, duration);
})
}
module.exports = {
getElement: getElement,
waitForTransition: waitForTransition,
waitAndGetElementById: waitAndGetElementById
}