From f93b9f8f8935973d0f181708ae25789511f44305 Mon Sep 17 00:00:00 2001 From: Nicolai Reuschling Date: Mon, 28 Aug 2017 15:08:03 +0200 Subject: [PATCH] initial release of dkdeploy-cucumber v4.0.0 --- .gitignore | 18 +++ .rubocop.yml | 18 +++ .travis.yml | 15 ++ CHANGELOG.md | 12 ++ CONTRIBUTORS.md | 16 ++ Gemfile | 4 + LICENSE | 7 + README.md | 57 +++++++ Rakefile | 1 + assets/dkdeploy-logo.png | Bin 0 -> 11497 bytes config/cucumber.yml | 4 + dkdeploy-cucumber.gemspec | 35 +++++ features/click.feature | 52 +++++++ features/content.feature | 95 ++++++++++++ features/form.feature | 136 ++++++++++++++++ features/htdocs/click.html | 18 +++ features/htdocs/click1.html | 20 +++ features/htdocs/click2.html | 10 ++ features/htdocs/content.html | 17 ++ features/htdocs/content.json | 3 + features/htdocs/form.html | 26 ++++ features/htdocs/misc.html | 13 ++ features/htdocs/url.html | 10 ++ features/misc.feature | 18 +++ features/support/env.rb | 35 +++++ features/url.feature | 28 ++++ lib/dkdeploy/cucumber.rb | 1 + .../cucumber/step_definitions/click_steps.rb | 39 +++++ .../step_definitions/content_steps.rb | 88 +++++++++++ .../cucumber/step_definitions/form_steps.rb | 146 ++++++++++++++++++ .../cucumber/step_definitions/images.rb | 11 ++ .../cucumber/step_definitions/misc_steps.rb | 105 +++++++++++++ .../cucumber/step_definitions/url_steps.rb | 31 ++++ lib/dkdeploy/cucumber/steps.rb | 2 + lib/dkdeploy/cucumber/support.rb | 3 + lib/dkdeploy/cucumber/support/path.rb | 13 ++ lib/dkdeploy/cucumber/support/selectors.rb | 15 ++ lib/dkdeploy/cucumber/support/with.rb | 9 ++ lib/dkdeploy/cucumber/version.rb | 15 ++ 39 files changed, 1146 insertions(+) create mode 100644 .gitignore create mode 100644 .rubocop.yml create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTORS.md create mode 100644 Gemfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Rakefile create mode 100644 assets/dkdeploy-logo.png create mode 100644 config/cucumber.yml create mode 100644 dkdeploy-cucumber.gemspec create mode 100644 features/click.feature create mode 100644 features/content.feature create mode 100644 features/form.feature create mode 100644 features/htdocs/click.html create mode 100644 features/htdocs/click1.html create mode 100644 features/htdocs/click2.html create mode 100644 features/htdocs/content.html create mode 100644 features/htdocs/content.json create mode 100644 features/htdocs/form.html create mode 100644 features/htdocs/misc.html create mode 100644 features/htdocs/url.html create mode 100644 features/misc.feature create mode 100644 features/support/env.rb create mode 100644 features/url.feature create mode 100644 lib/dkdeploy/cucumber.rb create mode 100644 lib/dkdeploy/cucumber/step_definitions/click_steps.rb create mode 100644 lib/dkdeploy/cucumber/step_definitions/content_steps.rb create mode 100644 lib/dkdeploy/cucumber/step_definitions/form_steps.rb create mode 100644 lib/dkdeploy/cucumber/step_definitions/images.rb create mode 100644 lib/dkdeploy/cucumber/step_definitions/misc_steps.rb create mode 100644 lib/dkdeploy/cucumber/step_definitions/url_steps.rb create mode 100644 lib/dkdeploy/cucumber/steps.rb create mode 100644 lib/dkdeploy/cucumber/support.rb create mode 100644 lib/dkdeploy/cucumber/support/path.rb create mode 100644 lib/dkdeploy/cucumber/support/selectors.rb create mode 100644 lib/dkdeploy/cucumber/support/with.rb create mode 100644 lib/dkdeploy/cucumber/version.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83095a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp +.lock +Gemfile.lock diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..f9f9ed2 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,18 @@ +AllCops: + Exclude: + - 'tmp/**/*' + - 'config/**/*' + - 'vendor/**/*' + TargetRubyVersion: 2.2 +GlobalVars: + AllowedVariables: [] +MethodLength: + Max: 25 +LineLength: + Max: 200 +SpecialGlobalVars: + Enabled: false +BracesAroundHashParameters: + Enabled: false +CyclomaticComplexity: + Max: 10 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..502d6dd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +sudo: false +language: ruby +cache: bundler +rvm: + - 2.2 + - 2.3.4 + - 2.4.1 + +before_install: + - gem install bundler --no-document + +script: + - bundle exec rubocop --display-cop-names --extra-details + - bundle exec cucumber + - bundle exec cucumber --profile negative diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a1ffed8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [4.0.0]- 2017-08-28 +### Summary + +- first public release + +[Unreleased]: https://github.com/dkdeploy/dkdeploy-cucumber/compare/master...develop +[4.0.0]: https://github.com/dkdeploy/dkdeploy-cucumber/releases/tag/v4.0.0 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..8cb806c --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,16 @@ +# dkdeploy CONTRIBUTORS + +The dkdeploy core maintainers would like to recognize following contributors (in alphabetic order): + +- Sascha Egerer +- Christoph Gerold +- Johannes Goslar +- Kieran Hayes +- Wilfried Irßlinger +- Thomas Jahnke +- Gleb Levitin +- Luka Lüdicke +- Nicolai Reuschling +- Lars Tode +- Timo Webler +- Mike Zaschka diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..06fd0d9 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in dkdeploy-cucumber.gemspec +gemspec diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dd37a6c --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2014-2017 dkd Internet Service GmbH, Frankfurt am Main (Germany), https://dkd.de + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ccdc596 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +![dkdeploy](assets/dkdeploy-logo.png) + +# Dkdeploy::Cucumber + +[![Build Status](https://api.travis-ci.org/dkdeploy/dkdeploy-cucumber.svg?branch=develop)](https://travis-ci.org/repositories/dkdeploy/dkdeploy-cucumber) +[![Gem Version](https://badge.fury.io/rb/dkdeploy-cucumber.svg)](https://badge.fury.io/rb/dkdeploy-cucumber) [![Inline docs](http://inch-ci.org/github/dkdeploy/dkdeploy-cucumber.svg?branch=develop)](http://inch-ci.org/github/dkdeploy/dkdeploy-cucumber) + +## Description + +This Rubygem `dkdeploy-cucumber` defines Cucumber steps for browser tests. +Tests are run with `capybara` and PhantomJS. + +## Installation + +Add this line to your application's Gemfile: + + gem 'dkdeploy-cucumber', '~> 4.0' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install dkdeploy-cucumber + +## Usage + +You can see usage examples in the `features` directory. + +## Testing + +## Prerequisite + +`rvm` to test against multiple Ruby versions. + +### Running tests + +1. Run `bundle exec rubocop` +2. Run `bundle exec cucumber` +3. Run `bundle exec cucumber --profile negative` + +### Running tests + +1. Checking coding styles (`rubocop`) +2. Running Cucumber tests (`cucumber`) + +## Contributing + +1. Install [git flow](https://github.com/nvie/gitflow) +2. If project is not checked out already do git clone `git@github.com:dkdeploy/dkdeploy-cucumber.git` +3. Checkout origin develop branch (`git checkout --track -b develop origin/develop`) +4. Git flow initialze `git flow init -d` +5. Installing gems `bundle update` +6. Create new feature branch (`git flow feature start my-new-feature`) +7. Run tests (README.md Testing) +8. Commit your changes (`git commit -am 'Add some feature'`) diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..c702cfc --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require 'bundler/gem_tasks' diff --git a/assets/dkdeploy-logo.png b/assets/dkdeploy-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf5b56c372cfe0d4ae959afe4e0359e720fedcc GIT binary patch literal 11497 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 2.0' + spec.add_development_dependency 'rubocop', '~> 0.48.1' + spec.add_development_dependency 'rspec', '~> 3.6' + spec.add_development_dependency 'capybara', '~> 2.15.1' + spec.add_development_dependency 'phantomjs', '~> 2.1.1.0' + spec.add_development_dependency 'poltergeist', '~> 1.16.0' + + spec.add_dependency 'cucumber', '~> 2.4' + spec.add_dependency 'rspec-expectations', '~> 3.6' + spec.add_dependency 'launchy', '~> 2.4.3' +end diff --git a/features/click.feature b/features/click.feature new file mode 100644 index 0000000..e68f28d --- /dev/null +++ b/features/click.feature @@ -0,0 +1,52 @@ +Feature: Click + Background: + Given I am on "click.html" + + Scenario: click button + Then I should not see "changed" + When I press "b1" + Then I should see "changed" + + @should_fail + Scenario: click button fail + When I press "b3" + + Scenario: click button within context + Then I should not see "changed2" + When I press "b2" within "#scope" + Then I should see "changed2" + + @should_fail + Scenario: click button within context fail + When I press "b1" within "#wrong" + + Scenario: click on element + Then I should not see "changed3" + When I click on element "#clickelement" + Then I should see "changed3" + + @should_fail + Scenario: click on element fail + When I click on element "#clickelement123" + + Scenario: follow link + Given I am on "click1.html" + Then I should not see "link2site" + When I follow "link" + Then I should see "link2site" + + @should_fail + Scenario: follow link fail + Given I am on "click1.html" + When I follow "link123" + + Scenario: click xpath + Given I am on "click1.html" + Then I should not see "link2site" + When I click xpath "/html/body/ul/li[2]/a" + Then I should see "link2site" + + @should_fail + Scenario: click xpath fail + Given I am on "click1.html" + When I click xpath "/html/body/ul/li[5]/a" diff --git a/features/content.feature b/features/content.feature new file mode 100644 index 0000000..6adf469 --- /dev/null +++ b/features/content.feature @@ -0,0 +1,95 @@ +Feature: Content Checks + Background: + Given I am on "content.html" + + Scenario: see something + Then I should see "something" + + @should_fail + Scenario: see something fail + Then I should see "otherthing" + + Scenario: do not see something + Then I should not see "otherthing" + + @should_fail + Scenario: do not see something fail + Then I should not see "something" + + Scenario: see regexp + Then I should see /so.{2}th.ng/ + + @should_fail + Scenario: see regexp fail + Then I should see /so.{3}th.ng/ + + Scenario: not see regexp + Then I should not see /murks/ + + @should_fail + Scenario: not see regexp fail + Then I should not see /thing/ + + Scenario: Date + Then I should see a date within "#date" + + @should_fail + Scenario: Date fail + Then I should see a date within "#falsedate" + + Scenario: Visible + Then the element "#p1" should be visible + + @should_fail + Scenario: Visible fail + Then the element "#p2" should be visible + + Scenario: Invisible + Then the element "#p2" should not be visible + + @should_fail + Scenario: Invisible fail + Then the element "#p1" should not be visible + + Scenario: Hover + Then I should see "before" within "#p3" + When I hover over "#hover" + Then I should see "after" within "#p3" + + @should_fail + Scenario: Hover fail + Then I should see "before" within "#p3" + When I hover over "#hover3" + + Scenario: At least + Then I should have at least 6 "p" within "body" + + + Scenario: JSON content + Given I am on "content.json" + Then I should see JSON: + """ + { + "test": 1 + } + """ + + @should_fail + Scenario: JSON content fail + Given I am on "content.json" + Then I should see JSON: + """ + { + "test": 2 + } + """ + + Scenario: exact page title + Given I am on "content.html" + Then the page should have a title of "Empty html" + + Scenario: page title matching regex + Given I am on "content.html" + Then the page title should match /Empty/ + Then the page title should match /(\s){1}/ + Then the page title should match /html/ diff --git a/features/form.feature b/features/form.feature new file mode 100644 index 0000000..7b86fe1 --- /dev/null +++ b/features/form.feature @@ -0,0 +1,136 @@ +Feature: Forms + Scenario: Single Fill + Given I am on "form.html" + Then "Timo" should be contained in field "developer" + When I fill in "Johnny" for "developer" + Then "Johnny" should be contained in field "developer" + + @should_fail + Scenario: Single Fill fail + Given I am on "form.html" + Then "Timo" should be contained in field "developer" + When I fill in "Peter" for "developer" + Then "Johnny" should be contained in field "developer" + + Scenario: Single Fill Exactly + Given I am on "form.html" + When I fill in "Johnny Timo" for "developer" + Then "Johnny" should not be equal to field "developer" + Then "Johnny Timo" should be equal to field "developer" + + @should_fail + Scenario: Single Fill Exactly Fail + Given I am on "form.html" + When I fill in "Johnny Timo" for "developer" + Then "Johnny" should be equal to field "developer" + + Scenario: Multi + Given I am on "form.html" + Then "Alex" should be contained in field "manager" + Then "Timo" should be contained in field "developer" + When I fill in the following: + | developer | Alex | + | manager | Johnny | + Then "Johnny" should be contained in field "manager" + Then "Alex" should be contained in field "developer" + + @should_fail + Scenario: Multi fail + Given I am on "form.html" + When I fill in the following: + | developer_fail | Alex | + | manager_fail | Johnny | + + Scenario: Select + Given I am on "form.html" + Then "value2" should be contained in field "selection" + When I select "Value 1" from "selection" + Then "value1" should be contained in field "selection" + + @should_fail + Scenario: Select + Given I am on "form.html" + When I select "Value 3" from "selection" + Then "value1" should be contained in field "selection" + + Scenario: Checkbox + Given I am on "form.html" + Then should not be checked "validation" + When I check "validation" + Then should be checked "validation" + + @should_fail + Scenario: Checkbox fail + Given I am on "form.html" + When I check "validation_fail" + + Scenario: Uncheck + Given I am on "form.html" + Then should not be checked "validation" + When I check "validation" + Then should be checked "validation" + When I uncheck "validation" + Then should not be checked "validation" + + @should_fail + Scenario: Uncheck fail + Given I am on "form.html" + When I check "validation" + When I uncheck "validation2" + + Scenario: Radio + Given I am on "form.html" + When I choose "radio1" + Then should be checked "radio1" + Then should not be checked "radio2" + When I choose "radio2" + Then should be checked "radio2" + Then should not be checked "radio1" + + @should_fail + Scenario: Radio fail + Given I am on "form.html" + When I choose "radio1" + Then should be checked "radio2" + + Scenario: File + Given I am on "form.html" + When I attach the file "features/htdocs/form.html" to "file1" + Then "C:\fakepath\form.html" should be equal to field "file1" + + @should_fail + Scenario: File fail + Given I am on "form.html" + When I attach the file "features/htdocs/form.htm" to "file1" + + Scenario: Blurer + Given I am on "form.html" + Then "before" should be contained in field "blurer" + When I leave the field "blurer" + Then "after" should be contained in field "blurer" + + @should_fail + Scenario: Blurer fail + Given I am on "form.html" + When I leave the field "radio1" + Then "after" should be contained in field "blurer" + + Scenario: Submit + Given I am on "form.html" + When I submit form "f1" + When I wait for 1 second + Then I should have the following query string: + | developer | Timo | + | manager | Alex | + | selection | value2 | + + @should_fail + Scenario: Submit fail + Given I am on "form.html" + When I choose "radio1" + When I submit form "f1" + When I wait for 1 second + Then I should have the following query string: + | developer | Timo | + | manager | Alex | + | selection | value2 | diff --git a/features/htdocs/click.html b/features/htdocs/click.html new file mode 100644 index 0000000..861e419 --- /dev/null +++ b/features/htdocs/click.html @@ -0,0 +1,18 @@ + + + + + Empty html + + +

