mediawiki_selenium 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/.yardopts +1 -0
- data/README.md +43 -318
- data/RELEASES.md +353 -0
- data/features/login_helper.feature +15 -0
- data/features/rspec.feature +64 -0
- data/features/saucelabs.feature +7 -2
- data/features/screenshots.feature +21 -0
- data/features/step_definitions/browser_steps.rb +4 -0
- data/features/step_definitions/environment_steps.rb +2 -2
- data/features/step_definitions/login_helper_steps.rb +11 -0
- data/features/step_definitions/rspec_steps.rb +32 -0
- data/features/step_definitions/saucelabs_steps.rb +18 -0
- data/features/step_definitions/screenshot_steps.rb +3 -0
- data/lib/mediawiki_selenium.rb +6 -3
- data/lib/mediawiki_selenium/browser_factory/base.rb +5 -1
- data/lib/mediawiki_selenium/cucumber.rb +4 -0
- data/lib/mediawiki_selenium/{support → cucumber}/env.rb +2 -4
- data/lib/mediawiki_selenium/cucumber/hooks.rb +73 -0
- data/lib/mediawiki_selenium/cucumber/sauce.rb +22 -0
- data/lib/mediawiki_selenium/{support/modules → cucumber}/strict_pending.rb +0 -0
- data/lib/mediawiki_selenium/environment.rb +41 -20
- data/lib/mediawiki_selenium/{support/modules → helpers}/api_helper.rb +15 -0
- data/lib/mediawiki_selenium/{support/modules → helpers}/headless_helper.rb +21 -13
- data/lib/mediawiki_selenium/helpers/login_helper.rb +36 -0
- data/lib/mediawiki_selenium/helpers/screenshot_helper.rb +58 -0
- data/lib/mediawiki_selenium/{support/modules → helpers}/user_factory_helper.rb +0 -0
- data/lib/mediawiki_selenium/pages.rb +3 -0
- data/lib/mediawiki_selenium/{support/pages → pages}/login_page.rb +0 -0
- data/lib/mediawiki_selenium/{support/pages → pages}/random_page.rb +0 -0
- data/lib/mediawiki_selenium/{support/pages → pages}/reset_preferences_page.rb +0 -0
- data/lib/mediawiki_selenium/remote_browser_factory.rb +12 -3
- data/lib/mediawiki_selenium/rspec.rb +51 -0
- data/lib/mediawiki_selenium/rspec/environment.rb +36 -0
- data/lib/mediawiki_selenium/rspec/features.rb +49 -0
- data/lib/mediawiki_selenium/step_definitions/login_steps.rb +1 -3
- data/lib/mediawiki_selenium/version.rb +1 -1
- data/mediawiki_selenium.gemspec +2 -2
- data/spec/environment_spec.rb +12 -0
- data/spec/screenshot_helper_spec.rb +95 -0
- data/templates/tests/browser/features/support/env.rb +2 -1
- metadata +52 -38
- data/lib/mediawiki_selenium/support.rb +0 -4
- data/lib/mediawiki_selenium/support/hooks.rb +0 -92
- data/lib/mediawiki_selenium/support/pages.rb +0 -3
- data/lib/mediawiki_selenium/support/sauce.rb +0 -26
@@ -27,6 +27,21 @@ module MediawikiSelenium
|
|
27
27
|
lookup(:mediawiki_api_url, default: -> { api_url_from(lookup(:mediawiki_url)) })
|
28
28
|
end
|
29
29
|
|
30
|
+
# Queries MW via the API to check for missing extension dependencies.
|
31
|
+
#
|
32
|
+
# @param dependencies [Array] Extension dependencies.
|
33
|
+
#
|
34
|
+
# @return [Array] Missing extensions.
|
35
|
+
#
|
36
|
+
def missing_extensions(dependencies)
|
37
|
+
return [] if dependencies.empty?
|
38
|
+
|
39
|
+
extensions = api.meta(:siteinfo, siprop: 'extensions').data['extensions']
|
40
|
+
extensions = extensions.map { |ext| ext['name'] }.compact.map(&:downcase)
|
41
|
+
|
42
|
+
dependencies - extensions
|
43
|
+
end
|
44
|
+
|
30
45
|
# Ensures the given alternative account exists by attempting to create it
|
31
46
|
# via the API. Any errors related to the account already existing are
|
32
47
|
# swallowed.
|
@@ -96,21 +96,29 @@ module MediawikiSelenium
|
|
96
96
|
# @see Environment#teardown
|
97
97
|
#
|
98
98
|
def teardown(info = {})
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
99
|
+
artifacts = {}
|
100
|
+
|
101
|
+
begin
|
102
|
+
artifacts = super
|
103
|
+
ensure
|
104
|
+
if @_headless_capture
|
105
|
+
if info[:status] == :failed
|
106
|
+
dir = File.absolute_path(headless_capture_path)
|
107
|
+
FileUtils.mkdir_p(dir)
|
108
|
+
|
109
|
+
filename = "#{(info[:name] || 'scenario').tr("#{File::SEPARATOR}\000", '-')}.mp4"
|
110
|
+
filename = File.join(dir, filename)
|
111
|
+
|
112
|
+
@_headless_display.video.stop_and_save(filename)
|
113
|
+
|
114
|
+
artifacts[filename] = 'video/mp4'
|
115
|
+
else
|
116
|
+
@_headless_display.video.stop_and_discard
|
117
|
+
end
|
112
118
|
end
|
113
119
|
end
|
120
|
+
|
121
|
+
artifacts
|
114
122
|
end
|
115
123
|
end
|
116
124
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module MediawikiSelenium
|
4
|
+
# Expepiates logging in to wikis by authenticating with the MW API and
|
5
|
+
# transfering the returned cookie to the current browser.
|
6
|
+
#
|
7
|
+
module LoginHelper
|
8
|
+
# Authenticate with the current wiki's API and save the resulting cookie
|
9
|
+
# in the current browser.
|
10
|
+
#
|
11
|
+
# @param cookies [Hash] Extra cookies to set.
|
12
|
+
#
|
13
|
+
def log_in(cookies = {})
|
14
|
+
visit_wiki do |url|
|
15
|
+
uri = URI.parse(url)
|
16
|
+
|
17
|
+
options = {}
|
18
|
+
options[:domain] = uri.host unless ['localhost', '127.0.0.1'].include?(uri.host)
|
19
|
+
|
20
|
+
api.cookies.each do |cookie|
|
21
|
+
browser.cookies.add cookie.name, cookie.value, options.merge({
|
22
|
+
secure: cookie.secure,
|
23
|
+
path: cookie.path,
|
24
|
+
expires: cookie.expires
|
25
|
+
})
|
26
|
+
end
|
27
|
+
|
28
|
+
cookies.each do |name, value|
|
29
|
+
browser.cookies.add name.to_s, value, options
|
30
|
+
end
|
31
|
+
|
32
|
+
browser.refresh
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module MediawikiSelenium
|
4
|
+
# Adds support to {Environment} for taking screenshots of the current
|
5
|
+
# browser window. If `screenshot_failures` is set to `true` for the current
|
6
|
+
# environment, one is automatically taken at the end of each failed
|
7
|
+
# test case.
|
8
|
+
#
|
9
|
+
module ScreenshotHelper
|
10
|
+
# Takes a screenshot of the given browser window and returns the path
|
11
|
+
# to the saved image.
|
12
|
+
#
|
13
|
+
# @param browser [Watir::Browser] Browser to take a sceeenshot of.
|
14
|
+
# @param name [String] Base name to use for the saved image file.
|
15
|
+
#
|
16
|
+
# @return [String] Path to the new screenshot.
|
17
|
+
#
|
18
|
+
def screenshot(browser, name)
|
19
|
+
dir = screenshot_failures_path
|
20
|
+
FileUtils.mkdir_p dir
|
21
|
+
|
22
|
+
name = name.tr("#{File::SEPARATOR}\000", '-')
|
23
|
+
|
24
|
+
path = File.join(dir, "#{name}.png")
|
25
|
+
browser.screenshot.save(path)
|
26
|
+
|
27
|
+
path
|
28
|
+
end
|
29
|
+
|
30
|
+
# Takes screenshots for failed tests.
|
31
|
+
#
|
32
|
+
# @param info [Hash] Test case information.
|
33
|
+
#
|
34
|
+
def teardown(info = {})
|
35
|
+
screenshots = []
|
36
|
+
|
37
|
+
artifacts = super(info) do |browser|
|
38
|
+
if info[:status] == :failed && screenshot_failures?
|
39
|
+
screenshots << screenshot(browser, info[:name] || 'scenario')
|
40
|
+
end
|
41
|
+
|
42
|
+
yield browser if block_given?
|
43
|
+
end
|
44
|
+
|
45
|
+
artifacts.merge(screenshots.each.with_object({}) { |img, arts| arts[img] = 'image/png' })
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def screenshot_failures?
|
51
|
+
lookup(:screenshot_failures, default: false).to_s == 'true'
|
52
|
+
end
|
53
|
+
|
54
|
+
def screenshot_failures_path
|
55
|
+
lookup(:screenshot_failures_path, default: 'screenshots')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'mediawiki_selenium/support/sauce'
|
2
|
-
|
3
1
|
require 'rest_client'
|
4
2
|
require 'uri'
|
5
3
|
|
@@ -17,6 +15,9 @@ module MediawikiSelenium
|
|
17
15
|
URL = 'http://ondemand.saucelabs.com/wd/hub'
|
18
16
|
|
19
17
|
class << self
|
18
|
+
# @attr [Array] SauceLabs job IDs from the most recent sessions.
|
19
|
+
attr_accessor :last_session_ids
|
20
|
+
|
20
21
|
def extend_object(base)
|
21
22
|
return if base.is_a?(self)
|
22
23
|
|
@@ -42,6 +43,10 @@ module MediawikiSelenium
|
|
42
43
|
# Submits status and Jenkins build info to Sauce Labs.
|
43
44
|
#
|
44
45
|
def teardown(env, status)
|
46
|
+
artifacts = super
|
47
|
+
|
48
|
+
self.class.last_session_ids = []
|
49
|
+
|
45
50
|
each do |browser|
|
46
51
|
sid = browser.driver.session_id
|
47
52
|
url = browser.driver.send(:bridge).http.send(:server_url)
|
@@ -61,8 +66,12 @@ module MediawikiSelenium
|
|
61
66
|
}.to_json
|
62
67
|
)
|
63
68
|
|
64
|
-
|
69
|
+
self.class.last_session_ids << sid
|
70
|
+
|
71
|
+
artifacts["http://saucelabs.com/jobs/#{sid}"] = 'text/url'
|
65
72
|
end
|
73
|
+
|
74
|
+
artifacts
|
66
75
|
end
|
67
76
|
|
68
77
|
protected
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rspec/core'
|
2
|
+
require 'mediawiki_selenium'
|
3
|
+
require 'mediawiki_selenium/rspec/features'
|
4
|
+
|
5
|
+
module MediawikiSelenium
|
6
|
+
module RSpec
|
7
|
+
# Returns a name for the given example metadata, derived from its example
|
8
|
+
# groups and description.
|
9
|
+
#
|
10
|
+
# @param metadata [RSpec::Core::Metadata, Hash] Base or nested metadata.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
#
|
14
|
+
def self.example_name(metadata)
|
15
|
+
name = metadata[:example_group] ? "#{example_name(metadata[:example_group])} " : ''
|
16
|
+
name += metadata[:description_args].first.to_s if metadata[:description_args].any?
|
17
|
+
name
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a status for the given RSpec example result.
|
21
|
+
#
|
22
|
+
# @param result [Object] Result of `example.run`.
|
23
|
+
#
|
24
|
+
# @return [:passed, :failed, :skipped]
|
25
|
+
#
|
26
|
+
def self.example_status(result)
|
27
|
+
case result
|
28
|
+
when Exception
|
29
|
+
:failed
|
30
|
+
when String
|
31
|
+
:skipped
|
32
|
+
else
|
33
|
+
:passed
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
autoload :Environment, 'mediawiki_selenium/rspec/environment'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
RSpec.configure do |config|
|
42
|
+
config.include MediawikiSelenium::RSpec::Environment
|
43
|
+
|
44
|
+
config.around(:each) do |example|
|
45
|
+
name = MediawikiSelenium::RSpec.example_name(example.metadata)
|
46
|
+
|
47
|
+
mw.setup(name: name)
|
48
|
+
result = example.run
|
49
|
+
mw.teardown(name: name, status: MediawikiSelenium::RSpec.example_status(result))
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MediawikiSelenium
|
2
|
+
module RSpec
|
3
|
+
module Environment
|
4
|
+
# Sets up and returns a new default environment.
|
5
|
+
#
|
6
|
+
# @return [Environment]
|
7
|
+
#
|
8
|
+
def mw
|
9
|
+
@_mw ||= MediawikiSelenium::Environment.load_default.extend(
|
10
|
+
MediawikiSelenium::ApiHelper,
|
11
|
+
MediawikiSelenium::PageFactory,
|
12
|
+
MediawikiSelenium::ScreenshotHelper,
|
13
|
+
MediawikiSelenium::UserFactoryHelper
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Allow for indirect calls to the {Environment} object.
|
20
|
+
#
|
21
|
+
# @see mw
|
22
|
+
#
|
23
|
+
def method_missing(name, *args, &block)
|
24
|
+
mw.respond_to?(name) ? mw.__send__(name, *args, &block) : super
|
25
|
+
end
|
26
|
+
|
27
|
+
# Allow for indirect calls to the {Environment} object.
|
28
|
+
#
|
29
|
+
# @see mw
|
30
|
+
#
|
31
|
+
def respond_to_missing?(name, include_all = false)
|
32
|
+
mw.respond_to?(name) || super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# This was "derived" from Capybara's RSpec integration
|
2
|
+
#
|
3
|
+
# https://github.com/jnicklas/capybara/blob/master/lib/capybara/rspec/features.rb
|
4
|
+
#
|
5
|
+
if RSpec::Core::Version::STRING.to_f >= 3.0
|
6
|
+
RSpec.shared_context "MW-Selenium Features", :mw_selenium_feature => true do
|
7
|
+
instance_eval do
|
8
|
+
alias background before
|
9
|
+
alias given let
|
10
|
+
alias given! let!
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.alias_example_group_to :feature, :mw_selenium_feature => true, :type => :feature
|
16
|
+
config.alias_example_to :scenario
|
17
|
+
config.alias_example_to :xscenario, :skip => "Temporarily disabled with xscenario"
|
18
|
+
config.alias_example_to :fscenario, :focus => true
|
19
|
+
end
|
20
|
+
else
|
21
|
+
module MediawikiSelenium::RSpec
|
22
|
+
module Features
|
23
|
+
def self.included(base)
|
24
|
+
base.instance_eval do
|
25
|
+
alias :background :before
|
26
|
+
alias :scenario :it
|
27
|
+
alias :xscenario :xit
|
28
|
+
alias :given :let
|
29
|
+
alias :given! :let!
|
30
|
+
alias :feature :describe
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def self.feature(*args, &block)
|
38
|
+
options = if args.last.is_a?(Hash) then args.pop else {} end
|
39
|
+
options[:mw_selenium_feature] = true
|
40
|
+
options[:type] = :feature
|
41
|
+
options[:caller] ||= caller
|
42
|
+
args.push(options)
|
43
|
+
|
44
|
+
#call describe on RSpec in case user has expose_dsl_globally set to false
|
45
|
+
RSpec.describe(*args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
RSpec.configuration.include MediawikiSelenium::RSpec::Features, :mw_selenium_feature => true
|
49
|
+
end
|
data/mediawiki_selenium.gemspec
CHANGED
@@ -30,9 +30,10 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_runtime_dependency 'cucumber', '~> 1.3', '>= 1.3.20'
|
31
31
|
spec.add_runtime_dependency 'headless', '~> 2.0', '>= 2.1.0'
|
32
32
|
spec.add_runtime_dependency 'json', '~> 1.8', '>= 1.8.1'
|
33
|
-
spec.add_runtime_dependency 'mediawiki_api', '~> 0.
|
33
|
+
spec.add_runtime_dependency 'mediawiki_api', '~> 0.5', '>= 0.5.0'
|
34
34
|
spec.add_runtime_dependency 'page-object', '~> 1.0'
|
35
35
|
spec.add_runtime_dependency 'rest-client', '~> 1.6', '>= 1.6.7'
|
36
|
+
spec.add_runtime_dependency 'rspec-core', '~> 2.14', '>= 2.14.4'
|
36
37
|
spec.add_runtime_dependency 'rspec-expectations', '~> 2.14', '>= 2.14.4'
|
37
38
|
spec.add_runtime_dependency 'syntax', '~> 1.2', '>= 1.2.0'
|
38
39
|
spec.add_runtime_dependency 'thor', '~> 0.19', '>= 0.19.1'
|
@@ -41,6 +42,5 @@ Gem::Specification.new do |spec|
|
|
41
42
|
spec.add_development_dependency 'yard', '~> 0.8', '>= 0.8.7.4'
|
42
43
|
spec.add_development_dependency 'redcarpet', '~> 3.2', '>= 3.2.0'
|
43
44
|
spec.add_development_dependency 'rubocop', '~> 0.29.1'
|
44
|
-
spec.add_development_dependency 'rspec-core', '~> 2.14', '>= 2.14.4'
|
45
45
|
spec.add_development_dependency 'rspec-mocks', '~> 2.14', '>= 2.14.4'
|
46
46
|
end
|
data/spec/environment_spec.rb
CHANGED
@@ -396,6 +396,18 @@ module MediawikiSelenium
|
|
396
396
|
end
|
397
397
|
end
|
398
398
|
|
399
|
+
describe '#setup' do
|
400
|
+
subject { env.setup(name: 'Some test name') }
|
401
|
+
|
402
|
+
it 'configures the browser factory to accept `:job_name` and `:build_number`' do
|
403
|
+
expect(env.browser_factory).to receive(:configure)
|
404
|
+
expect(env.browser_factory).to receive(:configure).with(:job_name)
|
405
|
+
expect(env.browser_factory).to receive(:configure).with(:build_number)
|
406
|
+
|
407
|
+
subject
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
399
411
|
describe '#teardown' do
|
400
412
|
subject { env.teardown(status: status) }
|
401
413
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MediawikiSelenium
|
4
|
+
describe ScreenshotHelper do
|
5
|
+
let(:env) { Environment.new(config).extend(ScreenshotHelper) }
|
6
|
+
|
7
|
+
let(:config) { {} }
|
8
|
+
|
9
|
+
describe '#screenshot' do
|
10
|
+
subject { env.screenshot(browser, name) }
|
11
|
+
|
12
|
+
let(:config) { { screenshot_failures_path: 'screenshots' } }
|
13
|
+
|
14
|
+
let(:browser) { double('Watir::Browser') }
|
15
|
+
let(:name) { 'Some test name' }
|
16
|
+
|
17
|
+
it 'takes a screenshot of the browser' do
|
18
|
+
screenshot = double('Watir::Screenshot')
|
19
|
+
expect(browser).to receive(:screenshot).and_return(screenshot)
|
20
|
+
expect(screenshot).to receive(:save).with('screenshots/Some test name.png')
|
21
|
+
|
22
|
+
subject
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns the path to the image file' do
|
26
|
+
screenshot = double('Watir::Screenshot')
|
27
|
+
allow(browser).to receive(:screenshot).and_return(screenshot)
|
28
|
+
allow(screenshot).to receive(:save)
|
29
|
+
|
30
|
+
expect(subject).to eq('screenshots/Some test name.png')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#teardown' do
|
35
|
+
subject { env.teardown(info) }
|
36
|
+
|
37
|
+
let(:info) { { name: 'Some test name', status: status } }
|
38
|
+
let(:status) { :passed }
|
39
|
+
|
40
|
+
let(:browser) { double(Watir::Browser) }
|
41
|
+
|
42
|
+
before do
|
43
|
+
# mock {Environment#teardown}
|
44
|
+
allow(env.browser_factory).to receive(:each) { |&blk| [browser].each(&blk) }
|
45
|
+
allow(env.browser_factory).to receive(:teardown)
|
46
|
+
allow(browser).to receive(:close)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'invokes the given block with each browser' do
|
50
|
+
allow(env).to receive(:screenshot)
|
51
|
+
|
52
|
+
expect { |blk| env.teardown(info, &blk) }.to yield_with_args(browser)
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'configured to take screenshots of failures' do
|
56
|
+
let(:config) { { screenshot_failures: true } }
|
57
|
+
|
58
|
+
context 'when the test case has failed' do
|
59
|
+
let(:status) { :failed }
|
60
|
+
|
61
|
+
it 'takes a screenshot of each browser' do
|
62
|
+
expect(env).to receive(:screenshot).with(browser, 'Some test name')
|
63
|
+
subject
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'includes each screenshot in the returned artifacts' do
|
67
|
+
allow(env).to receive(:screenshot).and_return('Some test name.png')
|
68
|
+
|
69
|
+
expect(subject).to include('Some test name.png')
|
70
|
+
expect(subject['Some test name.png']).to eq('image/png')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the test case has not failed' do
|
75
|
+
let(:status) { :passed }
|
76
|
+
|
77
|
+
it 'takes no screenshot' do
|
78
|
+
expect(env).not_to receive(:screenshot)
|
79
|
+
subject
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'configured to not take screenshots of failures' do
|
85
|
+
let(:config) { { screenshot_failures: false } }
|
86
|
+
let(:status) { :failed }
|
87
|
+
|
88
|
+
it 'takes no screenshot' do
|
89
|
+
expect(env).not_to receive(:screenshot)
|
90
|
+
subject
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|