Initial commit adding integration tests. (#1557)

* Initial commit adding integration tests.

* Fix test name.

* Adding package-lock.

* Some updates.

* More updates.

* Extend page comparison max to 9.99%

* Removed node assert statement.

* Updated baseline images and tests.

* Added docs, fixed docker configuration file.

* Updates.

* Update docker compose.

* Fix json error.

* Add comment to Dockerfile.

* Comment to trigger CI.

* Moved dependencies to allow for docker image build.

* Add restore command.

* Fix restore command.

* Change docker image.

* Add checkout command.

* Add java.

* Add node version install.

* Trying machine executor.

* Fix typo.:

* Remove old docker command.

* EVERYTHING IN ONE SHELL.

* Trying nvm.

* Fix typo.

* Fix node version...

* Fix typo.

* More node stuff.

* Updates to docker compose.

* Add chown to circleci config.

* Fix docker login error.

* Change exec command user.

* Add screenshots for debugging.

* Try fixing js command.

* Try fixing js command again.

* Change screenshot path

* Add mkdir for error shots.

* Trying something for postgres.

* Updates.

* Fix lint error.

* Try fixing errorshots.

* Updates before rebase.

* Updated baseline for new homepage.

* Remove baseline image save.

* Trying a change for errorshots.

* Update test.
This commit is contained in:
Benjamin Forehand Jr 2020-04-03 14:48:07 -05:00 коммит произвёл GitHub
Родитель d252cc2a29
Коммит b19eff6d4a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
27 изменённых файлов: 5003 добавлений и 4312 удалений

Просмотреть файл

@ -49,6 +49,28 @@ jobs:
name: Test Code
command: docker run blurts-server npm run lint
integration-test:
machine:
image: ubuntu-1604:201903-01
steps:
- checkout
- run:
name: Build and run tests.
command: |
export NVM_DIR="/opt/circleci/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm use 10
node -v
cp .env-dist .env
npm install
npm install --only=dev
sudo chown -R $USER:$USER .
mkdir ./tests/integration/errorShots
docker-compose -f tests/integration/docker-compose.yml up --build -d
docker-compose -f tests/integration/docker-compose.yml exec --user root firefox npm run test:integration
- store_artifacts:
path: ./tests/integration/errorShots/
deploy:
docker:
- image: docker:18.02.0-ce
@ -98,7 +120,6 @@ workflows:
filters:
tags:
only: /.*/
- deploy:
requires:
- build
@ -111,3 +132,7 @@ workflows:
filters:
branches:
only: master
- integration-test:
requires:
- build

Просмотреть файл

@ -41,19 +41,20 @@ BASKET_NEWSLETTER=mozilla-and-you
# leave FXA_ENABLED empty to disable FXA
FXA_ENABLED=
FXA_SETTINGS_URL="https://stable.dev.lcip.org/settings"
OAUTH_CLIENT_ID=cb1bef9d06bb9bc9
OAUTH_CLIENT_SECRET=f5fb99de6e0af18ab17e013ac1d439903179a97a1c510fc10bc3bd50bbce089b
OAUTH_CLIENT_ID=edd29a80019d61a1
OAUTH_CLIENT_SECRET=a80feaad77c847275d39ac989ee12a873ef6b54cbc184128f86f2afecdf003b5
OAUTH_AUTHORIZATION_URI="https://oauth-stable.dev.lcip.org/v1/authorization"
OAUTH_PROFILE_URI="https://stable.dev.lcip.org/profile/v1/profile"
OAUTH_TOKEN_URI="https://oauth-stable.dev.lcip.org/v1/token"
# HIBP API for breach data
HIBP_API_ROOT="https://stage.haveibeenpwned.com/api/v2/"
# How many seconds to wait before refreshing upstream breach data from HIBP
HIBP_RELOAD_BREACHES_TIMER=600
# HIBP API for range search and subscription
HIBP_KANON_API_ROOT="https://api.haveibeenpwned.com"
HIBP_KANON_API_TOKEN=""
HIBP_KANON_API_TOKEN=
HIBP_API_ROOT="https://haveibeenpwned.com/api/v2"
HIBP_API_TOKEN=
# How many milliseconds to wait before retrying an HIBP request
HIBP_THROTTLE_DELAY=2000
# Max number of times to try an HIBP request before throwing error

Просмотреть файл

@ -35,7 +35,17 @@ module.exports = {
],
env: {
jest: true,
}
},
},
{
files: [
'tests/integration/**/*.js',
],
globals: {
"$": "readonly",
"$$": "readonly",
"browser": "readonly",
},
},
],
plugins: [

Просмотреть файл

@ -12,7 +12,7 @@ USER app
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm install && rm -rf ~app/.npm /tmp/*
RUN npm install --production && rm -rf ~app/.npm /tmp/*
COPY --chown=app:app . /app

8413
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -10,9 +10,12 @@
"@sentry/node": "^4.2.3",
"arg": "^2.0.0",
"basic-auth": "^2.0.0",
"babel-minify": "^0.4.3",
"body-parser": "^1.18.3",
"clean-css-cli": "^4.2.1",
"client-oauth2": "^4.2.1",
"client-sessions": "^0.8.0",
"concat": "^1.0.3",
"cpr": "^3.0.1",
"csurf": "^1.9.0",
"dotenv": "^5.0.1",
@ -33,14 +36,22 @@
"mozlog": "^2.2.0",
"nodemailer": "^4.6.4",
"nodemailer-express-handlebars": "^3.0.0",
"npm-run-all": "^4.1.5",
"pg": "^7.4.3",
"sns-validator": "^0.3.4",
"uuid": "^3.2.1"
},
"devDependencies": {
"babel-minify": "^0.4.3",
"@wdio/cli": "^5.18.6",
"@wdio/dot-reporter": "^5.18.6",
"@wdio/firefox-profile-service": "^5.16.11",
"@wdio/local-runner": "^5.18.6",
"@wdio/mocha-framework": "^5.18.6",
"@wdio/selenium-standalone-service": "^5.16.10",
"@wdio/spec-reporter": "^5.18.6",
"@wdio/sync": "^5.18.5",
"chai": "^4.2.0",
"clean-css-cli": "^4.2.1",
"concat": "^1.0.3",
"coveralls": "^3.0.1",
"eslint": "^4.18.1",
"eslint-plugin-node": "^6.0.1",
@ -51,12 +62,13 @@
"node-mocks-http": "^1.7.0",
"nodemon": "^1.18.7",
"npm-audit-ci-wrapper": "^2.4.3",
"npm-run-all": "^4.1.5",
"onchange": "^6.1.0",
"postcss-cli": "^6.1.2",
"stylelint": "^9.10.1",
"stylelint-config-standard": "^18.2.0",
"uglify-js": "^3.4.9"
"uglify-js": "^3.4.9",
"wdio-docker-service": "^2.2.0",
"wdio-image-comparison-service": "^1.9.0"
},
"engines": {
"node": "10"
@ -116,6 +128,9 @@
"test:db:migrate": "NODE_ENV=tests knex migrate:latest --knexfile db/knexfile.js --env tests",
"test:tests": "NODE_ENV=tests HIBP_THROTTLE_DELAY=1000 HIBP_THROTTLE_MAX_TRIES=3 jest --runInBand --coverage tests/",
"test:coveralls": "cat ./coverage/lcov.info | coveralls",
"test:integration": "wdio tests/integration/wdio.conf.js",
"test:integration-headless": "MOZ_HEADLESS=1 wdio tests/integration/wdio.conf.js",
"test:integration-docker": "MOZ_HEADLESS=1 wdio tests/integration/wdio.docker.js",
"test": "run-s test:db:migrate test:tests test:coveralls"
}
}

182
scripts/wait-for-it.sh Normal file
Просмотреть файл

@ -0,0 +1,182 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
WAITFORIT_cmdname=${0##*/}
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
else
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
fi
WAITFORIT_start_ts=$(date +%s)
while :
do
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
WAITFORIT_result=$?
else
(echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
WAITFORIT_result=$?
fi
if [[ $WAITFORIT_result -eq 0 ]]; then
WAITFORIT_end_ts=$(date +%s)
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
break
fi
sleep 1
done
return $WAITFORIT_result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
else
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
fi
WAITFORIT_PID=$!
trap "kill -INT -$WAITFORIT_PID" INT
wait $WAITFORIT_PID
WAITFORIT_RESULT=$?
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
fi
return $WAITFORIT_RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
WAITFORIT_hostport=(${1//:/ })
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
shift 1
;;
--child)
WAITFORIT_CHILD=1
shift 1
;;
-q | --quiet)
WAITFORIT_QUIET=1
shift 1
;;
-s | --strict)
WAITFORIT_STRICT=1
shift 1
;;
-h)
WAITFORIT_HOST="$2"
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
WAITFORIT_HOST="${1#*=}"
shift 1
;;
-p)
WAITFORIT_PORT="$2"
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
WAITFORIT_PORT="${1#*=}"
shift 1
;;
-t)
WAITFORIT_TIMEOUT="$2"
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
WAITFORIT_TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
WAITFORIT_CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
# Check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
WAITFORIT_BUSYTIMEFLAG=""
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
WAITFORIT_ISBUSY=1
# Check if busybox timeout uses -t flag
# (recent Alpine versions don't support -t anymore)
if timeout &>/dev/stdout | grep -q -e '-t '; then
WAITFORIT_BUSYTIMEFLAG="-t"
fi
else
WAITFORIT_ISBUSY=0
fi
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
wait_for
WAITFORIT_RESULT=$?
exit $WAITFORIT_RESULT
else
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
wait_for_wrapper
WAITFORIT_RESULT=$?
else
wait_for
WAITFORIT_RESULT=$?
fi
fi
if [[ $WAITFORIT_CLI != "" ]]; then
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
exit $WAITFORIT_RESULT
fi
exec "${WAITFORIT_CLI[@]}"
else
exit $WAITFORIT_RESULT
fi