+

+ +
+ +
+ +

+

clickable

+ + diff --git a/features/htdocs/click1.html b/features/htdocs/click1.html new file mode 100644 index 0000000..5092a7c --- /dev/null +++ b/features/htdocs/click1.html @@ -0,0 +1,20 @@ + + + + + Empty html + + + test +

link1

+ +
    +
  • +

    text

    +
  • +
  • + other +
  • +
+ + diff --git a/features/htdocs/click2.html b/features/htdocs/click2.html new file mode 100644 index 0000000..9dc6161 --- /dev/null +++ b/features/htdocs/click2.html @@ -0,0 +1,10 @@ + + + + + Empty html + + +

link2site

+ + diff --git a/features/htdocs/content.html b/features/htdocs/content.html new file mode 100644 index 0000000..f6fcb2b --- /dev/null +++ b/features/htdocs/content.html @@ -0,0 +1,17 @@ + + + + + Empty html + + +

something

+

23.03.1718

+

234.93.1718

+ +

+ +

before

+

hover

+ + diff --git a/features/htdocs/content.json b/features/htdocs/content.json new file mode 100644 index 0000000..040a593 --- /dev/null +++ b/features/htdocs/content.json @@ -0,0 +1,3 @@ +{ + "test": 1 +} diff --git a/features/htdocs/form.html b/features/htdocs/form.html new file mode 100644 index 0000000..37534e6 --- /dev/null +++ b/features/htdocs/form.html @@ -0,0 +1,26 @@ + + + + + Empty html + + +
+ + + + + + + + + + + +
+ + diff --git a/features/htdocs/misc.html b/features/htdocs/misc.html new file mode 100644 index 0000000..a8b0fd6 --- /dev/null +++ b/features/htdocs/misc.html @@ -0,0 +1,13 @@ + + + + + Empty html + + +

