├── .gitignore ├── .travis.yml ├── Gruntfile.js ├── README.md ├── features ├── google.feature ├── step_definitions │ └── google-steps.js └── support │ ├── hooks.js │ └── world.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | node_modules 4 | screenshots 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | env: 5 | - PLATFORM=FIREFOX 6 | addons: 7 | firefox: "55.0.2" 8 | before_install: 9 | - wget https://github.com/mozilla/geckodriver/releases/download/v0.18.0/geckodriver-v0.18.0-linux64.tar.gz 10 | - mkdir geckodriver 11 | - tar -xzf geckodriver-v0.18.0-linux64.tar.gz -C geckodriver 12 | - export PATH=$PATH:$PWD/geckodriver 13 | services: 14 | - xvfb 15 | notifications: 16 | email: 17 | recipients: 18 | secure: DYwbn/1ENwl6+vlHkeBrVYsdL/HojfOJnfHrgY72lWr+Emvr0On28q7hCgb7aRMSi7H0Zx9zelg6625Kd/aTe8MLwfYmysT4tIBsWPXqLslEFMYAsqlExl24xyfEpDEa+QxmMjqWYaYVYTSDgmW0pUCsU0PiKirPJriGWZ2qGJ82sK7aS6Ic4/Hh5NNQ6oO0KscOUtpNlYq6/3Kxd+eQkJEVKNpr2tJqlqwi6T1YtHUT8yHsMpSdSOIWevZLWBcurimeS+l2HJ4PYm2vVmiGwQWBE1ALjHE47bGp+vOPsxIURHzJ77hq/+CBg4dHeviefl2J9tM8/L19zPpocwPz9tttmsEJdXMNHdZVfC0NpggTgIuK32hQeivqEajltYIMEe36qbDjUWcxO0cdzhuQddILNtdEwefANxK0z5e4Ah7weCh2B+ElLlL9iVT7KPYfs4ZdD1rv0V+bwCgZ74nARxU2Nw/ECNbNlBvsjNA64jr3+hFtSkg2TgNHJxv1uhxJun0wf6uSp2cZB46kg91R2BEybfEnV7QdVwOwEYs5scH5jmf3KdZ8N3YaFDhqpalhRXDu3T8x+xBxa2A20uRcJSMgGpLOIQdlb6uajP1TypxHu4tvjMJe7aaAsg/RYz4qwGfQ6IrOgD2AUq6RpAT3xI12TysXi1YkWxJIYLlAUCY= 19 | on_failure: always 20 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | module.exports = function(grunt) { 6 | 7 | grunt.initConfig({ 8 | 9 | env: { 10 | chrome: { 11 | PLATFORM: 'CHROME' 12 | }, 13 | firefox: { 14 | PLATFORM: 'FIREFOX' 15 | }, 16 | android: { 17 | PLATFORM: 'ANDROID' 18 | } 19 | }, 20 | 21 | jshint: { 22 | all: ['Gruntfile.js', 'features/step_definitions/*.js', 'features/support/*.js'], 23 | options: { 24 | node: true, 25 | strict: "global", 26 | esversion: 6 27 | } 28 | }, 29 | 30 | exec: { 31 | run_cucumber_tests: { 32 | command: path.join('node_modules', 'cucumber', 'bin', 'cucumber.js') 33 | } 34 | } 35 | 36 | }); 37 | 38 | grunt.loadNpmTasks('grunt-contrib-jshint'); 39 | grunt.loadNpmTasks('grunt-exec'); 40 | grunt.loadNpmTasks('grunt-env'); 41 | 42 | grunt.registerTask('default', ['jshint', 'exec']); 43 | grunt.registerTask('chrome', ['env:chrome', 'jshint', 'exec']); 44 | grunt.registerTask('firefox', ['env:firefox', 'jshint', 'exec']); 45 | grunt.registerTask('android', ['env:android', 'jshint', 'exec']); 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automated Testing in JavaScript with Cucumber-JS and Selenium-Webdriver 2 | 3 | ![build status](https://travis-ci.org/Matt-B/cucumber-js-selenium-webdriver-example.svg?branch=master) 4 | 5 | This is an example project using cucumber-js and selenium-webdriver to run browser-based automated tests, in both desktop Chrome and Chrome on Android. 6 | 7 | I've put this here as it took me a while to get going with this setup, and I thought others might find this useful as a starter to experiment with cucumber and webdriver in JavaScript, or as a base on which to build their own test suites. 8 | 9 | Currently, the project has a single test which searches Google for 'cucumbers' and verifies some results are shown. It runs the tests in Chrome and so you'll need Chrome and the Chromedriver executable on your path. If you're running the tests on Android, you'll need an Android device with Chrome installed. All the JavaScript is linted using jshint before the tests are run (using options specified in the Gruntfile). If any scenarios fail, a screenshot will be taken as a PNG and put in ./screenshots/. After the tests are finished Cucumber shuts down the Webdriver instance. 10 | 11 | ## Running the tests on the desktop 12 | 13 | To get going, you'll need Chrome (or Chromium) installed, and you'll also need the Chromedriver executable available on your path. You can get Chromedriver from [here](http://chromedriver.storage.googleapis.com/index.html) and then, in Linux, you can add the directory to your path like this: 14 | 15 | export PATH=$PATH:~/path/to/directory/containing/chromedriver 16 | 17 | Verify it is working by opening a terminal and typing 'chromedriver'. You should see: 18 | 19 | [me@computer ~]$ chromedriver 20 | Starting ChromeDriver (v2.10) on port 9515 21 | Only local connections are allowed. 22 | 23 | If all seems OK, Ctrl+C to get rid of that, and carry on: 24 | 25 | git clone git://github.com/Matt-B/cucumber-js-selenium-webdriver-example.git 26 | cd cucumber-js-selenium-webdriver-example 27 | npm install 28 | node_modules/grunt-cli/bin/grunt 29 | 30 | Which should first use jshint to lint the step definitions (options are specified in the Gruntfile.js), and then run the tests, producing output that looks something like: 31 | 32 | [me@computer cucumber-js-selenium-webdriver-example]$ grunt 33 | Running "jshint:files" (jshint) task 34 | >> 4 files lint free. 35 | 36 | Running "exec:run_cucumber_tests" (exec) task 37 | 38 | Feature: Searching for cucumbers 39 | As an internet user 40 | In order to find out more about cucumbers 41 | I want to be able to search for information about cucumbers 42 | 43 | 44 | Scenario: Google cucumber search # features/google.feature:6 45 | When I search Google for "cucumbers" # features/google.feature:7 46 | Then I should see some results # features/google.feature:8 47 | 48 | 49 | 1 scenario (1 passed) 50 | 2 steps (2 passed) 51 | 52 | Done, without errors. 53 | 54 | ## Running the tests on an Android device 55 | 56 | I've only tried this with a physical device, but it should work with the emulator with some small changes. 57 | 58 | To get started, connect an Android device to your computer via USB and ensure USB debugging is turned on. You might need to authorise the computer on the phone before anything will work. 59 | 60 | If you haven't done so already, ensure you've got the project and installed dependencies: 61 | 62 | git clone git://github.com/Matt-B/cucumber-js-selenium-webdriver-example.git 63 | cd cucumber-js-selenium-webdriver-example 64 | npm install 65 | 66 | This will install Appium, but you'll need to start the server (it's probably best to do this in a separate terminal): 67 | 68 | cd node_modules/appium/ 69 | node . 70 | 71 | Once that has started successfully, then try running the test: 72 | 73 | [me@computer cucumber-js-selenium-webdriver-example]$ grunt android 74 | Running "jshint:files" (jshint) task 75 | >> 4 files lint free. 76 | 77 | Running "exec:run_cucumber_tests" (exec) task 78 | 79 | Feature: Searching for cucumbers 80 | As an internet user 81 | In order to find out more about cucumbers 82 | I want to be able to search for information about cucumbers 83 | 84 | 85 | Scenario: Google cucumber search # features/google.feature:6 86 | When I search Google for "cucumbers" # features/google.feature:7 87 | Then I should see some results # features/google.feature:8 88 | 89 | 90 | 1 scenario (1 passed) 91 | 2 steps (2 passed) 92 | 93 | Done, without errors. 94 | 95 | 96 | All done! 97 | 98 | ## Writing your own tests 99 | 100 | If you want to use this as a jumping off point for a new test project, then remove all the git gubbins: 101 | 102 | rm -R .git 103 | 104 | You're now ready to make your changes and put it under your own source control (git, or otherwise). 105 | -------------------------------------------------------------------------------- /features/google.feature: -------------------------------------------------------------------------------- 1 | Feature: Searching for cucumbers 2 | As an internet user 3 | In order to find out more about cucumbers 4 | I want to be able to search for information about cucumbers 5 | 6 | Scenario: Google cucumber search 7 | When I search Google for "cucumbers" 8 | Then I should see some results 9 | -------------------------------------------------------------------------------- /features/step_definitions/google-steps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var {defineSupportCode} = require('cucumber'); 4 | var {By, until, Key} = require('selenium-webdriver'); 5 | var {expect} = require('chai'); 6 | 7 | defineSupportCode(function({When, Then}) { 8 | 9 | When(/^I search Google for "([^"]*)"$/, function (searchQuery, next) { 10 | this.driver.get('http://www.google.co.uk/webhp?complete=0'); 11 | this.driver.findElement(By.name('q')) 12 | .sendKeys(searchQuery); 13 | this.driver.findElement(By.name('q')) 14 | .sendKeys(Key.ENTER) 15 | .then(function() { 16 | next(); 17 | }); 18 | }); 19 | 20 | Then(/^I should see some results$/, function (next) { 21 | this.driver.wait(until.elementLocated(By.css('div.g'))); 22 | this.driver.findElements(By.css('div.g')) 23 | .then(function(elements) { 24 | expect(elements.length).to.not.equal(0); 25 | next(); 26 | }); 27 | }); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /features/support/hooks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var {defineSupportCode} = require('cucumber'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var sanitize = require("sanitize-filename"); 7 | 8 | defineSupportCode(function({After, AfterAll}) { 9 | 10 | After(function(scenarioResult) { 11 | if(scenarioResult.isFailed()) { 12 | this.driver.takeScreenshot().then(function(data){ 13 | var base64Data = data.replace(/^data:image\/png;base64,/,""); 14 | fs.writeFile(path.join('screenshots', sanitize(scenarioResult.scenario.name + ".png").replace(/ /g,"_")), base64Data, 'base64', function(err) { 15 | if(err) console.log(err); 16 | }); 17 | }); 18 | } 19 | return this.driver.quit(); 20 | }); 21 | 22 | }); -------------------------------------------------------------------------------- /features/support/world.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var {defineSupportCode} = require('cucumber'); 4 | var {Builder, By, until} = require('selenium-webdriver'); 5 | var fs = require('fs'); 6 | var platform = process.env.PLATFORM || "CHROME"; 7 | 8 | var buildAndroidDriver = function() { 9 | return new Builder(). 10 | usingServer('http://localhost:4723/wd/hub'). 11 | withCapabilities({ 12 | platformName: 'Android', 13 | deviceName: 'Android device', 14 | browserName: 'Chrome' 15 | }). 16 | build(); 17 | }; 18 | 19 | var buildChromeDriver = function() { 20 | return new Builder().forBrowser("chrome").build(); 21 | }; 22 | 23 | var buildFirefoxDriver = function() { 24 | return new Builder().forBrowser("firefox").build(); 25 | }; 26 | 27 | var buildDriver = function() { 28 | switch(platform) { 29 | case 'ANDROID': 30 | return buildAndroidDriver(); 31 | case 'FIREFOX': 32 | return buildFirefoxDriver(); 33 | default: 34 | return buildChromeDriver(); 35 | } 36 | }; 37 | 38 | defineSupportCode(function({setDefaultTimeout}) { 39 | setDefaultTimeout(60 * 1000); 40 | }); 41 | 42 | var World = function World() { 43 | 44 | var screenshotPath = "screenshots"; 45 | 46 | this.driver = buildDriver(); 47 | 48 | if(!fs.existsSync(screenshotPath)) { 49 | fs.mkdirSync(screenshotPath); 50 | } 51 | 52 | }; 53 | 54 | defineSupportCode(function({setWorldConstructor}) { 55 | setWorldConstructor(World); 56 | }); 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cucumber-js-selenium-webdriver-example", 3 | "version": "1.0.0", 4 | "description": "A starter for acceptance testing using cucumber-js and webdriver-js.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git@github.com:Matt-B/cucumber-js-selenium-webdriver-example.git" 8 | }, 9 | "scripts": { 10 | "test": "node_modules/grunt-cli/bin/grunt" 11 | }, 12 | "devDependencies": { 13 | "cucumber": "^2.3.1", 14 | "selenium-webdriver": "^3.5.0", 15 | "chai": "^4.1.1", 16 | "grunt": "^1.0.1", 17 | "grunt-cli": "^1.2.0", 18 | "grunt-contrib-jshint": "^1.1.0", 19 | "grunt-exec": "^3.0.0", 20 | "grunt-env": "^0.4.4", 21 | "sanitize-filename": "^1.6.1", 22 | "appium": "^1.6.5" 23 | } 24 | } 25 | --------------------------------------------------------------------------------