Jdlrobson has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/233764

Change subject: QA: My extensions first browser test
......................................................................

QA: My extensions first browser test

Bug: T110198
Change-Id: I38b27161dd8ad2651bb32c00150e3c76f2ae7b30
---
A Gemfile
A Gemfile.lock
A tests/browser/.gitignore
A tests/browser/LocalSettings.php
A tests/browser/README.mediawiki
A tests/browser/environments.yml
A tests/browser/features/internal_survey.feature
A tests/browser/features/step_definitions/common_article_steps.rb
A tests/browser/features/step_definitions/common_steps.rb
A 
tests/browser/features/step_definitions/create_account_failure_messages_steps.rb
A tests/browser/features/step_definitions/create_page_api_steps.rb
A tests/browser/features/step_definitions/diff_steps.rb
A tests/browser/features/step_definitions/editor_steps.rb
A tests/browser/features/step_definitions/editor_ve_steps.rb
A tests/browser/features/step_definitions/issues_steps.rb
A tests/browser/features/step_definitions/language_steps.rb
A tests/browser/features/step_definitions/mainmenu_steps.rb
A tests/browser/features/step_definitions/messages_steps.rb
A tests/browser/features/step_definitions/nearby_steps.rb
A tests/browser/features/step_definitions/notification_steps.rb
A tests/browser/features/step_definitions/pageactions_steps.rb
A tests/browser/features/step_definitions/references_steps.rb
A tests/browser/features/step_definitions/search_steps.rb
A tests/browser/features/step_definitions/special_contributions_steps.rb
A tests/browser/features/step_definitions/special_history_steps.rb
A tests/browser/features/step_definitions/special_userlogin_steps.rb
A tests/browser/features/step_definitions/special_userprofile_steps.rb
A tests/browser/features/step_definitions/special_watchlist_steps.rb
A tests/browser/features/step_definitions/talk_steps.rb
A tests/browser/features/step_definitions/toc_steps.rb
A tests/browser/features/step_definitions/toggling_steps.rb
A tests/browser/features/step_definitions/ui_links_steps.rb
A tests/browser/features/step_definitions/watchstar_steps.rb
A tests/browser/features/support/common_steps.rb
A tests/browser/features/support/env.rb
A tests/browser/features/support/hooks.rb
A tests/browser/features/support/pages/article_page.rb
A tests/browser/features/support/permissions.sqlite
38 files changed, 1,103 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/QuickSurveys 
refs/changes/64/233764/1

diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..06d966a
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,9 @@
+# ruby=ruby-2.1.1
+# ruby-gemset=MobileFrontend
+
+source 'https://rubygems.org'
+
+gem 'chunky_png'
+gem 'jsduck'
+gem 'mediawiki_selenium', '~> 1.5.0'
+gem 'rubocop', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..1c3e4ab
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,112 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    ast (2.0.0)
+    astrolabe (1.3.0)
+      parser (>= 2.2.0.pre.3, < 3.0)
+    builder (3.2.2)
+    childprocess (0.5.6)
+      ffi (~> 1.0, >= 1.0.11)
+    chunky_png (1.3.4)
+    cucumber (1.3.20)
+      builder (>= 2.1.2)
+      diff-lcs (>= 1.1.3)
+      gherkin (~> 2.12)
+      multi_json (>= 1.7.5, < 2.0)
+      multi_test (>= 0.1.2)
+    data_magic (0.21)
+      faker (>= 1.1.2)
+      yml_reader (>= 0.4)
+    diff-lcs (1.2.5)
+    dimensions (1.2.0)
+    domain_name (0.5.24)
+      unf (>= 0.0.5, < 1.0.0)
+    faker (1.4.3)
+      i18n (~> 0.5)
+    faraday (0.9.1)
+      multipart-post (>= 1.2, < 3)
+    faraday-cookie_jar (0.0.6)
+      faraday (>= 0.7.4)
+      http-cookie (~> 1.0.0)
+    ffi (1.9.10)
+    gherkin (2.12.2)
+      multi_json (~> 1.3)
+    headless (2.1.0)
+    http-cookie (1.0.2)
+      domain_name (~> 0.5)
+    i18n (0.7.0)
+    jsduck (5.3.4)
+      dimensions (~> 1.2.0)
+      json (~> 1.8.0)
+      parallel (~> 0.7.1)
+      rdiscount (~> 2.1.6)
+      rkelly-remix (~> 0.0.4)
+    json (1.8.2)
+    mediawiki_api (0.4.1)
+      faraday (~> 0.9, >= 0.9.0)
+      faraday-cookie_jar (~> 0.0, >= 0.0.6)
+    mediawiki_selenium (1.5.0)
+      cucumber (~> 1.3, >= 1.3.20)
+      headless (~> 2.0, >= 2.1.0)
+      json (~> 1.8, >= 1.8.1)
+      mediawiki_api (~> 0.4, >= 0.4.1)
+      page-object (~> 1.0)
+      rest-client (~> 1.6, >= 1.6.7)
+      rspec-expectations (~> 2.14, >= 2.14.4)
+      syntax (~> 1.2, >= 1.2.0)
+      thor (~> 0.19, >= 0.19.1)
+    mime-types (2.6.1)
+    multi_json (1.11.2)
+    multi_test (0.1.2)
+    multipart-post (2.0.0)
+    netrc (0.10.3)
+    page-object (1.1.0)
+      page_navigation (>= 0.9)
+      selenium-webdriver (>= 2.44.0)
+      watir-webdriver (>= 0.6.11)
+    page_navigation (0.9)
+      data_magic (>= 0.14)
+    parallel (0.7.1)
+    parser (2.2.0.3)
+      ast (>= 1.1, < 3.0)
+    powerpack (0.1.0)
+    rainbow (2.0.0)
+    rdiscount (2.1.8)
+    rest-client (1.8.0)
+      http-cookie (>= 1.0.2, < 2.0)
+      mime-types (>= 1.16, < 3.0)
+      netrc (~> 0.7)
+    rkelly-remix (0.0.7)
+    rspec-expectations (2.99.2)
+      diff-lcs (>= 1.1.3, < 2.0)
+    rubocop (0.29.1)
+      astrolabe (~> 1.3)
+      parser (>= 2.2.0.1, < 3.0)
+      powerpack (~> 0.1)
+      rainbow (>= 1.99.1, < 3.0)
+      ruby-progressbar (~> 1.4)
+    ruby-progressbar (1.7.1)
+    rubyzip (1.1.7)
+    selenium-webdriver (2.46.2)
+      childprocess (~> 0.5)
+      multi_json (~> 1.0)
+      rubyzip (~> 1.0)
+      websocket (~> 1.0)
+    syntax (1.2.0)
+    thor (0.19.1)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.7.1)
+    watir-webdriver (0.8.0)
+      selenium-webdriver (>= 2.46.2)
+    websocket (1.2.2)
+    yml_reader (0.5)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  chunky_png
+  jsduck
+  mediawiki_selenium (~> 1.5.0)
+  rubocop
diff --git a/tests/browser/.gitignore b/tests/browser/.gitignore
new file mode 100644
index 0000000..5509614
--- /dev/null
+++ b/tests/browser/.gitignore
@@ -0,0 +1,5 @@
+# Puppet-managed dependencies for browser tests
+.bundle
+.gem
+.ruby-version
+screenshots
diff --git a/tests/browser/LocalSettings.php b/tests/browser/LocalSettings.php
new file mode 100644
index 0000000..49e04a9
--- /dev/null
+++ b/tests/browser/LocalSettings.php
@@ -0,0 +1,21 @@
+<?php
+$wgQuickSurveysConfig = array(
+       array(
+               "name" => "internal example survey",
+               "type" => "internal",
+               "question" => 
"ext-quicksurveys-example-internal-survey-question",
+               "answers" => array(
+                       "positive" => 
"ext-quicksurveys-example-internal-survey-answer-positive",
+                       "neutral" => 
"ext-quicksurveys-example-internal-survey-answer-neutral",
+                       "negative"=> 
"ext-quicksurveys-example-internal-survey-answer-negative"
+               ),
+               "schema" => "QuickSurveysResponse",
+               "enabled" => false,
+               "coverage" => "100",
+               "description" => "yo",
+               "platform" => array(
+                       "desktop" => array( "stable" ),
+                       "mobile" => array( "stable", "beta", "alpha" ),
+               ),
+       )
+);
\ No newline at end of file
diff --git a/tests/browser/README.mediawiki b/tests/browser/README.mediawiki
new file mode 100644
index 0000000..afa2982
--- /dev/null
+++ b/tests/browser/README.mediawiki
@@ -0,0 +1,2 @@
+= Setup =
+Please include the LocalSettings.php file in your MediaWiki instance.
diff --git a/tests/browser/environments.yml b/tests/browser/environments.yml
new file mode 100644
index 0000000..b6e4d95
--- /dev/null
+++ b/tests/browser/environments.yml
@@ -0,0 +1,45 @@
+# Customize this configuration as necessary to provide defaults for various
+# test environments.
+#
+# The set of defaults to use is determined by the MEDIAWIKI_ENVIRONMENT
+# environment variable.
+#
+#   export MEDIAWIKI_ENVIRONMENT=mw-vagrant-host
+#   bundle exec cucumber
+#
+# Additional variables set by the environment will override the corresponding
+# defaults defined here.
+#
+#   export MEDIAWIKI_ENVIRONMENT=mw-vagrant-host
+#   export MEDIAWIKI_USER=Selenium_user2
+#   bundle exec cucumber
+#
+mw-vagrant-host: &default
+  mediawiki_url: http://127.0.0.1:8080/wiki/
+  user_factory: true
+
+barry:
+  browser: phantomjs
+  user_factory: false
+  # mediawiki_url: Will be set manually
+
+mw-vagrant-guest:
+  mediawiki_url: http://127.0.0.1/wiki/
+  user_factory: true
+
+beta:
+  mediawiki_url: http://en.m.wikipedia.beta.wmflabs.org/wiki/
+  mediawiki_user: Selenium_user
+  # mediawiki_password: SET THIS IN THE ENVIRONMENT!
+
+test2:
+  mediawiki_url: http://test2.wikipedia.org/wiki/
+  mediawiki_user: Selenium_user
+  # mediawiki_password: SET THIS IN THE ENVIRONMENT!
+
+integration:
+  browser: chrome
+  user_factory: true
+  # mediawiki_url: JENKINS WILL SET THIS
+
+default: *default
diff --git a/tests/browser/features/internal_survey.feature 
b/tests/browser/features/internal_survey.feature
new file mode 100644
index 0000000..1247ec2
--- /dev/null
+++ b/tests/browser/features/internal_survey.feature
@@ -0,0 +1,8 @@
+@chrome @en.m.wikipedia.beta.wmflabs.org @firefox @test2.m.wikipedia.org 
@vagrant
+Feature: Create failure messages
+  Background:
+    Given the quick survey test pages are installed
+        And I am on the "Quick survey test 1" page with the quick survey flag 
enabled
+
+  Scenario: Internal survey is visible
+    Then I should see the survey
diff --git a/tests/browser/features/step_definitions/common_article_steps.rb 
b/tests/browser/features/step_definitions/common_article_steps.rb
new file mode 100644
index 0000000..c685eba
--- /dev/null
+++ b/tests/browser/features/step_definitions/common_article_steps.rb
@@ -0,0 +1,68 @@
+Given(/^I click continue$/) do
+  on(ArticlePage).continue_button_element.when_present.click
+end
+
+Given(/^I click submit$/) do
+  on(ArticlePage) do |page|
+    page.spinner_loading_element.when_not_present
+    page.submit_button_element.when_present.click
+  end
+end
+
+Given(/^I click the escape button$/) do
+  on(ArticlePage).escape_button_element.when_present.click
+end
+
+Given(/^I switch to desktop$/) do
+  on(ArticlePage).desktop_link_element.click
+end
+
+When(/^I click on the history link in the last modified bar$/) do
+  on(ArticlePage).last_modified_bar_history_link_element.when_present.click
+  expect(on(SpecialHistoryPage).side_list_element.when_present(10)).to 
be_visible
+end
+
+When(/^I click on the page$/) do
+  on(ArticlePage).content_wrapper_element.click
+end
+
+When(/^I click the unwatch star$/) do
+  on(ArticlePage).unwatch_star_element.when_present.click
+end
+
+When(/^I click the watch star$/) do
+  on(ArticlePage).watch_star_element.when_present.click
+end
+
+Then(/^I should see a toast notification$/) do
+  expect(on(ArticlePage).toast_element.when_present(10)).to be_visible
+end
+
+Then(/^I should see a toast error$/) do
+  expect(on(ArticlePage).toast_element.when_present.class_name).to match 
'error'
+end
+
+Then /^I should see a drawer with message "(.+)"$/ do |text|
+  expect(on(ArticlePage).drawer_element.when_present.text).to match text
+end
+
+Then(/^I should see the error box message "(.+)"$/) do |error_message|
+  expect(on(ArticlePage).error_message).to match (error_message)
+end
+
+Then(/^the text of the first heading should be "(.*)"$/) do |title|
+  on(ArticlePage) do |page|
+    page.wait_until do
+      page.first_heading_element.when_present.text.include? title
+    end
+    expect(page.first_heading_element.when_present.text).to match title
+  end
+end
+
+Then /^the watch star should be selected$/ do
+  expect(on(ArticlePage).unwatch_star_element).to be_visible
+end
+
+Then /^the watch star should not be selected$/ do
+  expect(on(ArticlePage).watch_star_element).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/common_steps.rb 
b/tests/browser/features/step_definitions/common_steps.rb
new file mode 100644
index 0000000..e460af6
--- /dev/null
+++ b/tests/browser/features/step_definitions/common_steps.rb
@@ -0,0 +1,109 @@
+Given /^I am in beta mode$/ do
+  visit(MainPage) do |page|
+    page_uri = URI.parse(page.page_url_value)
+
+    # A domain is explicitly given to avoid a bug in earlier versions of Chrome
+    domain = page_uri.host == 'localhost' ? nil : page_uri.host
+    browser.cookies.add 'mf_useformat', 'true', domain: domain
+    browser.cookies.add 'optin', 'beta', domain: domain
+
+    page.refresh
+  end
+end
+
+Given /^I am logged in as a new user$/ do
+  step 'I am on the "Main Page" page'
+  step 'I click on "Log in" in the main navigation menu'
+  # FIXME: Actually create a new user instead of using an existing one
+  on(SpecialUserLoginPage).login_with('Selenium_newuser', password)
+end
+
+Given(/^I am logged in as a user with a > (\d+) edit count$/) do |count|
+  api.meta(:userinfo, uiprop: 'editcount').data['editcount'].upto(count.to_i) 
do |n|
+    api.create_page("Ensure #{user} edit count - #{n + 1}", 'foo')
+  end
+
+  visit(SpecialUserLoginPage).login_with(user, password)
+end
+
+Given(/^I am logged into the mobile website$/) do
+  step 'I am using the mobile site'
+  visit(LoginPage).login_with(user, password, false)
+  # avoids login failing (see https://phabricator.wikimedia.org/T109593)
+  expect(on(ArticlePage).is_authenticated_element.when_present(20)).to exist
+end
+
+Given(/^I am on a page that does not exist$/) do
+  name = 'NewPage' + Time.now.to_i.to_s
+  visit(ArticlePage, using_params: { article_name: name })
+end
+
+Given(/^I am on the random page$/) do
+  visit(ArticlePage, using_params: { article_name: @random_string })
+end
+
+Given(/^I am on the sign-up page$/) do
+  visit(SpecialUserLoginPage).create_account_link_element.when_present.click
+end
+
+Given(/^I am on the "(.+)" page$/) do |article|
+  # Ensure we do not cause a redirect
+  article = article.gsub(/ /, '_')
+  visit(ArticlePage, using_params: { article_name: article })
+end
+
+Given(/^I am using the mobile site$/) do
+  visit(MainPage) do |page|
+    page_uri = URI.parse(page.page_url_value)
+
+    domain = page_uri.host == 'localhost' ? nil : page_uri.host
+    browser.cookies.add 'mf_useformat', 'true', domain: domain
+
+    page.refresh
+  end
+end
+
+Given(/^I am viewing the site in mobile mode$/) do
+  browser.window.resize_to(320, 480)
+end
+
+Given(/^I am viewing the site in tablet mode$/) do
+  # Use numbers significantly larger than 768px to account for browser chrome
+  browser.window.resize_to(1280, 1024)
+end
+
+Given(/^I choose to create an account$/) do
+  on(SpecialUserLoginPage).create_account_link_element.when_present.click
+end
+
+Given(/^my browser doesn't support JavaScript$/) do
+  browser_factory.override(browser_user_agent: 'Opera/9.80 (J2ME/MIDP; Opera 
Mini/9.80 (S60; SymbOS; Opera Mobi/23.348; U; en) Presto/2.5.25 Version/10.54')
+end
+
+Given(/^the "(.*?)" page is protected\.$/) do |page|
+  api.protect_page(page, 'MobileFrontend Selenium test protected this page')
+end
+
+When(/^I click the browser back button$/) do
+  on(ArticlePage).back
+end
+
+When(/^I say Cancel in the confirm dialog$/) do
+  on(ArticlePage).confirm(false) {}
+end
+
+When(/^I say OK in the confirm dialog$/) do
+  on(ArticlePage).confirm(true) do
+  end
+end
+
+When(/^I visit the page "(.*?)" with hash "(.*?)"$/) do |article, hash|
+  # Ensure we do not cause a redirect
+  article = article.gsub(/ /, '_')
+  visit(ArticlePage, using_params: { article_name: article, hash: hash })
+end
+
+Then(/^there should be a red link with text "(.+)"$/) do |text|
+  # FIXME: Switch to link_element when red links move to stable
+  expect(on(ArticlePage).content_wrapper_element.link_element(text: 
text).when_present(10)).to be_visible
+end
diff --git 
a/tests/browser/features/step_definitions/create_account_failure_messages_steps.rb
 
b/tests/browser/features/step_definitions/create_account_failure_messages_steps.rb
new file mode 100644
index 0000000..6a4bc8b
--- /dev/null
+++ 
b/tests/browser/features/step_definitions/create_account_failure_messages_steps.rb
@@ -0,0 +1,16 @@
+When(/^I sign up with two different passwords$/) do
+  on(SpecialUserLoginPage) do |page|
+    page.username = 'some_username'
+    page.password = 's0me decent password'
+    page.confirm_password = 's0me wrong password'
+    page.signup_submit
+  end
+end
+
+Then(/^I should see an error indicating they do not match$/) do
+  expect(on(SpecialUserLoginPage).error_box).to match('The passwords you 
entered do not match')
+end
+
+Then(/^I should still be on the sign-up page$/) do
+  expect(on(SpecialUserLoginPage).first_heading).to match('Create account')
+end
diff --git a/tests/browser/features/step_definitions/create_page_api_steps.rb 
b/tests/browser/features/step_definitions/create_page_api_steps.rb
new file mode 100644
index 0000000..6d343e0
--- /dev/null
+++ b/tests/browser/features/step_definitions/create_page_api_steps.rb
@@ -0,0 +1,93 @@
+# export MEDIAWIKI_API_URL = http://en.wikipedia.beta.wmflabs.org/w/api.php
+
+Given(/^I create a random page using the API$/) do
+  api.create_page @random_string, @random_string
+end
+
+Given(/^I go to a page that has references$/) do
+  wikitext = "MobileFrontend is a MediaWiki extension.<ref>Test reference</ref>
+
+==References==
+<references />"
+
+  api.create_page 'Selenium References test page', wikitext
+  step 'I am on the "Selenium References test page" page'
+end
+
+Given(/^I go to a page that has sections$/) do
+  wikitext = "==Section 1==
+Hello world
+== Section 2 ==
+Section 2.
+=== Section 2A ===
+Section 2A.
+== Section 3 ==
+Section 3.
+"
+
+  api.create_page 'Selenium section test page2', wikitext
+  step 'I am on the "Selenium section test page2" page'
+end
+
+Given(/^I am on a page which has cleanup templates$/) do
+  wikitext = <<-END.gsub(/^ */, '')
+      This page is used by Selenium to test MediaWiki functionality.
+
+      <table class="metadata plainlinks ambox ambox-content ambox-Refimprove" 
role="presentation">
+        <tr>
+          <td class="mbox-image">[[File:Question_book-new.svg|thumb]]</td>
+          <td class="mbox-text">
+            <span class="mbox-text-span">This article \'\'\'needs additional 
citations for [[Wikipedia:Verifiability|verification]]\'\'\'. <span 
class="hide-when-compact">Please help [[Selenium page issues test 
page#editor/0|improve this article]] by 
[[Help:Introduction_to_referencing/1|adding citations to reliable sources]]. 
Unsourced material may be challenged and removed.</span> <small><i>(October 
2012)</i></small></span>
+          </td>
+        </tr>
+      </table>
+    END
+
+  api.create_page 'Selenium page issues test page', wikitext
+  step 'I am on the "Selenium page issues test page" page'
+end
+
+Given(/^the page "(.*?)" exists$/) do |title|
+  api.create_page title, 'Test is used by Selenium web driver'
+  step 'I am on the "' + title + '" page'
+end
+
+Given(/^at least one article with geodata exists$/) do
+  api.create_page 'Selenium geo test page', <<-end
+This page is used by Selenium to test geo related features.
+
+{{#coordinates:43|-75|primary}}
+  end
+end
+
+Given(/^I go to a page that has languages$/) do
+  wikitext = 'This page is used by Selenium to test language related features.
+
+[[es:Selenium language test page]]'
+
+  api.create_page 'Selenium language test page', wikitext
+  step 'I am on the "Selenium language test page" page'
+end
+
+Given(/^the wiki has a terms of use$/) do
+  api.create_page 'MediaWiki:mobile-frontend-terms-url', 'Terms_of_use'
+  api.create_page 'MediaWiki:mobile-frontend-terms-text', 'Terms of use'
+end
+
+Given(/^the page "(.*?)" exists and has at least "(\d+)" edits$/) do |title, 
min_edit_count|
+  # Page must first exist before we can get edit count information
+  step 'the page "' + title + '" exists'
+  min_edit_count = min_edit_count.to_i
+  visit(ArticlePage, using_params: { article_name: title.gsub(' ', '_') + 
'?action=info' })
+  on(ArticlePage) do |page|
+    (page.edit_count.gsub(',', '').to_i + 1).upto(min_edit_count) do |n|
+      api.create_page title, "Test is used by Selenium web driver edit ##{n}"
+    end
+  end
+end
+
+Given(/^I visit a protected page$/) do
+  api.create_page 'Selenium protected test 2', 'Test is used by Selenium web 
driver'
+  step 'the "Selenium protected test 2" page is protected.'
+  step 'I am on the "Selenium protected test 2" page'
+end
diff --git a/tests/browser/features/step_definitions/diff_steps.rb 
b/tests/browser/features/step_definitions/diff_steps.rb
new file mode 100644
index 0000000..2fcbee9
--- /dev/null
+++ b/tests/browser/features/step_definitions/diff_steps.rb
@@ -0,0 +1,7 @@
+Then(/^I should see "(.*?)" as added content$/) do |text|
+  expect(on(DiffPage).inserted_content_element.text).to eq text
+end
+
+Then(/^I should see "(.*?)" as removed content$/) do |text|
+  expect(on(DiffPage).deleted_content_element.text).to eq text
+end
diff --git a/tests/browser/features/step_definitions/editor_steps.rb 
b/tests/browser/features/step_definitions/editor_steps.rb
new file mode 100644
index 0000000..fff8809
--- /dev/null
+++ b/tests/browser/features/step_definitions/editor_steps.rb
@@ -0,0 +1,56 @@
+Given(/^the page "(.+)" has the following edits:$/) do |page, table|
+  page = page.gsub(' ', '_')
+  table.rows.each { |(text)| api.edit(title: page, text: text) }
+end
+
+When(/^I clear the editor$/) do
+  on(ArticlePage).editor_textarea_element.when_present.clear
+end
+
+When(/^I click the edit button$/) do
+  on(ArticlePage).edit_link_element.when_present.click
+end
+
+When(/^I click the editor mode switcher button$/) do
+  on(ArticlePage).overlay_editor_mode_switcher_element.when_present.click
+end
+
+When(/^I click the source editor button$/) do
+  on(ArticlePage).source_editor_button_element.when_present.click
+end
+
+When(/^I click the VisualEditor button$/) do
+  on(ArticlePage).visual_editor_button_element.when_present.click
+end
+
+When(/^I click the wikitext editor overlay close button$/) do
+  on(ArticlePage).editor_overlay_close_button_element.when_present.click
+end
+
+When(/^I do not see the wikitext editor overlay$/) do
+  on(ArticlePage).editor_overlay_element.when_not_visible
+end
+
+When(/^I see the wikitext editor overlay$/) do
+  on(ArticlePage).editor_textarea_element.when_present
+end
+
+When(/^I type "(.+)" into the editor$/) do |text|
+  on(ArticlePage).editor_textarea_element.when_present.send_keys(text)
+end
+
+Then(/^I should not see the read in another language button$/) do
+  expect(on(ArticlePage).language_button_element).not_to be_visible
+end
+
+Then(/^I should not see the wikitext editor overlay$/) do
+  expect(on(ArticlePage).editor_overlay_element).not_to be_visible
+end
+
+Then(/^I see the anonymous editor warning$/) do
+  expect(on(ArticlePage).anon_editor_warning_element.when_present).to 
be_visible
+end
+
+Then /^I should see the read in another language button$/ do
+  expect(on(ArticlePage).language_button_element.when_present).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/editor_ve_steps.rb 
b/tests/browser/features/step_definitions/editor_ve_steps.rb
new file mode 100644
index 0000000..42f1541
--- /dev/null
+++ b/tests/browser/features/step_definitions/editor_ve_steps.rb
@@ -0,0 +1,44 @@
+Given(/^I am editing a new article with VisualEditor$/) do
+  api.create_page 'Selenium Test Edit', ''
+  step 'I am on the "Selenium Test Edit" page'
+  step 'I click the edit button'
+  step 'I click the editor mode switcher button'
+  step 'I click the VisualEditor button'
+  step 'VisualEditor has loaded'
+end
+
+Given(/^VisualEditor has loaded$/) do
+  on(ArticlePage).editor_ve_element.when_present(20)
+end
+
+When(/^I edit the article using VisualEditor$/) do
+  on(ArticlePage) do |page|
+    @text_to_type = "text-#{rand(32**8).to_s(32)}"
+    page.editor_ve_element.when_present.send_keys(' ')
+    page.editor_ve_element.send_keys(@text_to_type)
+    page.wait_until { page.continue_button_element.enabled? }
+    page.continue_button
+    page.wait_until { page.submit_button_element.enabled? }
+    page.confirm(true) { page.submit_button }
+    sleep 2 # this gets around a race condition bug in ChromeDriver where both 
the confirm and the toast are in the page at once, and Chrome reports either 
"stale element reference: element is not attached to the page document" or 
"Element does not exist in cache"
+    page.wait_until { page.toast.include?('Your edit was saved') }
+    page.wait_until { page.content_element.visible? }
+  end
+end
+
+When(/^I switch to editing the source$/) do
+  step 'I click the editor mode switcher button'
+  step 'I click the source editor button'
+end
+
+Then(/^I should no longer see the VisualEditor$/) do
+  expect(on(ArticlePage).editor_ve_element).to_not be_visible
+end
+
+Then(/^I should see the article content$/) do
+  expect(on(ArticlePage).content_element.when_present).to be_visible
+end
+
+Then(/^I should see the edit reflected in the article content$/) do
+  expect(on(ArticlePage).content).to match @text_to_type
+end
diff --git a/tests/browser/features/step_definitions/issues_steps.rb 
b/tests/browser/features/step_definitions/issues_steps.rb
new file mode 100644
index 0000000..c9ed395
--- /dev/null
+++ b/tests/browser/features/step_definitions/issues_steps.rb
@@ -0,0 +1,23 @@
+When(/^I click the overlay issue close button$/) do
+  on(ArticlePage).overlay_close_button_element.when_present.click
+end
+
+When(/^I click the page issues stamp$/) do
+  on(ArticlePage).issues_stamp_element.when_present.click
+end
+
+When(/^I see the issues overlay$/) do
+  on(ArticlePage).overlay_element.when_present
+end
+
+When(/^this page has issues$/) do
+  on(ArticlePage).issues_stamp_element.when_present
+end
+
+Then(/^I should not see the issues overlay$/) do
+  expect(on(ArticlePage).overlay_element).not_to be_visible
+end
+
+Then(/^I should see the issues overlay$/) do
+  expect(on(ArticlePage).overlay_element.when_present).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/language_steps.rb 
b/tests/browser/features/step_definitions/language_steps.rb
new file mode 100644
index 0000000..12179b9
--- /dev/null
+++ b/tests/browser/features/step_definitions/language_steps.rb
@@ -0,0 +1,15 @@
+When /^I click the language button$/ do
+  on(ArticlePage).language_button_element.when_present.click
+end
+
+When(/^I click the language overlay close button$/) do
+  on(ArticlePage).overlay_languages_element.when_present.button_element(class: 
'cancel').click
+end
+
+When(/^I see the language overlay$/) do
+  on(ArticlePage).overlay_languages_element.when_present
+end
+
+Then(/^I should not see the languages overlay$/) do
+  expect(on(ArticlePage).overlay_languages_element).not_to be_visible
+end
diff --git a/tests/browser/features/step_definitions/mainmenu_steps.rb 
b/tests/browser/features/step_definitions/mainmenu_steps.rb
new file mode 100644
index 0000000..d59df40
--- /dev/null
+++ b/tests/browser/features/step_definitions/mainmenu_steps.rb
@@ -0,0 +1,28 @@
+When(/^I click on the main navigation button$/) do
+  on(ArticlePage).mainmenu_button_element.click
+end
+
+When(/^I click on "(.*?)" in the main navigation menu$/) do |text|
+  step 'I click on the main navigation button'
+  on(ArticlePage).navigation_element.link_element(text: 
text).when_visible.click
+end
+
+Then(/^I should see a link to "(.*?)" in the main navigation menu$/) do |text|
+  expect(on(ArticlePage).navigation_element.link_element(text: text)).to 
be_visible
+end
+
+Then(/^I should not see a link to "(.*?)" in the main navigation menu$/) do 
|text|
+  expect(on(ArticlePage).navigation_element.link_element(text: text)).not_to 
be_visible
+end
+
+Then(/^I should see a link to the about page$/) do
+  expect(on(ArticlePage).about_link_element).to be_visible
+end
+
+Then(/^I should see a link to the disclaimer$/) do
+  expect(on(ArticlePage).disclaimer_link_element).to be_visible
+end
+
+Then(/^I should see a link to my user profile page in the main navigation 
menu$/) do
+  expect(on(ArticlePage).navigation_element.link_element(href: 
/UserProfile\/#{user}/, text: user_label)).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/messages_steps.rb 
b/tests/browser/features/step_definitions/messages_steps.rb
new file mode 100644
index 0000000..2d6fcda
--- /dev/null
+++ b/tests/browser/features/step_definitions/messages_steps.rb
@@ -0,0 +1,3 @@
+Then('I should see the error "$message"') do |message|
+  expect(on(ArticlePage).error_message).to match(message)
+end
diff --git a/tests/browser/features/step_definitions/nearby_steps.rb 
b/tests/browser/features/step_definitions/nearby_steps.rb
new file mode 100644
index 0000000..80e7541
--- /dev/null
+++ b/tests/browser/features/step_definitions/nearby_steps.rb
@@ -0,0 +1,20 @@
+Given(/^I give permission for the page to access my location$/) do
+  unless ENV['NEARBY_FIREFOX']
+    puts "NEARBY_FIREFOX environment variable is not defined. This test won't 
work without it!"
+  end
+end
+
+When(/^I click a nearby result$/) do
+  on(ArticlePage).page_list_element.when_present(20).link_element(class: 
'title').click
+end
+
+Then(/^I should see at least one result in the nearby items list$/) do
+  on(ArticlePage) do |page|
+    expect(page.page_list_element.when_present(20)).to be_visible
+    expect(page.page_list_element.link_element(class: 'title')).to be_visible
+  end
+end
+
+Then(/^I should see the page preview overlay$/) do
+  expect(on(ArticlePage).overlay_element.when_present(20).div_element(class: 
'content')).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/notification_steps.rb 
b/tests/browser/features/step_definitions/notification_steps.rb
new file mode 100644
index 0000000..e2e5e35
--- /dev/null
+++ b/tests/browser/features/step_definitions/notification_steps.rb
@@ -0,0 +1,37 @@
+When /^I click on the notification icon$/ do
+  on(ArticlePage) do |page|
+    page.wait_until do
+      # Wait for JS to hijack standard link
+      # TODO: If this approach works well, we should implement general
+      # `wait_for_resource` and `resource_ready?` helper methods in
+      # mw-selenium, and document this pattern on mw.org
+      browser.execute_script("return 
mw.loader.getState('mobile.notifications') === 'ready'")
+    end
+
+    page.notifications_button_element.when_present.click
+  end
+end
+
+Given(/^I have no notifications$/) do
+  expect(on(ArticlePage).notifications_button_element.when_present).to 
be_visible
+  # This is somewhat hacky, but I don't want this test making use of Echo's 
APIs which may change
+  browser.execute_script("$( function () { $( '.notification-count' ).hide(); 
} );")
+end
+
+When(/^I click the notifications overlay close button$/) do
+  sleep 1
+  on(ArticlePage).notifications_overlay_close_button_element.when_present.click
+end
+
+When(/^the notifications overlay appears$/) do
+  on(ArticlePage).notifications_overlay_element.when_present
+end
+
+Then(/^after (.+) seconds I should not see the notifications overlay$/) do 
|seconds|
+  sleep seconds.to_i
+  expect(on(ArticlePage).notifications_overlay_element).not_to be_visible
+end
+
+Then(/^I should see the notifications overlay$/) do
+  expect(on(ArticlePage).notifications_overlay_element.when_present).to 
be_visible
+end
diff --git a/tests/browser/features/step_definitions/pageactions_steps.rb 
b/tests/browser/features/step_definitions/pageactions_steps.rb
new file mode 100644
index 0000000..2485187
--- /dev/null
+++ b/tests/browser/features/step_definitions/pageactions_steps.rb
@@ -0,0 +1,7 @@
+Given(/^I click the edit icon holder$/) do
+  on(ArticlePage).edit_button_holder_element.when_present.click
+end
+
+Then(/^I should not see an upload an image to this page button$/) do
+  expect(on(ArticlePage).upload_button_element).not_to be_visible
+end
diff --git a/tests/browser/features/step_definitions/references_steps.rb 
b/tests/browser/features/step_definitions/references_steps.rb
new file mode 100644
index 0000000..f28b94f
--- /dev/null
+++ b/tests/browser/features/step_definitions/references_steps.rb
@@ -0,0 +1,14 @@
+When(/^I click on a reference$/) do
+  on(ArticlePage) do |page|
+    page.reference_element.click
+    page.reference_drawer_element.when_present
+  end
+end
+
+Then(/^I should see the reference drawer$/) do
+  expect(on(ArticlePage).reference_drawer_element).to be_visible
+end
+
+Then(/^I should not see the reference drawer$/) do
+  expect(on(ArticlePage).reference_drawer_element.when_not_present).to be_nil
+end
diff --git a/tests/browser/features/step_definitions/search_steps.rb 
b/tests/browser/features/step_definitions/search_steps.rb
new file mode 100644
index 0000000..5f7592a
--- /dev/null
+++ b/tests/browser/features/step_definitions/search_steps.rb
@@ -0,0 +1,79 @@
+When(/^I click a search result$/) do
+  on(ArticlePage).search_result_element.when_present.click
+end
+
+When(/^I click the placeholder search box$/) do
+  on(ArticlePage).search_box_placeholder_element.when_present.click
+  # this check is needed to accommodate for the hack for opening the virtual
+  # keyboard (see comments in search.js)
+  on(ArticlePage).wait_until do
+    on(ArticlePage).current_url.end_with? '#/search'
+  end
+end
+
+When(/^I click the search button$/) do
+  on(ArticlePage).search_button_element.when_present.click
+end
+
+When(/^I see the search in pages button$/) do
+  expect(on(ArticlePage).search_within_pages_element.when_visible).to 
be_visible
+end
+
+When(/^I click the search in pages button$/) do
+  on(ArticlePage).search_within_pages_element.when_present.click
+end
+
+When(/^I click a search watch star$/) do
+  on(ArticlePage).search_watchstars_element.when_present.click
+end
+
+When(/^I press the enter key$/) do
+  on(ArticlePage).search_box2_element.when_present.send_keys :enter
+end
+
+When(/^I click the search overlay close button$/) do
+  on(ArticlePage).search_overlay_close_button_element.click
+end
+
+When(/^I see the search overlay$/) do
+  on(ArticlePage).search_overlay_element.when_present
+end
+
+When(/^I type into search box "(.+)"$/) do |search_term|
+  on(ArticlePage) do |page|
+    if page.search_box2_element.exists?
+      # Type in JavaScript mode
+      page.search_box2 = search_term
+    else
+      page.search_box_placeholder = search_term
+    end
+  end
+end
+
+Then(/^I should not see the search overlay$/) do
+  expect(on(ArticlePage).search_overlay_element).not_to be_visible
+end
+
+Then(/^I should see a list of search results$/) do
+  expect(on(SearchPage).list_of_results_element.when_present(10)).to be_visible
+end
+
+Then(/^I should see the search button$/) do
+  expect(on(ArticlePage).search_button_element.when_present).to be_visible
+end
+
+When(/^I should see the search overlay$/) do
+  expect(on(ArticlePage).search_overlay_element.when_present).to be_visible
+end
+
+Then(/^search results should contain "(.+)"$/) do |text|
+  expect(on(ArticlePage).search_result_element.when_present.text).to eq text
+end
+
+Then(/^I should not see '#\/search' in URL$/) do
+  expect(on(ArticlePage).current_url.end_with? '#/search').to be false
+end
+
+Then(/^I should see a toast$/) do
+  expect(on(ArticlePage).toast_element.when_present).to be_visible
+end
diff --git 
a/tests/browser/features/step_definitions/special_contributions_steps.rb 
b/tests/browser/features/step_definitions/special_contributions_steps.rb
new file mode 100644
index 0000000..e4e6ec0
--- /dev/null
+++ b/tests/browser/features/step_definitions/special_contributions_steps.rb
@@ -0,0 +1,23 @@
+Given(/^I am on my contributions page$/) do
+  visit(SpecialContributionsPage, using_params: { user: user })
+end
+
+When(/^I click the link in the header bar$/) do
+  on(SpecialContributionsPage).content_header_bar_link_element.click
+end
+
+Then(/^I should see a list of page contributions$/) do
+  expect(on(SpecialContributionsPage).side_list_element).to be_visible
+end
+
+Then(/^I should see a summary of the last contribution$/) do
+  expect(on(SpecialContributionsPage).last_contribution_element).to be_visible
+end
+
+Then(/^the last contribution summary should not show the username$/) do
+  expect(on(SpecialHistoryPage).last_contribution_username_element).not_to 
be_visible
+end
+
+Then(/^the last contribution summary should show the title of the page 
edited$/) do
+  expect(on(SpecialContributionsPage).last_contribution_title_element).to 
be_visible
+end
diff --git a/tests/browser/features/step_definitions/special_history_steps.rb 
b/tests/browser/features/step_definitions/special_history_steps.rb
new file mode 100644
index 0000000..21697b9
--- /dev/null
+++ b/tests/browser/features/step_definitions/special_history_steps.rb
@@ -0,0 +1,28 @@
+When(/^I click the more link$/) do
+  on(SpecialHistoryPage).more_link_element.click
+end
+
+When(/^I open the latest diff$/) do
+  on(SpecialHistoryPage).last_contribution_link_element.click
+  expect(on(SpecialMobileDiffPage).user_info_element.when_present(20)).to 
be_visible
+end
+
+Then(/^I should see a more button$/) do
+  expect(on(SpecialHistoryPage).more_link_element).to be_visible
+end
+
+Then(/^the last contribution summary should not show the title of the page 
edited$/) do
+  expect(on(SpecialHistoryPage).last_contribution_title_element).not_to 
be_visible
+end
+
+Then(/^the last contribution summary should show the edit summary$/) do
+  expect(on(SpecialHistoryPage).last_contribution_edit_summary_element).to 
be_visible
+end
+
+Then(/^the last contribution summary should show the time of the last edit$/) 
do
+  expect(on(SpecialHistoryPage).last_contribution_timestamp_element).to 
be_visible
+end
+
+Then(/^the last contribution summary should show the username who made the 
last edit$/) do
+  expect(on(SpecialHistoryPage).last_contribution_username_element).to 
be_visible
+end
diff --git a/tests/browser/features/step_definitions/special_userlogin_steps.rb 
b/tests/browser/features/step_definitions/special_userlogin_steps.rb
new file mode 100644
index 0000000..3328fa4
--- /dev/null
+++ b/tests/browser/features/step_definitions/special_userlogin_steps.rb
@@ -0,0 +1,15 @@
+Then(/^I should not see a message warning me I am already logged in$/) do
+  expect(on(SpecialUserLoginPage).warning_box_element).not_to be_visible
+end
+
+Then(/^I should see a message box at the top of the login page$/) do
+  expect(on(SpecialUserLoginPage).message_box_element).to be_visible
+end
+
+Then(/^I should see a password reset link$/) do
+  expect(on(SpecialUserLoginPage).password_reset_element).to be_visible
+end
+
+Then /^I should see the log in prompt message "(.+)"$/ do |text|
+  expect(on(SpecialUserLoginPage).message_box_element.when_present.text).to 
match text
+end
diff --git 
a/tests/browser/features/step_definitions/special_userprofile_steps.rb 
b/tests/browser/features/step_definitions/special_userprofile_steps.rb
new file mode 100644
index 0000000..c837028
--- /dev/null
+++ b/tests/browser/features/step_definitions/special_userprofile_steps.rb
@@ -0,0 +1,27 @@
+Given(/^I visit my user profile page$/) do
+  visit(SpecialUserProfilePage, using_params: { user: user })
+end
+
+Then(/^I should be on my user profile page$/) do
+  expect(on(SpecialUserProfilePage).activity_heading_element).to be_visible
+end
+
+Then(/^I should see my last edit$/) do
+  expect(on(SpecialUserProfilePage).last_edit_element).to be_visible
+end
+
+Then(/^there should be a link to my contributions$/) do
+  expect(on(SpecialUserProfilePage).contributions_link_element).to be_visible
+end
+
+Then(/^there should be a link to my talk page$/) do
+  expect(on(SpecialUserProfilePage).talk_link_element).to be_visible
+end
+
+Then(/^there should be a link to my uploads$/) do
+  expect(on(SpecialUserProfilePage).uploads_link_element).to be_visible
+end
+
+Then(/^there should be a link to my user page$/) do
+  expect(on(SpecialUserProfilePage).user_page_link_element).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/special_watchlist_steps.rb 
b/tests/browser/features/step_definitions/special_watchlist_steps.rb
new file mode 100644
index 0000000..54a6051
--- /dev/null
+++ b/tests/browser/features/step_definitions/special_watchlist_steps.rb
@@ -0,0 +1,36 @@
+Given(/^I have recently edited pages on my watchlist$/) do
+  api.create_page 'Selenium Watchlist', 'Edit by #{user}'
+  api.action('watch', token_type: 'watch', titles: 'Selenium Watchlist')
+end
+
+When(/^the Pages tab is selected$/) do
+  expect(on(WatchlistPage).selected_pages_tab_element.when_present).to 
be_visible
+end
+
+When(/^I click the Pages tab$/) do
+  on(WatchlistPage).pages_tab_link_element.when_present.click
+end
+
+When(/^I switch to the list view of the watchlist$/) do
+  on(WatchlistPage).list_link_element.click
+end
+
+When(/^I switch to the modified view of the watchlist$/) do
+  on(WatchlistPage).feed_link_element.click
+end
+
+Then(/^I should see a list of diff summary links$/) do
+  expect(on(WatchlistPage).page_list_diffs_element.when_present).to be_visible
+end
+
+Then(/^I should see a list of pages I am watching$/) do
+  expect(on(WatchlistPage).page_list_a_to_z_element.when_present).to be_visible
+end
+
+Then(/^the a to z button should be selected$/) do
+  expect(on(WatchlistPage).list_link_element.parent.element.class_name).to 
match 'active'
+end
+
+Then(/^the modified button should be selected$/) do
+  expect(on(WatchlistPage).feed_link_element.parent.element.class_name).to 
match 'active'
+end
diff --git a/tests/browser/features/step_definitions/talk_steps.rb 
b/tests/browser/features/step_definitions/talk_steps.rb
new file mode 100644
index 0000000..20fa073
--- /dev/null
+++ b/tests/browser/features/step_definitions/talk_steps.rb
@@ -0,0 +1,24 @@
+When(/^I click the talk button$/) do
+  on(ArticlePage).talk_element.when_present.click
+end
+
+When(/^I click the add discussion button$/) do
+  on(ArticlePage).talkadd_element.when_present.click
+end
+
+Then(/^I should see the talk overlay$/) do
+  expect(on(ArticlePage).overlay_heading_element.when_present.text).to match 
'Talk'
+end
+
+Then(/^there should be no talk button$/) do
+  expect(on(ArticlePage).talk_element).not_to be_visible
+end
+
+Then(/^there should be an add discussion button$/) do
+  # give overlay time to fully load
+  expect(on(ArticlePage).talkadd_element.when_present(10)).to be_visible
+end
+
+Then(/^there should be no add discussion button$/) do
+  except(on(ArticlePage).talkadd_element).not_to be_visible
+end
diff --git a/tests/browser/features/step_definitions/toc_steps.rb 
b/tests/browser/features/step_definitions/toc_steps.rb
new file mode 100644
index 0000000..2a7d746
--- /dev/null
+++ b/tests/browser/features/step_definitions/toc_steps.rb
@@ -0,0 +1,10 @@
+Then(/^I should not see the table of contents$/) do
+  on(ArticlePage) do |page|
+    page.toc_element.when_not_visible
+    expect(page.toc_element).not_to be_visible
+  end
+end
+
+Then(/^I should see the table of contents$/) do
+  expect(on(ArticlePage).toc_element.when_present(10)).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/toggling_steps.rb 
b/tests/browser/features/step_definitions/toggling_steps.rb
new file mode 100644
index 0000000..9094929
--- /dev/null
+++ b/tests/browser/features/step_definitions/toggling_steps.rb
@@ -0,0 +1,15 @@
+When(/^I click on the first collapsible section heading$/) do
+  on(ArticlePage).first_section_element.when_present.click
+end
+
+Then(/^I should not see the content of the first section$/) do
+  expect(on(ArticlePage).first_section_content_element).not_to be_visible
+end
+
+Then(/^I should see the content of the first section$/) do
+  expect(on(ArticlePage).first_section_content_element.when_present(10)).to 
be_visible
+end
+
+Then(/^the heading element with id "(.*?)" should be visible$/) do |id|
+  expect(on(ArticlePage).span_element(id: id).when_present(10)).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/ui_links_steps.rb 
b/tests/browser/features/step_definitions/ui_links_steps.rb
new file mode 100644
index 0000000..aa8433b
--- /dev/null
+++ b/tests/browser/features/step_definitions/ui_links_steps.rb
@@ -0,0 +1,23 @@
+Then(/^I should see a link to the privacy page$/) do
+  expect(on(ArticlePage).privacy_link_element).to be_visible
+end
+
+Then(/^I should see a link to the terms of use$/) do
+  expect(on(ArticlePage).terms_link_element).to be_visible
+end
+
+Then(/^I should see the history link$/) do
+  expect(on(ArticlePage).edit_history_link_element).to be_visible
+end
+
+Then(/^I should see the last modified bar history link$/) do
+  expect(on(ArticlePage).last_modified_bar_history_link_element).to be_visible
+end
+
+Then(/^I should see the license link$/) do
+  expect(on(ArticlePage).license_link_element).to be_visible
+end
+
+Then(/^I should see the switch to desktop link$/) do
+  expect(on(ArticlePage).desktop_link_element).to be_visible
+end
diff --git a/tests/browser/features/step_definitions/watchstar_steps.rb 
b/tests/browser/features/step_definitions/watchstar_steps.rb
new file mode 100644
index 0000000..c6366e2
--- /dev/null
+++ b/tests/browser/features/step_definitions/watchstar_steps.rb
@@ -0,0 +1,24 @@
+Given(/^I am viewing a watched page$/) do
+  api.create_page 'Selenium mobile watch test', 'watch test'
+  api.watch_page 'Selenium mobile watch test'
+  step 'I am on the "Selenium mobile watch test" page'
+end
+
+Given(/^I am viewing an unwatched page$/) do
+  api.create_page 'Selenium mobile watch test', 'watch test'
+  api.unwatch_page 'Selenium mobile watch test'
+  step 'I am on the "Selenium mobile watch test" page'
+end
+
+Then(/^I should see a toast with message about watching the page$/) do
+  expect(on(ArticlePage).toast_element.when_present.text).to match 'Added 
Selenium mobile watch test to your watchlist'
+end
+
+Then(/^I should see a toast with message about unwatching the page$/) do
+  on(ArticlePage) do |page|
+    page.wait_until do
+      page.text.include? 'Removed' # Chrome needs this, FF does not
+    end
+    expect(page.toast_element.when_present.text).to match 'Removed Selenium 
mobile watch test from your watchlist'
+  end
+end
diff --git a/tests/browser/features/support/common_steps.rb 
b/tests/browser/features/support/common_steps.rb
new file mode 100644
index 0000000..2dbb6ad
--- /dev/null
+++ b/tests/browser/features/support/common_steps.rb
@@ -0,0 +1,40 @@
+Given(/^the quick survey test pages are installed$/) do
+  wikitext = "<!-- test for page with no infobox and no image -->
+'''Arcathius''' ({{lang-el| ο Άρκαθίας}} means in Greek ''ruler'', <ref>Book 
1</ref>
+flourished second half of 2nd century BC and first half of 1st century BC) was 
a Prince from the
+[[Kingdom of Pontus]]. He was a prince of [[Persian people|Persian]] and
+[[Macedonia (Greece)|Greek Macedonian ancestry]]. Arcathius was among the sons 
born to
+King [[Mithridates VI of Pontus]] and 
+[[Laodice (sister-wife of Mithridates VI of Pontus)|his sister-wife Laodice]].
+<ref>Book 1</ref>
+He was born and raised in the [[Kingdom of Pontus]].
+
+Arcathius joined his father’s generals [[Neoptolemus (Pontic 
general)|Neoptolemus]] and
+[[Archelaus (general)|Archelaus]] with 10,000 horses which he brought from 
[[Lesser Armenia]]
+at the commencement of the [[First Mithridatic War]] (89 BC–85 BC).
+<ref>http://wikipedia.org</ref> He participated in the great
+battle fought near the [[Gök River|Amnias River]] in [[Paphlagonia]] which
+King [[Nicomedes IV of Bithynia]] was defeated.<ref>http://wikipedia.org</ref> 
+
+He was a brilliant cavalry commander. In 86 BC, Arcathius invaded Macedonia 
with a
+separate army and completely conquered the country. He then proceeded to march 
against
+the Roman Dictator [[Lucius Cornelius Sulla]], but on his way, Arcathius died 
near 
+Mount Tisaion.
+<ref>Some book</ref>
+Arcathius was a happy person in character and his father considered him as a
+beloved son and as a victorious hero in war.
+<ref>Some book</ref>
+
+==References==
+<references />"
+
+  api.create_page 'Quick survey test 1', wikitext
+end
+
+Given(/^I am on the "(.*?)" page with the quick survey flag enabled$/) do 
|arg1|
+  visit(ArticlePage, using_params: { article_name: arg1, query_string: 
'?quicksurvey=true' })
+end
+
+Then(/^I should see the survey$/) do
+  expect(on(ArticlePage).survey_element.when_present).to be_visible
+end
diff --git a/tests/browser/features/support/env.rb 
b/tests/browser/features/support/env.rb
new file mode 100644
index 0000000..5eff4ce
--- /dev/null
+++ b/tests/browser/features/support/env.rb
@@ -0,0 +1,4 @@
+require 'mediawiki_selenium'
+
+require 'mediawiki_selenium/support'
+require 'mediawiki_selenium/step_definitions'
diff --git a/tests/browser/features/support/hooks.rb 
b/tests/browser/features/support/hooks.rb
new file mode 100644
index 0000000..56f159b
--- /dev/null
+++ b/tests/browser/features/support/hooks.rb
@@ -0,0 +1,6 @@
+# Needed for cucumber --dry-run -f stepdefs
+require_relative 'env'
+
+Before('@skip') do |scenario|
+  scenario.skip_invoke!
+end
diff --git a/tests/browser/features/support/pages/article_page.rb 
b/tests/browser/features/support/pages/article_page.rb
new file mode 100644
index 0000000..d993f2d
--- /dev/null
+++ b/tests/browser/features/support/pages/article_page.rb
@@ -0,0 +1,7 @@
+class ArticlePage
+  include PageObject
+
+
+  page_url "<%= URI.encode(params[:article_name]) 
%><%=params[:query_string]%><%= params[:hash] %>"
+  div(:survey, css: '.panel')
+end
diff --git a/tests/browser/features/support/permissions.sqlite 
b/tests/browser/features/support/permissions.sqlite
new file mode 100644
index 0000000..06d2031
--- /dev/null
+++ b/tests/browser/features/support/permissions.sqlite
Binary files differ

-- 
To view, visit https://gerrit.wikimedia.org/r/233764
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I38b27161dd8ad2651bb32c00150e3c76f2ae7b30
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/QuickSurveys
Gerrit-Branch: dev
Gerrit-Owner: Jdlrobson <jrob...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to