Просмотреть файл

@ -0,0 +1,26 @@
# Build app for integration tests
FROM node:10
RUN useradd -d /app -u 1001 -g 1000 app
RUN npm update -g
RUN rm -rf /tmp/*
# RUN apk add --no-cache bash
WORKDIR /app
RUN chown app /app
USER app
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm install && rm -rf ~app/.npm /tmp/*
COPY --chown=app:root . /app
RUN npm run build:all
CMD NODE_ICU_DATA=./node_modules/full-icu node server.js

Просмотреть файл

@ -0,0 +1,22 @@
# Firefox Monitor Integration tests
### Prerequisites
- Docker
- Docker-Compose
- [geckodriver]
- [npm]
- [nodejs]
## Firefox Setup
Make sure Firefox is in your system path if you want to watch the tests run inside your browser.
1. Follow setup instructions [here](https://github.com/mozilla/blurts-server#install).
2. Set appropriate ENV variables, HIBP_API_TOKEN, HIBP_KANON_API_TOKEN, MAILINATOR_PASSWORD
2. Build and start the containers: ```docker-compose -f tests/integration/docker-compose.yml up --build -d```.
3. Run the tests: ```npm run test:integration```.
They can also be run headlessly: ```npm run test:integration-headless```, and inside docker: ```npm run test:integration-docker```.
[geckodriver]: https://github.com/mozilla/geckodriver/releases
[npm]: https://docs.npmjs.com/
[nodejs]: https://nodejs.org/en/

Просмотреть файл

@ -0,0 +1,55 @@
# Integration test docker-compose
version: "3.7"
services:
server:
build:
context: ../..
dockerfile: tests/integration/Dockerfile.integration-test.yml
ports:
- ${PORT}:${PORT}
depends_on:
- postgres
environment:
- HIBP_KANON_API_TOKEN
- HIBP_API_TOKEN
- FXA_ENABLED=1
- LOGOS_ORIGIN=null
- BREACH_RESOLUTION_ENABLED=false
- MONITOR_FXA_PASSWORD
- DATABASE_URL=postgres://postgres@postgres:5432/blurts
links:
- postgres
postgres:
image: postgres
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_DB=blurts
- POSTGRES_HOST_AUTH_METHOD=trust
expose:
- "5432"
ports:
- "5432:5432"
migration:
image: integration_server:latest
command: bash -c "chmod +x ./scripts/wait-for-it.sh && ./scripts/wait-for-it.sh postgres:5432 -- npm run db:migrate"
environment:
- DATABASE_URL=postgres://postgres@postgres:5432/blurts
links:
- postgres
depends_on:
- postgres
firefox:
image: b4handjr/selenium-firefox:python3-latest
env_file: ../../.env
environment:
- MOZ_HEADLESS=1
- MONITOR_FXA_PASSWORD
volumes:
- ../../:/code
expose:
- "4444"
ports:
- "5900:5900"
shm_size: 2g
network_mode: host

Просмотреть файл

@ -0,0 +1,21 @@
"use strict";
class UserDashboardPage {
waitForPageToLoad() {
$("#dashboard").waitForExist(5000);
return this;
}
get addEmailBox() { return $("#email-add"); }
get verificationLink() { return $("#email-add-submit"); }
manageEmailAddresses() {
const UserPreferencesPage = require("./userPreferences.page");
$(".manage-emails").waitForExist(5000);
$(".manage-emails").click();
return UserPreferencesPage.waitForPageToLoad();
}
}
module.exports = new UserDashboardPage();

Просмотреть файл

@ -0,0 +1,29 @@
"use strict";
class HomePage {
/* Represents the Home page */
waitForPageToLoad() {
this.monitorLogo.waitForExist(5000);
return this;
}
get monitorLogo() { return $(".fx-monitor-logotype"); }
get breachCard() { return new BreachCard(); }
get breachEmailAddress() { return $("#scan-email"); }
get checkBreachesButton() {
$(".input-group-button > input:nth-child(1)").click();
const ScanResultsPage = require("./scanResults.page");
return ScanResultsPage.waitForPageToLoad();
}
}
class BreachCard {
/* Represents the Breach card region */
get latestBreachCard() { return $(".latest-breach"); }
}
module.exports = new HomePage();

