diff --git a/features/event_management.feature b/features/event_management.feature index fc8accb32..d709cbad0 100644 --- a/features/event_management.feature +++ b/features/event_management.feature @@ -69,8 +69,7 @@ Scenario: Company registration request in PETS Then I should see "Signed in successfully." Then I am in User's browser Given I am on the home page - And I click the "Register as an Employer" button - And I wait 1 second + And I click the "Register as an Employer" link Then I should see "Request Company Registration" And I fill in the fields: | Company Name | Widgets, Inc. | diff --git a/features/job_skills.feature b/features/job_skills.feature index 9a3245ab9..cdf837ab9 100644 --- a/features/job_skills.feature +++ b/features/job_skills.feature @@ -23,9 +23,9 @@ Background: adding job to database | Skill3 | Long haul driver with Class C license | Given the following jobs exist: - | title | description | company | creator | shift | skills | city | - | Job1 | About job1. | Widgets Inc. | jane@ymail.com | Day | Skill1, Skill2 | city1 | - | Job2 | About job2. | Widgets Inc. | carter@ymail.com | Day | Skill3 | city2 | + | title | description | company | creator | shift | skills | city | + | Job1 | About job1. | Widgets Inc. | jane@ymail.com | Day | Skill1 | city1 | + | Job2 | About job2. | Widgets Inc. | carter@ymail.com | Day | Skill3 | city2 | @javascript @@ -63,16 +63,16 @@ Scenario: Edit job and change associated skills And I am logged in as "carter@ymail.com" with password "qwerty123" And I click the "Jobs" link Then I click the "Job1" link - And I wait 1 second And I should see "Skill1" - And I should see "Skill2" Then I click the "Edit Job" link And I wait 1 second - And I click the second "remove job skill" link + And I click the first "remove job skill" link And I click the "Add Job Skill" link - And I select "Skill3" in second select list "Name:" + And I select "Skill3" in first select list "Name:" + And I click the "Add Job Skill" link + And I select "Skill2" in second select list "Name:" And I press "Update" Then I should see "Job1 has been updated successfully." - And I should see "Skill1" + And I should see "Skill2" And I should see "Skill3" - And I should not see "Skill2" + And I should not see "Skill1" diff --git a/features/pages.feature b/features/pages.feature new file mode 100644 index 000000000..5292c349f --- /dev/null +++ b/features/pages.feature @@ -0,0 +1,15 @@ +Feature: a user clicking contact is verified by a recaptcha + + As prospective user of PETS + I want to make contact and send a message + +@selenium_browser +Scenario: click the contact link + Given I am on the home page + And I click the first "Contact" link + And I fill in "Full Name" with "Charlie Bucket" + And I fill in "Email" with "cb@gmail.commetplus.org" + And I fill in "Message" with "Hi Metplus" + And I click the recaptcha + And I click the "Send Message" button + Then I should see "Your message was sent successfully!" diff --git a/features/step_definitions/users_steps.rb b/features/step_definitions/users_steps.rb index 8a9d0c610..0b160acc0 100644 --- a/features/step_definitions/users_steps.rb +++ b/features/step_definitions/users_steps.rb @@ -1,3 +1,4 @@ +include ServiceStubHelpers::RecaptchaValidator Given(/^I have the following Job Seekers$/) do |table| # table is a table.hashes.keys # => [:email, :password] table.hashes.each do |seeker| @@ -11,14 +12,15 @@ end When(/^I visit profile for "(\w+)"$/) do |first_name| - user = User.find_by_first_name!(first_name) - visit "/users/edit.#{user.id}" + user = User.find_by_first_name!(first_name) + visit "/users/edit.#{user.id}" end -Then(/^I should verify the change of first_name "(.*?)", last_name "(.*?)" and phone "(.*?)"$/) do |first_name, last_name, phone| - user = User.find_by_first_name(first_name) - expect(user.last_name).to eql last_name - expect(user.phone).to eql phone +Then(/^I\sshould\sverify\sthe\schange\sof\sfirst_name\s"(.*?)",\s + last_name\s"(.*?)"\sand\sphone\s"(.*?)"$/x) do |first_name, last_name, phone| + user = User.find_by_first_name(first_name) + expect(user.last_name).to eql last_name + expect(user.phone).to eql phone end Then(/^I should( not)? be remembered$/) do |not_remembered| @@ -33,10 +35,26 @@ expect(person_type.expires).to be_future end end -j + Then(/^I should be logged out$/) do cookies = page.driver.cookies expect(cookies['person_type']).to be nil expect(cookies['user_id']).to be nil end +Given(/^I click the recaptcha$/) do + + # Override server-side verification of recaptcha response: + # (the recaptcha widget is being served as a separate document within + # an iframe. There does not seem to be a way to simulate user entry + # (checking a box) for the "I'm not a robot" prompt. This is by design + # since the intent is to prevent automated entry that simulates a human. + # Thus, 1) we won't attempt to "check the box", and 2) we will override + # server-side verification to just return true (== verified by Google). + + class RecaptchaService + def self.verify(_, _) + true + end + end +end diff --git a/features/step_definitions/web_steps.rb b/features/step_definitions/web_steps.rb index 940686c51..078f086d3 100644 --- a/features/step_definitions/web_steps.rb +++ b/features/step_definitions/web_steps.rb @@ -162,6 +162,7 @@ def search_text(text) end And(/^show me the page$/) do + save_and_open_page end When(/^(?:I|they) click and accept the "([^"]*)" button$/) do |button_text| diff --git a/features/support/env.rb b/features/support/env.rb index 52d93ddc2..5dd86339b 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -8,6 +8,9 @@ require 'email_spec/cucumber' require 'capybara/cucumber' require 'capybara/poltergeist' +require 'webmock/cucumber' +require 'cucumber/rspec/doubles' +require './spec/support/service_stub_helpers' # Capybara defaults to CSS3 selectors rather than XPath. # If you'd prefer to use XPath, just uncomment this line and adjust any @@ -68,40 +71,18 @@ Cucumber::Rails::Database.javascript_strategy = :truncation Capybara.default_max_wait_time = 10 -# see http://blog.pixarea.com/2013/02/locking-the-firefox-version-when-testing-rails-with-capybara-and-selenium/ for -# details on how to set this up -Capybara.register_driver :selenium do |app| - require 'selenium/webdriver' - # FIREFOX_BINARY_PATH (if used) needs to be the path to the FF executable - Selenium::WebDriver::Firefox::Binary.path = - ENV['FIREFOX_BINARY_PATH'] || Selenium::WebDriver::Firefox::Binary.path - Capybara::Selenium::Driver.new(app, browser: :firefox) -end -Before '@selenium' do - Capybara.current_session.current_window.resize_to(1024, 768) +Capybara.register_driver :selenium_browser do |app| + Capybara::Selenium::Driver.new( + app, + browser: :chrome + ) end -if ENV['IN_BROWSER'] - # On demand: non-headless tests via Selenium/WebDriver - # To run the scenarios in browser (default: Firefox), use the following command line: - # IN_BROWSER=true bundle exec cucumber - # or (to have a pause of 1 second between each step): - # IN_BROWSER=true PAUSE=1 bundle exec cucumber - Capybara.default_driver = :selenium - AfterStep do - sleep(ENV['PAUSE'] || 0).to_i - end -else - # DEFAULT: headless tests with poltergeist/PhantomJS - Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new( - app, - window_size: [1280, 1024], - js_errors: false, - timeout: 10 - # debug: true - ) - end - Capybara.javascript_driver = :poltergeist +Capybara.register_driver :selenium do |app| + Capybara::Selenium::Driver.new( + app, + browser: :chrome, + args: ['headless'] + ) end diff --git a/features/support/hooks.rb b/features/support/hooks.rb index 4aa912f2a..55ecf4616 100644 --- a/features/support/hooks.rb +++ b/features/support/hooks.rb @@ -1,19 +1,22 @@ Before('@javascript') do + # Currently, poltergeist is preferred for headless tests Capybara.current_driver = :poltergeist end -Before('@selenium') do - # Note that poltergeist is the preferred web driver for tests that - # require javascript support - Capybara.javascript_driver = :selenium +Before('@selenium_browser') do + # Use this hook for running tests with visible browser ("non-headless") + Capybara.current_driver = :selenium_browser end -After('@selenium') do - Capybara.reset_sessions! - Capybara.javascript_driver = :poltergeist +Before('@selenium') do + # Use this hook for running headless tests using Chrome + Capybara.current_driver = :selenium end -After('@javascript') do +After('@javascript, @selenium_browser, @selenium') do Capybara.reset_sessions! + # force Chrome to quit after each scenario: + page.driver.quit if Capybara.current_driver == :selenium || + Capybara.current_driver == :selenium_browser Capybara.current_driver = :rack_test end diff --git a/spec/services/recaptcha_service_spec.rb b/spec/services/recaptcha_service_spec.rb new file mode 100644 index 000000000..d3232b5d8 --- /dev/null +++ b/spec/services/recaptcha_service_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +RSpec.describe RecaptchaService, type: :model do + context 'User login with captcha' do + describe 'verify recaptcha response' do + let!(:captcha_response) { 'XX' } + let!(:ip_address) { '168.140.181.4' } + before :each do + stub_recaptcha_verify + end + it 'should login with correct recaptcha' do + expect(RecaptchaService.verify(captcha_response, ip_address)).to be true + end + it 'is disallowed when no recaptcha' do + expect(RecaptchaService.verify(nil, ip_address)).to be false + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5d7d3827e..cc7e8cf40 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ -# This file was generated by the `rails generate rspec:install` command. Conventionally, all -# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# This file was generated by the `rails generate rspec:install` command. +# Conventionally, allspecs live under a `spec` directory, which RSpec adds to +# the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause # this file to always be loaded, without a need to explicitly require it in any # files. @@ -25,6 +26,7 @@ Dir['./spec/support/**/*.rb'].each { |f| require f } include ServiceStubHelpers::EmailValidator +include ServiceStubHelpers::RecaptchaValidator # WebMock config # Uncomment to disable Webmock and access external services during test: @@ -63,56 +65,54 @@ mocks.verify_partial_doubles = true end -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # These two settings work together to allow you to limit a spec run - # to individual examples or groups you care about by tagging them with - # `:focus` metadata. When nothing is tagged with `:focus`, all examples - # get run. - config.filter_run :focus - config.run_all_when_everything_filtered = true - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax - # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching - config.disable_monkey_patching! - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = 'doc' - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end - config.example_status_persistence_file_path = "spec/examples.txt" + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # These two settings work together to allow you to limit a spec run + # # to individual examples or groups you care about by tagging them with + # # `:focus` metadata. When nothing is tagged with `:focus`, all examples + # # get run. + # config.filter_run :focus + # config.run_all_when_everything_filtered = true + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax + # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching + # config.disable_monkey_patching! + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = 'doc' + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed + config.example_status_persistence_file_path = 'spec/examples.txt' config.include FactoryGirl::Syntax::Methods config.before(:suite) do @@ -146,5 +146,5 @@ config.include(EmailSpec::Helpers) config.include(EmailSpec::Matchers) - config.pattern = "**/*_spec.rb" + config.pattern = '**/*_spec.rb' end diff --git a/spec/support/service_stub_helpers.rb b/spec/support/service_stub_helpers.rb index 61b1f13cf..797f07cf6 100644 --- a/spec/support/service_stub_helpers.rb +++ b/spec/support/service_stub_helpers.rb @@ -163,9 +163,9 @@ def stub_cruncher_job_create_retry_auth_fail .to_raise(RestClient::Unauthorized) end - def stub_cruncher_job_create_fail(result_code) + def stub_cruncher_job_create_fail(resultCode) stub_request(:post, url_job_create) - .to_return(body: "{\"resultCode\":\"#{result_code}\"}", status: 200, + .to_return(body: "{\"resultCode\":\"#{resultCode}\"}", status: 200, headers: { 'Content-Type' => 'application/json' }) end @@ -250,9 +250,9 @@ def stub_cruncher_match_jobs_retry_auth_fail .to_raise(RestClient::Unauthorized) end - def stub_cruncher_match_jobs_fail(result_code) + def stub_cruncher_match_jobs_fail(resultCode) stub_request(:get, url_match_jobs) - .to_return(body: "{\"resultCode\": \"#{result_code}\"}", status: 200, + .to_return(body: "{\"resultCode\": \"#{resultCode}\"}", status: 200, headers: { 'Content-Type': 'application/json' }) end @@ -325,4 +325,13 @@ def stub_email_validate_error .to_raise(RuntimeError) end end + module RecaptchaValidator + def stub_recaptcha_verify + body_json = "{\n \"success\": true, + \n \"challenge_ts\": \"2017-07-10T05:00:00Z\", + \n \"hostname\": \"localhost.c9users.io\"\n }" + stub_request(:any, %r{https://www.google.com/recaptcha/api/siteverify}) + .to_return(body: body_json) + end + end end