diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc3c07fad..a426f7c56 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -108,3 +108,49 @@ mocha test/runner ``` Please try to add corresponding testcase to runner or unit. + +## Running tests in Dockerized environment +Instead of manually running php, json_server and selenium for before tests you +can use `docker-compose` to run those automatically. +You can find `docker-compose.yml` file in `test` directory and run all commands +from this directory. Currently we provide following commands to run tests with +respective dependencies: + +### Run unit tests +``` shell +docker-compose run --rm test-unit +``` + +### Run helper tests +``` shell +docker-compose run --rm test-helpers + +# or pass path to helper test to run specific helper, +# for example to run only WebDriverIO tests: +docker-compose run --rm test-helpers test/helper/WebDriverIO_test.js + +# Or to run only rest and ApiDataFactory tests +docker-compose run --rm test-helpers test/rest +``` + +### Run acceptance tests +To that we provide two separate services respectively for WebDriverIO and +Nightmare tests: +``` shell +docker-compose run --rm test-acceptance.webdriverio +docker-compose run --rm test-acceptance.nightmare +``` + +### Running against specific Node version +By default dockerized tests are run against node 6.9.5, you can run it against +specific version as long as there is Docker container available for such +version. To do that you need to build codecept's Docker image prior to running +tests and pass `NODE_VERSION` as build argument. + +For example to prepare `test-helpers` containers based on node 8.7.0: +``` shell +docker-compose build --build-arg NODE_VERSION=8.7.0 test-helpers +``` + +And now every command based on `test-helpers` service will use node 8.7.0. The +same argument can be passed when building unit and acceptance tests services. diff --git a/Dockerfile b/Dockerfile index 497cc0b0e..998cb8e04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,36 @@ -FROM node:6.9.5 -# LTS +ARG NODE_VERSION=6.9.5 +FROM node:${NODE_VERSION} - # Set grep as an ENV variable -ENV CODECEPT_ARGS="" +# Add our user and group first to make sure their IDs get assigned consistently, +# regardless of whatever dependencies get added. +RUN groupadd --system nightmare && useradd --system --create-home --gid nightmare nightmare - # Set HOST ENV variable for Selenium Server -ENV HOST=selenium +# Installing the pre-required packages and libraries for electron & Nightmare +RUN apt-get update && \ + apt-get install -y libgtk2.0-0 libgconf-2-4 \ + libasound2 libxtst6 libxss1 libnss3 xvfb - # Add our user and group first to make sure their IDs get assigned consistently, - # regardless of whatever dependencies get added. -RUN groupadd --system nightmare && useradd --system --create-home --gid nightmare nightmare +WORKDIR /tmp +COPY package.json /tmp/ - # Installing the pre-required packages and libraries for electron & Nightmare -RUN apt-get update && \ - apt-get install -y libgtk2.0-0 libgconf-2-4 \ - libasound2 libxtst6 libxss1 libnss3 xvfb +# Install packages +RUN npm install --loglevel=warn -ADD . / +RUN mkdir /codecept +WORKDIR /codecept - # Install latest version of Nightmare -RUN npm install +COPY . /codecept - # Set the entrypoint for Nightmare -ENTRYPOINT ["/docker/entrypoint"] +RUN cp -a /tmp/node_modules /codecept/ - # Run tests -CMD ["bash", "/docker/run.sh"] +# Allow to pass argument to codecept run via env variable +ENV CODECEPT_ARGS="" +# Set HOST ENV variable for Selenium Server +ENV HOST=selenium + +# Set the entrypoint for Nightmare +ENTRYPOINT ["docker/entrypoint"] + +# Run tests +CMD ["bash", "docker/run.sh"] diff --git a/docker/entrypoint b/docker/entrypoint index 8d30cfdad..0e313dc8a 100755 --- a/docker/entrypoint +++ b/docker/entrypoint @@ -1,8 +1,4 @@ #!/usr/bin/env bash set -e -# Start Xvfb -Xvfb -ac -screen scrn 1280x1024x24 :9.0 & -export DISPLAY=:9.0 - -exec "$@" \ No newline at end of file +xvfb-run -a --server-args="-screen 0 1024x768x24" "$@" diff --git a/docker/run.sh b/docker/run.sh index 133e3a85c..674ac3830 100755 --- a/docker/run.sh +++ b/docker/run.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -source /docker/help.sh +source docker/help.sh # Check if tests are correctly mounted if [[ -d "/tests/" ]]; then @@ -8,7 +8,7 @@ if [[ -d "/tests/" ]]; then # Run the tests cd /tests/ - node /bin/codecept.js run $CODECEPT_ARGS + node /codecept/bin/codecept.js run $CODECEPT_ARGS else display_usage fi diff --git a/test/.env b/test/.env new file mode 100644 index 000000000..0dae31b63 --- /dev/null +++ b/test/.env @@ -0,0 +1,4 @@ +SITE_URL=http://php:8000 +SELENIUM_HOST=selenium.chrome +SELENIUM_PORT=4444 +JSON_SERVER_URL=http://json_server:8010 diff --git a/test/acceptance/codecept.Nightmare.js b/test/acceptance/codecept.Nightmare.js new file mode 100644 index 000000000..a76ecdb92 --- /dev/null +++ b/test/acceptance/codecept.Nightmare.js @@ -0,0 +1,16 @@ +const TestHelper = require('../support/TestHelper'); + +module.exports.config = { + tests: "./*_test.js", + timeout: 10000, + output: "./output", + helpers: { + Nightmare: { + url: TestHelper.siteUrl() + } + }, + include: {}, + bootstrap: false, + mocha: {}, + name: "acceptance" +} diff --git a/test/acceptance/codecept.Nightmare.json b/test/acceptance/codecept.Nightmare.json deleted file mode 100644 index e94172434..000000000 --- a/test/acceptance/codecept.Nightmare.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tests": "./*_test.js", - "timeout": 10000, - "output": "./output", - "helpers": { - "Nightmare": { - "url": "http://127.0.0.1:8000" - } - }, - "include": {}, - "bootstrap": false, - "mocha": {}, - "name": "acceptance" -} \ No newline at end of file diff --git a/test/acceptance/codecept.WebDriverIO.js b/test/acceptance/codecept.WebDriverIO.js new file mode 100644 index 000000000..d4485e063 --- /dev/null +++ b/test/acceptance/codecept.WebDriverIO.js @@ -0,0 +1,20 @@ +const TestHelper = require('../support/TestHelper.js'); + +module.exports.config = { + tests: "./*_test.js", + timeout: 10000, + output: "./output", + helpers: { + WebDriverIO: { + url: TestHelper.siteUrl(), + browser: "chrome", + host: TestHelper.seleniumHost(), + port: TestHelper.seleniumPort() + } + + }, + include: {}, + bootstrap: false, + mocha: {}, + name: "acceptance" +} diff --git a/test/acceptance/codecept.WebDriverIO.json b/test/acceptance/codecept.WebDriverIO.json deleted file mode 100644 index ee3a6f708..000000000 --- a/test/acceptance/codecept.WebDriverIO.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "tests": "./*_test.js", - "timeout": 10000, - "output": "./output", - "helpers": { - "WebDriverIO": { - "url": "http://127.0.0.1:8000", - "browser": "chrome" - } - - }, - "include": {}, - "bootstrap": false, - "mocha": {}, - "name": "acceptance" -} \ No newline at end of file diff --git a/test/acceptance/within_test.js b/test/acceptance/within_test.js index 4ad2e27a4..3660a7780 100644 --- a/test/acceptance/within_test.js +++ b/test/acceptance/within_test.js @@ -1,6 +1,6 @@ Feature('within'); -Scenario('within on form @WebDriverIO @protractor @nightmare', (I) => { +Scenario('within on form @WebDriverIO @Protractor @Nightmare', (I) => { I.amOnPage('/form/bug1467'); I.see('TEST TEST'); within({css: '[name=form2]'}, () => { @@ -16,7 +16,7 @@ Scenario('within on iframe @WebDriverIO', (I) => { within({frame: 'iframe'}, () => { I.fillField('rus', 'Updated'); I.click('Sign in!'); - I.see('Email Address'); + I.waitForText('Email Address'); }); I.see('Iframe test'); I.dontSee('Email Address'); @@ -47,7 +47,7 @@ Scenario('within on nested iframe (depth=1) @WebDriverIO', (I) => { within({frame: ['[name=content]']}, () => { I.fillField('rus', 'Updated'); I.click('Sign in!'); - I.see('Email Address'); + I.waitForText('Email Address'); }); I.see('Iframe test'); I.dontSee('Email Address'); @@ -84,4 +84,4 @@ Scenario('within on nested iframe (depth=2) and mixed class and xpath selector @ }); I.see('Nested Iframe test'); I.dontSee('Email Address'); -}); \ No newline at end of file +}); diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 000000000..ccb343309 --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,77 @@ +version: '3' +services: + test-unit: + build: .. + entrypoint: node_modules/.bin/gulp + command: test + working_dir: /codecept + volumes: + - ..:/codecept + - node_modules:/codecept/node_modules + + test-helpers: + build: .. + entrypoint: docker/entrypoint node_modules/.bin/mocha --invert --fgrep Appium + command: test/helper + working_dir: /codecept + env_file: .env + depends_on: + - selenium.chrome + - php + - json_server + volumes: + - ..:/codecept + - node_modules:/codecept/node_modules + + test-acceptance.webdriverio: + build: .. + env_file: .env + environment: + - CODECEPT_ARGS=-c codecept.WebDriverIO.js --grep @WebDriverIO + depends_on: + - php + - selenium.chrome + volumes: + - ./acceptance:/tests + - ./support:/support + - node_modules:/codecept/node_modules + + test-acceptance.nightmare: + build: .. + env_file: .env + environment: + - CODECEPT_ARGS=-c codecept.Nightmare.js --grep @Nightmare + depends_on: + - php + - selenium.chrome + volumes: + - ./acceptance:/tests + - ./support:/support + - node_modules:/codecept/node_modules + + selenium.chrome: + image: selenium/standalone-chrome:3.5.2-antimony + ports: + - 4444:4444 + + php: + image: php:7.0 + command: php -S 0.0.0.0:8000 -t /test/data/app + ports: + - 8000:8000 + volumes: + - .:/test + + json_server: + build: .. + entrypoint: [] + command: npm run json-server + working_dir: /codecept + expose: + - 8010 + volumes: + - ..:/codecept + - node_modules:/codecept/node_modules + +volumes: + node_modules: diff --git a/test/helper/Nightmare_test.js b/test/helper/Nightmare_test.js index 3bd376db9..ea6e3c6f9 100644 --- a/test/helper/Nightmare_test.js +++ b/test/helper/Nightmare_test.js @@ -1,8 +1,10 @@ 'use strict'; +let TestHelper = require('../support/TestHelper'); + let Nightmare = require('../../lib/helper/Nightmare'); let should = require('chai').should(); let I, browser; -let site_url = 'http://127.0.0.1:8000'; +let site_url = TestHelper.siteUrl(); let assert = require('assert'); let path = require('path'); let fs = require('fs'); diff --git a/test/helper/Protractor_test.js b/test/helper/Protractor_test.js index 8d0db9af7..f6184d8c0 100644 --- a/test/helper/Protractor_test.js +++ b/test/helper/Protractor_test.js @@ -1,8 +1,9 @@ 'use strict'; +let TestHelper = require('../support/TestHelper'); let Protractor = require('../../lib/helper/Protractor'); let site_url = 'http://davertmik.github.io/angular-demo-app'; -const web_app_url = 'http://127.0.0.1:8000' +const web_app_url = TestHelper.siteUrl(); let assert = require('assert'); let I, browser; let path = require('path'); @@ -30,7 +31,11 @@ describe('Protractor', function() { before(() => { global.codecept_dir = path.join(__dirname, '../data'); - I = new Protractor({url: site_url, browser: 'chrome'}); + I = new Protractor({ + url: site_url, + browser: 'chrome', + seleniumAddress: TestHelper.seleniumAddress() + }); return I._init().then(() => { return I._beforeSuite(); }); diff --git a/test/helper/SeleniumWebdriver_test.js b/test/helper/SeleniumWebdriver_test.js index 555b07645..217c765d2 100644 --- a/test/helper/SeleniumWebdriver_test.js +++ b/test/helper/SeleniumWebdriver_test.js @@ -1,8 +1,10 @@ 'use strict'; +let TestHelper = require('../support/TestHelper'); + let SeleniumWebdriver = require('../../lib/helper/SeleniumWebdriver'); let should = require('chai').should(); let I, browser; -let site_url = 'http://127.0.0.1:8000'; +let site_url = TestHelper.siteUrl(); let assert = require('assert'); let path = require('path'); let fs = require('fs'); @@ -27,8 +29,8 @@ describe('SeleniumWebdriver', function () { url: site_url, browser: 'chrome', windowSize: '500x700', - restart: false - + restart: false, + seleniumAddress: TestHelper.seleniumAddress() }); return I._init().then(() => { return I._beforeSuite().then(() => { diff --git a/test/helper/WebDriverIO_test.js b/test/helper/WebDriverIO_test.js index 549910278..4fbfb767f 100644 --- a/test/helper/WebDriverIO_test.js +++ b/test/helper/WebDriverIO_test.js @@ -1,9 +1,10 @@ 'use strict'; +let TestHelper = require('../support/TestHelper'); let WebDriverIO = require('../../lib/helper/WebDriverIO'); let should = require('chai').should(); let wd; -let site_url = 'http://127.0.0.1:8000'; +let site_url = TestHelper.siteUrl(); let assert = require('assert'); let path = require('path'); let fs = require('fs'); @@ -29,7 +30,9 @@ describe('WebDriverIO', function () { url: site_url, browser: 'chrome', windowSize: '500x700', - smartWait: 10 // just to try + smartWait: 10, // just to try + host: TestHelper.seleniumHost(), + port: TestHelper.seleniumPort() }); }); @@ -172,7 +175,7 @@ describe('WebDriverIO', function () { it('should check attributes values for given element', () => { return wd.amOnPage('/info') .then(() => wd.seeAttributesOnElements('//form', { method: "post"})) - .then(() => wd.seeAttributesOnElements('//form', { method: "post", action: "http://127.0.0.1:8000/"})) + .then(() => wd.seeAttributesOnElements('//form', { method: "post", action: `${site_url}/` })) .then(() => wd.seeAttributesOnElements('//form', { method: "get"})) .then(expectError) .catch((e) => { @@ -277,17 +280,17 @@ describe('WebDriverIO', function () { .then(() => wd.waitInUrl('/info2', 0.1)) .then(expectError) .catch((e) => { - assert.equal(e.message, `expected url to include /info2, but found http://127.0.0.1:8000/info`); + assert.equal(e.message, `expected url to include /info2, but found ${site_url}/info`); }); }); it('should wait for the entire URL to match the expected', () => { return wd.amOnPage('/info') .then(() => wd.waitUrlEquals('/info')) - .then(() => wd.waitUrlEquals('http://127.0.0.1:8000/info')) + .then(() => wd.waitUrlEquals(`${site_url}/info`)) .then(() => wd.waitUrlEquals('/info2', 0.1)) .then(expectError) .catch((e) => { - assert.equal(e.message, `expected url to be http://127.0.0.1:8000/info2, but found http://127.0.0.1:8000/info`); + assert.equal(e.message, `expected url to be ${site_url}/info2, but found ${site_url}/info`); }); }); }); diff --git a/test/rest/ApiDataFactory_test.js b/test/rest/ApiDataFactory_test.js index 98d373ab8..5c3edce3e 100644 --- a/test/rest/ApiDataFactory_test.js +++ b/test/rest/ApiDataFactory_test.js @@ -1,7 +1,9 @@ 'use strict'; +const TestHelper = require('../support/TestHelper'); + let ApiDataFactory = require('../../lib/helper/ApiDataFactory'); let should = require('chai').should(); -let api_url = 'http://127.0.0.1:8010'; +let api_url = TestHelper.jsonServerUrl(); let assert = require('assert'); let path = require('path'); let fs = require('fs'); @@ -52,6 +54,7 @@ describe('ApiDataFactory', function () { }); describe('create and cleanup records', function() { + this.timeout(20000) it('should create a new post', function*() { yield I.have('post'); @@ -154,4 +157,4 @@ describe('ApiDataFactory', function () { }); }); -}); \ No newline at end of file +}); diff --git a/test/rest/REST_test.js b/test/rest/REST_test.js index c29c7a430..d5197a275 100644 --- a/test/rest/REST_test.js +++ b/test/rest/REST_test.js @@ -1,7 +1,8 @@ 'use strict'; +const TestHelper = require('../support/TestHelper'); let REST = require('../../lib/helper/REST'); let should = require('chai').should(); -let api_url = 'http://127.0.0.1:8010'; +let api_url = TestHelper.jsonServerUrl(); let assert = require('assert'); let path = require('path'); let fs = require('fs'); @@ -103,4 +104,4 @@ describe('REST', function () { }); }); -}); \ No newline at end of file +}); diff --git a/test/support/TestHelper.js b/test/support/TestHelper.js new file mode 100644 index 000000000..4f2fe9d27 --- /dev/null +++ b/test/support/TestHelper.js @@ -0,0 +1,23 @@ +class TestHelper { + static siteUrl() { + return (process.env.SITE_URL || 'http://localhost:8000') + } + + static seleniumAddress() { + return `http://${this.seleniumHost()}:${this.seleniumPort()}/wd/hub` + } + + static seleniumHost() { + return (process.env.SELENIUM_HOST || 'localhost') + } + + static seleniumPort() { + return (process.env.SELENIUM_PORT || '4444') + } + + static jsonServerUrl() { + return (process.env.JSON_SERVER_URL || 'http://localhost:8010') + } +} + +module.exports = TestHelper; diff --git a/test/wercker.yml b/test/wercker.yml new file mode 100644 index 000000000..9df35f55f --- /dev/null +++ b/test/wercker.yml @@ -0,0 +1,15 @@ +box: + id: docker/compose:1.16.0 + cmd: /bin/sh + entrypoint: /bin/bash -c + +build: + steps: + - script: + code: | + echo "HELLO WORLD" + +# test: +# steps: +# - script: +# code: docker-compose run --rm test-unit diff --git a/wercker.yml b/wercker.yml new file mode 100644 index 000000000..597b78aa0 --- /dev/null +++ b/wercker.yml @@ -0,0 +1,13 @@ +box: docker/compose:1.16.0 + +build: + steps: + - script: + code: | + cd test + docker-compose build --build-arg NODE_VERSION=6.9.5 + +test: + steps: + - script: + code: docker-compose run --rm test-unit