Просмотреть файл

@ -0,0 +1,19 @@
/* global $$ */
/* global $ */
"use strict";
class ScanResultsPage {
waitForPageToLoad() {
$(".scan-results").waitForExist(5000);
return this;
}
get numberOfBreaches() {
return $(".headline > span:nth-child(1)").getText();
}
get breachCards() { return $$(".breach-card"); }
}
module.exports = new ScanResultsPage();

Просмотреть файл

@ -0,0 +1,13 @@
/* global $ */
"use strict";
class UserPreferencesPage {
waitForPageToLoad() {
$(".preferences").waitForExist(5000);
return this;
}
}
module.exports = new UserPreferencesPage();

Просмотреть файл

@ -0,0 +1,9 @@
"use strict";
class NavBar {
/* Represents the navbar */
get signIn() { return $("#sign-in-btn"); }
}
module.exports = new NavBar();

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 173 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 99 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 695 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 414 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.2 MiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.0 MiB

Просмотреть файл

@ -0,0 +1,17 @@
"use strict";
describe("Firefox Monitor Breaches Page", function() {
this.retries(2);
beforeEach(function() {
browser.url("/breaches");
});
it("should look like normal", function() {
expect(browser.checkFullPageScreen("Breaches_Page", {
hideElements: [
$$(".breach-info-wrapper"),
],
})).to.be.within(0, 9.99);
});
});