before

+ +

before

+ + + diff --git a/features/htdocs/url.html b/features/htdocs/url.html new file mode 100644 index 0000000..a173d32 --- /dev/null +++ b/features/htdocs/url.html @@ -0,0 +1,10 @@ + + + + + Empty html + + + + + \ No newline at end of file diff --git a/features/misc.feature b/features/misc.feature new file mode 100644 index 0000000..1acd94c --- /dev/null +++ b/features/misc.feature @@ -0,0 +1,18 @@ +Feature: Content Checks + Background: + Given I am on "misc.html" + + Scenario: timer + Then I should see "before" within "#p1" + Then I press "b1" + Then I wait for 1 second + Then I should see "before" within "#p1" + Then I wait for 3 second + Then I should see "after" within "#p1" + + @should_fail + Scenario: timer fail + Then I should see "before" within "#p2" + Then I press "b2" + Then I wait for 1 second + Then I should see "after" within "#p2" diff --git a/features/support/env.rb b/features/support/env.rb new file mode 100644 index 0000000..834673a --- /dev/null +++ b/features/support/env.rb @@ -0,0 +1,35 @@ +# Capybara configuration (using phantomjs) + +require 'capybara/cucumber' +require 'phantomjs' +require 'capybara/poltergeist' +require 'rspec/expectations' + +require 'dkdeploy/cucumber/steps' +require 'dkdeploy/cucumber/support' + +Capybara.register_driver :poltergeist do |app| + Capybara::Poltergeist::Driver.new(app, phantomjs: Phantomjs.path) +end +Capybara.default_driver = :poltergeist +Capybara.javascript_driver = :poltergeist +Capybara.default_selector = :css + +Capybara.app = Rack::Directory.new File.expand_path('../../htdocs', __FILE__) + +# register Capybara +World(Capybara) + +# Workaround for Poltergeist +# +# needs to be deleted when headless browser engine is switched out +# +# Workaround for a bug, in newer poltergeist versions +# see https://github.com/teampoltergeist/poltergeist/issues/754 +# Page object (capybara session) is fine except for status_code always returning nil when we have multiple +# successful page loads in one cucumber run +# Return value of page.visit is always {"state":'success'} +# Although it has been observed that the status_code is returned properly on a 404 error, this work-around is just to be sure +Before do + page.driver.clear_memory_cache +end diff --git a/features/url.feature b/features/url.feature new file mode 100644 index 0000000..5bc99bf --- /dev/null +++ b/features/url.feature @@ -0,0 +1,28 @@ +Feature: URLs + + Scenario: I am on existing page + Given I am on "url.html" + Then I should be on "/url.html" + + @should_fail + Scenario: I should be on fail + Given I am on "url.html" + Then I should be on "/url2.html" + + @should_fail + Scenario: I am on fail + Given I am on "not_exists.html" + + Scenario: Query String + Given I am on "url.html?test=abbrev&again=abnormally" + Then I should be on "/url.html" + Then I should have the following query string: + | test | abbrev | + | again | abnormally | + + @should_fail + Scenario: Query String fail + Given I am on "url.html?test=abbrev&again=abnormally" + Then I should have the following query string: + | test | abbrev3 | + | again | abnormally3 | diff --git a/lib/dkdeploy/cucumber.rb b/lib/dkdeploy/cucumber.rb new file mode 100644 index 0000000..dc3dc14 --- /dev/null +++ b/lib/dkdeploy/cucumber.rb @@ -0,0 +1 @@ +require 'dkdeploy/cucumber/version' diff --git a/lib/dkdeploy/cucumber/step_definitions/click_steps.rb b/lib/dkdeploy/cucumber/step_definitions/click_steps.rb new file mode 100644 index 0000000..15b017b --- /dev/null +++ b/lib/dkdeploy/cucumber/step_definitions/click_steps.rb @@ -0,0 +1,39 @@ +# Click button. +# +# @yieldparam button [String] Name of button. Relative to selector. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/) do |button, selector| + with_scope(selector) do + click_button(button) + end +end + +# Click link. +# +# @yieldparam button [String] Name of link. Relative to selector. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/) do |link, selector| + with_scope(selector) do + click_link(link) + end +end + +# Click xpath. +# +# @yieldparam xpath [String] xpath to link. Relative to selector. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )click xpath "([^"]*)"(?: within "([^"]*)")?$/) do |xpath, selector| + with_scope(selector) do + find(:xpath, xpath).click + end +end + +# Click element. +# +# @yieldparam element [String] Css selector for element. +# @yieldparam selector [String] Define scope per css selector. +When(/^I click on element "([^"]*)"(?: within "([^"]*)")?$/) do |element, selector| + with_scope(selector) do + find(element.to_s).click + end +end diff --git a/lib/dkdeploy/cucumber/step_definitions/content_steps.rb b/lib/dkdeploy/cucumber/step_definitions/content_steps.rb new file mode 100644 index 0000000..eed46bf --- /dev/null +++ b/lib/dkdeploy/cucumber/step_definitions/content_steps.rb @@ -0,0 +1,88 @@ +# Check json body. +# +# @yieldparam expected_json [String] Expected json string. +Then(/^(?:|I )should see JSON:$/) do |expected_json| + require 'json' + expected = JSON.pretty_generate(JSON.parse(expected_json)) + actual = JSON.pretty_generate(JSON.parse(page.text)) + expect(actual).to eq(expected) +end + +# Search text on page. +# +# @yieldparam text [String] Text to search on page. +# @yieldparam selector [String] Define scope per css selector. +Then(/^(?:|I )(should|should not) see "([^"]*)"(?: within "([^"]*)")?$/) do |should_or_not, text, selector| + should_or_not = should_or_not == 'should' + with_scope(selector) do + expect(page).to(should_or_not ? have_content(text) : have_no_content(text)) + end +end + +# Search regex on page. +# +# @yieldparam regexp [String] Regex to search on page. +# @yieldparam selector [String] Define scope per css selector. +Then(%r{^(?:|I )(should|should not) see /([^/]*)/(?: within "([^"]*)")?$}) do |should_or_not, regexp, selector| + regexp = Regexp.new(regexp) + should_or_not = should_or_not == 'should' + with_scope(selector) do + expect(page).to(should_or_not ? have_xpath('//*', text: regexp) : have_no_xpath('//*', text: regexp)) + end +end + +# Search for xpath existance on page. +# +# @yieldparam xpath [String] xpath to search on page. +# @yieldparam selector [String] Define scope per css selector. +Then(/^(?:|I )(should|should not) see xpath "([^"]*)"(?: within "([^"]*)")?$/) do |should_or_not, xpath, selector| + should_or_not = should_or_not == 'should' + with_scope(selector) do + expect(page).to(should_or_not ? have_xpath(xpath) : have_no_xpath(xpath)) + end +end + +# Check if ruby can parse content within scope as element. +# +# @yieldparam selector [String] Define scope per css selector. +Then(/^I should see a date within "([^"]*)"$/) do |selector| + with_scope(selector) do + require 'date' + expect(Date.parse(page.text)).not_to be_nil + end +end + +# Check if element is visible. +# +# @yieldparam element [String] Css selector for element. +Then(/^the element "([^"]*)" (should|should not) be visible$/) do |element, should_or_not| + should_or_not = should_or_not == 'should' + element = find(selector_for(element).to_s, visible: false) + expect(element.visible?).to should_or_not ? be_truthy : be_falsey +end + +# Check if at least x elements are existant +# +# @yieldparam number_of_elements [String] Count of elements. +# @yieldparam selector [String] Css selector for elements. +# @yieldparam parent [String] Define scope per css selector. +Then(/^I should have at least (\d+) "([^"]*)" within "([^"]*)"$/) do |number_of_elements, selector, parent| + with_scope(parent) do + elements = all(selector_for(selector)) + expect(elements.size).to be >= number_of_elements.to_i + end +end + +# Check if page has a certain title. +# +# @yieldparam selector [String] Define page title. +Then(/^the page should have a title of "([^"]*)"$/) do |title| + expect(page).to have_title(title) +end + +# Check if page has a certain title. +# +# @yieldparam selector [String] Define page title. +Then(%r{^the page title should match /([^/]*)/$}) do |title_regex| + expect(page.title).to match(title_regex) +end diff --git a/lib/dkdeploy/cucumber/step_definitions/form_steps.rb b/lib/dkdeploy/cucumber/step_definitions/form_steps.rb new file mode 100644 index 0000000..4f0dd4b --- /dev/null +++ b/lib/dkdeploy/cucumber/step_definitions/form_steps.rb @@ -0,0 +1,146 @@ +# Fill text into form field +# +# @yieldparam field [String] Field name. +# @yieldparam value [String] Field value. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )fill in "([^"]*)" for "([^"]*)"(?: within "([^"]*)")?$/) do |value, field, selector| + with_scope selector do + fill_in field, with: value + end +end + +# Use this to fill in an entire form with data from a table. Example: +# +# When I fill in the following: +# | Account Number | 5002 | +# | Expiry date | 2009-11-01 | +# | Note | Nice guy | +# | Wants Email? | | +# +# TODO: Add support for checkbox, select og option +# based on naming conventions. +# +# @yieldparam selector [String] Define scope per css selector. +# @yieldparam fields [Hash] See above +When(/^(?:|I )fill in the following(?: within "([^"]*)")?:$/) do |selector, fields| + with_scope selector do + fields.rows_hash.each do |name, value| + step %(I fill in "#{value}" for "#{name}") + end + end +end + +# Select value with form select box +# +# @yieldparam value [String] Field value. +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )select "([^"]*)" from "([^"]*)"(?: within "([^"]*)")?$/) do |value, field, selector| + with_scope selector do + select value, from: field + end +end + +# Activate checkbox. +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )check "([^"]*)"(?: within "([^"]*)")?$/) do |field, selector| + with_scope selector do + check field + end +end + +# Deactivate checkbox. +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )uncheck "([^"]*)"(?: within "([^"]*)")?$/) do |field, selector| + with_scope selector do + uncheck field + end +end + +# Activate radiobutton +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )choose "([^"]*)"(?: within "([^"]*)")?$/) do |field, selector| + with_scope selector do + choose field + end +end + +# Attach file to field. +# +# @yieldparam path [String] Path to file. +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +When(/^(?:|I )attach the file "([^"]*)" to "([^"]*)"(?: within "([^"]*)")?$/) do |path, field, selector| + with_scope selector do + attach_file field, path + end +end + +# Check if value of given form field match current value. +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +# @yieldparam value [String] Value to check. +Then(/^"([^"]*)" (should|should not) be contained in field "([^"]*)"(?: within "([^"]*)")?$/) do |value, should_or_not, field, selector| + should_or_not = should_or_not.eql? 'should' + with_scope selector do + field = find_field field + field_value = field.tag_name == 'textarea' ? field.text : field.value + decision = field_value =~ /#{value}/ + expect(decision).to(should_or_not ? be_truthy : be_falsey) + end +end + +# Check if value of given form field match exactly the current value or not. +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +# @yieldparam negate [Boolean] Negative check? +# @yieldparam value [String] Value to check. +Then(/^"([^"]*)" should( not)? be equal to field "([^"]*)"(?: within "([^"]*)")?$/) do |value, negate, field, selector| + expectation = negate ? :not_to : :to + with_scope selector do + field = find_field(field) + field_value = field.tag_name == 'textarea' ? field.text : field.value + if value.nil? # Capybara returns nil for empty fields, so we can't test for equality + expect(field_value).send(expectation, be_blank) + else + expect(field_value).send(expectation, eq(value)) + end + end +end + +# Check if checkbox or radio button is (not) checked. +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +Then(/^(should|should not) be checked "([^"]*)"(?: within "([^"]*)")?$/) do |should_or_not, field, selector| + should_or_not = should_or_not.eql? 'should' + with_scope selector do + field_checked = find_field(field)['checked'] + expect(field_checked).to(should_or_not ? be_truthy : be_falsey) + end +end + +# Lost focus of current field. Execute event "blur". +# +# @yieldparam field [String] Field name. +# @yieldparam selector [String] Define scope per css selector. +When(/^I leave the field "([^"]*)"(?: within "([^"]*)")?$/) do |field, selector| + with_scope selector do + field = find_field(field).trigger 'blur' + end +end + +# Submit form by id. +# +# @yieldparam form_id [String] Form id. +Then(/^I submit form "([^\"]*)"?$/) do |form_id| + page.execute_script "document.forms['#{form_id}'].submit();" +end diff --git a/lib/dkdeploy/cucumber/step_definitions/images.rb b/lib/dkdeploy/cucumber/step_definitions/images.rb new file mode 100644 index 0000000..b59f2a6 --- /dev/null +++ b/lib/dkdeploy/cucumber/step_definitions/images.rb @@ -0,0 +1,11 @@ +# Test for image +# +Then(/^(?:|I )should see the image "([^"]*)"(?: within "([^"]*)")?$/) do |image, selector| + with_scope(selector) do + if page.respond_to? :should + expect(page).to have_xpath("//img[contains(@src,'#{image}')]") + else + assert page.has_xpath?("//img[contains(@src,'#{image}')]") + end + end +end diff --git a/lib/dkdeploy/cucumber/step_definitions/misc_steps.rb b/lib/dkdeploy/cucumber/step_definitions/misc_steps.rb new file mode 100644 index 0000000..d6a00e2 --- /dev/null +++ b/lib/dkdeploy/cucumber/step_definitions/misc_steps.rb @@ -0,0 +1,105 @@ +# Hover given element. +# +# @yieldparam element [String] Css selector for element. +When(/^I hover over "([^"]*)"$/) do |element| + element = find(selector_for(element).to_s) + element.hover +end + +# Test for css attribute value +# +Then(/^the element "([^"]*)" should have a css attribute "([^"]*)" with value "([^"]*)"$/) do |element, css_key, value| + expect(page.evaluate_script("jQuery('#{selector_for(element)}').css('" + css_key + "')")).to have_content(value) +end + +# Test for attribute value +# +Then(/^the element with xpath "([^"]*)" should have an attribute "([^"]*)" with (the exact )?value "([^"]*)"$/) do |element, attr, exact, value| + xpath_attribute_compare = exact ? '[@' + attr + '="' + value + '"]' : '[contains("' + value + '", ' + attr + ')]' + + xpath_path = element + xpath_attribute_compare + + if page.respond_to? :should + expect(page).to have_xpath(xpath_path) + else + assert page.has_xpath?(xpath_path) + end +end + +# Test for class attribute value +# +Then(/^the element "([^"]*)" should have class "([^"]*)"$/) do |element, value| + expect(page.evaluate_script("jQuery('#{selector_for(element)}').hasClass('" + value + "')")).to be_truthy +end + +# Test for absence of class attribute value +# +Then(/^the element "([^"]*)" should not have class "([^"]*)"$/) do |element, value| + expect(page.evaluate_script("jQuery('#{selector_for(element)}').hasClass('" + value + "')")).to be_falsey +end + +Then(/^I should have exactly "(\d+) ([^"]*)" within "([^"]*)"$/) do |number_of_elements, selector, parent| + with_scope(parent) do + elements = all(selector_for(selector)) + expect(elements.size).to eq(number_of_elements.to_i) + end +end + +Then(/^I should have not more than "(\d+) ([^"]*)" within "([^"]*)"$/) do |number_of_elements, selector, parent| + with_scope(parent) do + elements = all(selector_for(selector)) + expect(elements.size).to be <= number_of_elements.to_i + end +end + +Then(/^the element "([^"]*)" should not be visible after waiting (\d) seconds$/) do |element, seconds| + sleep(seconds.to_i) + expect(page.evaluate_script("jQuery('#{selector_for(element)}').is(':visible')")).to be_falsey +end + +Then(/^the element "([^"]*)" should be visible after waiting (\d) seconds$/) do |element, seconds| + sleep(seconds.to_i) + expect(page.evaluate_script("jQuery('#{selector_for(element)}').is(':visible')")).to be_truthy +end + +And(/^all elements "([^"]*)" within "([^"]*)" should have the class "([^"]*)"$/) do |selector, parent, value| + with_scope(parent) do + expect(all(selector_for(selector)).size).to eq(all(selector_for("#{selector}#{value}")).size) + end +end + +Then(/^I native hover the element "([^"]*)"$/) do |element| + element = selector_for(element) + page.find(element).native.hover +end + +# Test the page title +# +Then(/^I should see the page titled "([^\"]*)"$/) do |title| + expect(page).to have_title(title) +end + +# use only for debugging +# +Then(/^I take a screenshot$/) do || + page.save_screenshot('./cucumber_debug_screenshot.png', full: true) +end + +# Save current page. +# +Then(/^show me the page$/) do + save_and_open_page # rubocop:disable Lint/Debugger +end + +# Mark current test as pending. +# +Given(/^PENDING/) do + skip +end + +# Wait x seconds. +# +# @yieldparam seconds [String] Seconds to wait. +Then(/^I wait for (\d+) seconds*$/) do |seconds| + sleep seconds.to_i +end diff --git a/lib/dkdeploy/cucumber/step_definitions/url_steps.rb b/lib/dkdeploy/cucumber/step_definitions/url_steps.rb new file mode 100644 index 0000000..bc82cab --- /dev/null +++ b/lib/dkdeploy/cucumber/step_definitions/url_steps.rb @@ -0,0 +1,31 @@ +# Open given url. +# +# @yieldparam page_name [String] Name of page to open. +Given(/^(?:|I )am on "([^"]*)"$/) do |page_name| + visit path_to(page_name) + status_code = page.status_code.to_s # CODE_TO_OBJ matches string the net http class + expect(Net::HTTPResponse::CODE_TO_OBJ).to include(status_code) + response = Net::HTTPResponse::CODE_TO_OBJ[status_code].allocate # create a dummy instance to easily check the error type + expect(response).not_to be_a Net::HTTPClientError + expect(response).not_to be_a Net::HTTPServerError + expect(response).not_to be_a Net::HTTPError +end + +# Check current url. +# +# @yieldparam page_name [String] Page to test. +Then(/^(?:|I )should be on "([^"]*)"$/) do |page_name| + current_path = URI.parse(current_url).path + expect(current_path).to eq path_to(page_name) +end + +# Check current url query parameters. +# +# @yieldparam expected_pairs [String] GET Parameter to test. +Then(/^(?:|I )should have the following query string:$/) do |expected_pairs| + query = URI.parse(current_url).query + actual_params = query ? CGI.parse(query) : {} + expected_params = {} + expected_pairs.rows_hash.each_pair { |k, v| expected_params[k] = v.split(',') } + expect(actual_params).to eq expected_params +end diff --git a/lib/dkdeploy/cucumber/steps.rb b/lib/dkdeploy/cucumber/steps.rb new file mode 100644 index 0000000..2efc3e9 --- /dev/null +++ b/lib/dkdeploy/cucumber/steps.rb @@ -0,0 +1,2 @@ +# Loads steps from directory `./step_definitions'. +Dir.glob(File.expand_path('../step_definitions/*.rb', __FILE__)).each { |step_file| require step_file } diff --git a/lib/dkdeploy/cucumber/support.rb b/lib/dkdeploy/cucumber/support.rb new file mode 100644 index 0000000..2d85163 --- /dev/null +++ b/lib/dkdeploy/cucumber/support.rb @@ -0,0 +1,3 @@ + +# Loads ruby files from directory `supports'. +Dir.glob(File.expand_path('../support/*.rb', __FILE__)).each { |support_file| require support_file } diff --git a/lib/dkdeploy/cucumber/support/path.rb b/lib/dkdeploy/cucumber/support/path.rb new file mode 100644 index 0000000..ca1c260 --- /dev/null +++ b/lib/dkdeploy/cucumber/support/path.rb @@ -0,0 +1,13 @@ +# Navigation helper module for cucumber steps +# +module NavigationHelpers + # Mapping for path names to real remote path + # + # @param path [String] + # @return [String] + def path_to(path) + path + end +end + +World(NavigationHelpers) diff --git a/lib/dkdeploy/cucumber/support/selectors.rb b/lib/dkdeploy/cucumber/support/selectors.rb new file mode 100644 index 0000000..9d77e28 --- /dev/null +++ b/lib/dkdeploy/cucumber/support/selectors.rb @@ -0,0 +1,15 @@ +# Cucumber step helper function for selector_for +# +module HtmlSelectorsHelpers + # Maps a name to a selector. Used primarily by the + # + # When /^(.+) within (.+)$/ do |step, scope| + # + # step definitions in web_steps.rb + # + def selector_for(locator) + locator + end +end + +World(HtmlSelectorsHelpers) diff --git a/lib/dkdeploy/cucumber/support/with.rb b/lib/dkdeploy/cucumber/support/with.rb new file mode 100644 index 0000000..57f64e5 --- /dev/null +++ b/lib/dkdeploy/cucumber/support/with.rb @@ -0,0 +1,9 @@ +# Cucumber step helper function for with_in +# +module WithinHelpers + def with_scope(locator) + locator ? within(*selector_for(locator)) { yield } : yield + end +end + +World(WithinHelpers) diff --git a/lib/dkdeploy/cucumber/version.rb b/lib/dkdeploy/cucumber/version.rb new file mode 100644 index 0000000..8701363 --- /dev/null +++ b/lib/dkdeploy/cucumber/version.rb @@ -0,0 +1,15 @@ +module Dkdeploy + module Cucumber + # Class for version number + # + class Version + MAJOR = 4 + MINOR = 0 + PATCH = 0 + + def self.to_s + "#{MAJOR}.#{MINOR}.#{PATCH}" + end + end + end +end