Просмотреть файл

@ -0,0 +1,81 @@
/* eslint-disable no-console */
"use strict";
const HomePage = require("../pages/desktop/home.page");
const NavBar = require("../regions/navbar.region");
const UserDashboardPage = require("../pages/desktop/dashboard.page");
describe("Firefox Monitor homepage", function() {
/* this.retries(2); */
beforeEach(function() {
browser.url("/");
});
it("should load the latest breach card", function() {
const homePage = HomePage;
homePage.waitForPageToLoad();
expect(homePage.breachCard.latestBreachCard.isDisplayed()).to.be.true;
});
it("should look like normal", function() {
expect(browser.checkFullPageScreen("Home_Page", {
hideElements: [
$$(".breach-info-wrapper"),
],
})).to.be.within(0, 9.99);
});
it("should load correct number of breaches from an email input", function() {
const homePage = HomePage;
homePage.waitForPageToLoad();
homePage.breachEmailAddress.setValue(global.primaryEmail);
const scanResults = homePage.checkBreachesButton;
expect(scanResults.breachCards.length)
.to
.equal(Number(scanResults.numberOfBreaches));
});
it("should allow secondary email to be added", function() {
const homePage = HomePage;
const navBar = NavBar;
homePage.waitForPageToLoad();
navBar.signIn.click();
// FxA login
// Check if fxa email is needed, if error, just continue
try {
$(".email").waitForExist(5000);
$(".email").click();
$(".email").setValue(global.primaryEmail);
$("#submit-btn").click();
} catch (error) {
console.log(error);
}
// FxA password login
$("#password").waitForExist(5000);
$("#password").click();
$("#password").setValue(global.monitorFxaPassword);
$("#submit-btn").click();
// Begin navigiation in monitor
const dashboard = UserDashboardPage.waitForPageToLoad();
dashboard.addEmailBox.setValue(global.secondaryEmail);
dashboard.verificationLink.click();
// Wait for dashboard to reload
try {
dashboard.waitForPageToLoad();
dashboard.manageEmailAddresses();
} catch (error) {
console.log(error);
}
const email = $$(".e-address")[1];
// Check email
expect(email.getText()).to.equal(global.secondaryEmail);
$(".remove-email-submit").click();
browser.refresh();
expect(email.getText()).to.equal(global.primaryEmail);
});
});

Просмотреть файл

@ -0,0 +1,13 @@
"use strict";
describe("Firefox Monitor Security Tips Page", function() {
this.retries(2);
beforeEach(function() {
browser.url("/security-tips");
});
it("should look like normal", function() {
expect(browser.checkFullPageScreen("SecurityTips_Page", {})).to.be.within(0, 5);
});
});

Просмотреть файл

@ -0,0 +1,27 @@
/*
This file is used to create a baseline image for a webpage.
*/
"use strict";
describe("Firefox Monitor homepage", function() {
this.retries(2);
beforeEach(function() {
browser.url("/");
});
it("should look like normal", function() {
browser.saveFullPageScreen("Home_Page", {
hideElements: [
$$(".breach-info-wrapper"),
],
});
expect(browser.checkFullPageScreen("Home_Page", {
hideElements: [
$$(".breach-info-wrapper"),
],
})).to.equal(0);
});
});

Просмотреть файл

@ -0,0 +1,278 @@
/* eslint-disable no-process-env */
/* eslint-disable no-undef */
/* eslint-disable strict */
const { join } = require("path");
require("dotenv").config();
exports.config = {
//
// ====================
// Runner Configuration
// ====================
//
// WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or
// on a remote machine).
runner: "local",
//
// Override default path ('/wd/hub') for chromedriver service.
// path: '/wd/hub',
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called. Notice that, if you are calling `wdio` from an
// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
// directory is where your package.json resides, so `wdio` will be called from there.
//
specs: [
"./tests/integration/tests/**/test-*-page.js",
],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude options in
// order to group specific specs to a specific capability.
//
// First, you can define how many instances should be started at the same time. Let's
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
// files and you set maxInstances to 10, all spec files will get tested at the same time
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances: 1,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://docs.saucelabs.com/reference/platforms-configurator
//
capabilities: [{
browserName: "firefox",
"moz:firefoxOptions": {
log: { level: "trace" },
prefs: {},
},
}],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// Level of logging verbosity: trace | debug | info | warn | error | silent
logLevel: "error",
//
// Set specific log levels per logger
// loggers:
// - webdriver, webdriverio
// - @wdio/applitools-service, @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service
// - @wdio/mocha-framework, @wdio/jasmine-framework
// - @wdio/local-runner, @wdio/lambda-runner
// - @wdio/sumologic-reporter
// - @wdio/cli, @wdio/config, @wdio/sync, @wdio/utils
// Level of logging verbosity: trace | debug | info | warn | error | silent
// logLevels: {
// webdriver: 'info',
// '@wdio/applitools-service': 'info'
// },
//
// If you only want to run your tests until a specific amount of tests have failed use
// bail (default is 0 - don't bail, run all tests).
bail: 0,
//
// Set a base URL in order to shorten url command calls. If your `url` parameter starts
// with `/`, the base url gets prepended, not including the path portion of your baseUrl.
// If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url
// gets prepended directly.
baseUrl: process.env.SERVER_URL,
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
//
// Default timeout in milliseconds for request
// if browser driver or grid doesn't send response
connectionRetryTimeout: 90000,
//
// Default request retries count
connectionRetryCount: 3,
//
// Test runner services
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
services: [
"firefox-profile", "selenium-standalone", ["image-comparison", {
baselineFolder: join(process.cwd(), "./tests/integration/tests/Visual_Baseline/"),
formatImageName: process.env.MOZ_HEADLESS ? "{tag}-headless-{width}x{height}" : "{tag}-{width}x{height}",
screenshotPath: join(process.cwd(), ".tmp/"),
savePerInstance: true,
// autoSaveBaseline: true,
}],
],
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
// see also: https://webdriver.io/docs/frameworks.html
//
// Make sure you have the wdio adapter package for the specific framework installed
// before running any tests.
framework: "mocha",
//
// The number of times to retry the entire specfile when it fails as a whole
// specFileRetries: 1,
//
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: https://webdriver.io/docs/dot-reporter.html
reporters: ["dot", "spec"],
//
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
mochaOpts: {
ui: "bdd",
timeout: 120000,
},
//
// =====
// Hooks
// =====
// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
// it and to build services around it. You can either apply a single function or an array of
// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
// resolved to continue.
/**
* Gets executed once before all workers get launched.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
// onPrepare: function (config, capabilities) {
// },
/**
* Gets executed just before initialising the webdriver session and test framework. It allows you
* to manipulate configurations depending on the capability or spec.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
//beforeSession: function (config, capabilities, specs) {
//},
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
before: function (capabilities, specs) {
const chai = require("chai");
global.expect = chai.expect;
chai.Should();
global.primaryEmail = "test@mailinator.com";
global.secondaryEmail = "test" + Math.random() + "@mailinator.com";
global.monitorFxaPassword = process.env.MONITOR_FXA_PASSWORD || "a_secure_password ;)";
browser.setWindowSize(1920, 1080);
},
/**
* Runs before a WebdriverIO command gets executed.
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
*/
// beforeCommand: function (commandName, args) {
// },
/**
* Hook that gets executed before the suite starts
* @param {Object} suite suite details
*/
// beforeSuite: function (suite) {
// },
/**
* Function to be executed before a test (in Mocha/Jasmine) starts.
*/
// beforeTest: function (test, context) {
// },
/**
* Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
* beforeEach in Mocha)
*/
// beforeHook: function (test, context) {
// },
/**
* Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
* afterEach in Mocha)
*/
// afterHook: function (test, context, { error, result, duration, passed, retries }) {
// },
/**
* Function to be executed after a test (in Mocha/Jasmine).
*/
afterTest: function(test, context, { error, result, duration, passed, retries }) {
/* Take screenshots for debugging on circleci */
if ("CIRCLECI" in process.env) {
const path = join(process.cwd(), "./tests/integration/errorShots/error-"+Date.now()+".png");
browser.saveScreenshot(path);
}
},
/**
* Hook that gets executed after the suite has ended
* @param {Object} suite suite details
*/
// afterSuite: function (suite) {
// },
/**
* Runs after a WebdriverIO command gets executed
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
* @param {Number} result 0 - command success, 1 - command error
* @param {Object} error error object if any
*/
// afterCommand: function (commandName, args, result, error) {
// },
/**
* Gets executed after all tests are done. You still have access to all global variables from
* the test.
* @param {Number} result 0 - test pass, 1 - test fail
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// after: function (result, capabilities, specs) {
// },
/**
* Gets executed right after terminating the webdriver session.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// afterSession: function (config, capabilities, specs) {
// },
/**
* Gets executed after all workers got shut down and the process is about to exit. An error
* thrown in the onComplete hook will result in the test run failing.
* @param {Object} exitCode 0 - success, 1 - fail
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {<Object>} results object containing test results
*/
// onComplete: function(exitCode, config, capabilities, results) {
// },
/**
* Gets executed when a refresh happens.
* @param {String} oldSessionId session ID of the old session
* @param {String} newSessionId session ID of the new session
*/
//onReload: function(oldSessionId, newSessionId) {
//}
};

Просмотреть файл

@ -0,0 +1,37 @@
/* eslint-disable no-process-env */
"use strict";
const common = require("./wdio.conf.js");
const { join } = require("path");
exports.config = Object.assign({}, common.config, {
baseUrl: "http://localhost:6060",
maxInstances: 3,
services: ["docker", "firefox-profile", "selenium-standalone", ["image-comparison", {
baselineFolder: join(process.cwd(), "./tests/integration/tests/Visual_Baseline/"),
formatImageName: process.env.MOZ_HEADLESS ? "{tag}-headless-{width}x{height}" : "{tag}-{width}x{height}",
screenshotPath: join(process.cwd(), ".tmp/"),
savePerInstance: true,
}]],
dockerOptions: {
image: "selenium/standalone-firefox",
healthCheck: {
url: "http://localhost:4444",
maxRetries: 3,
inspectInterval: 1000,
startDelay: 2000,
},
options: {
e: ["MOZ_HEADLESS=1",
"MONITOR_FXA_PASSWORD=${MONITOR_FXA_PASSWORD}",
"HIBP_KANON_API_TOKEN=${HIBP_KANON_API_TOKEN}",
"HIBP_API_TOKEN=${HIBP_API_TOKEN}",
],
p: ["4444:4444", "5900:5900"],
v: "/dev/shm:/dev/shm",
shmSize: "2g",
network: "host",
},
dockerLogs: "docker-logs/",
},
});