Dduvall has submitted this change and it was merged. Change subject: Environment abstraction layer and browser factories ......................................................................
Environment abstraction layer and browser factories Implemented Environment to better encapsulate environment configuration and allow for easy switching between different contexts (different users, wikis, etc.) without relying on mutating the global state of ENV. It also helps one to avoid coupling in step definitions by supplying helpers such as `as_user(:b)` or `on_wiki(:b)` to replace references to specific application state (e.g. 'Given I'm logged in as "Specific User"). Implemented BrowserFactory classes to provide an environment "binding" system by which one can customize the browser options before it's instantiated/opened using settings passed in via environment variables. Refactored loading of MediawikiSelenium classes and implementation to use autoload. Avoid loading of cucumber-related definitions (steps, env, hooks, etc.) so that the library can be loaded in other contexts (e.g. for testing the framework itself). Started writing specs. Change-Id: I1a3aa64b8fdca73aff2778ec12143a8789e4843f --- A .rspec M lib/mediawiki_selenium.rb A lib/mediawiki_selenium/browser_factory.rb A lib/mediawiki_selenium/browser_factory/base.rb A lib/mediawiki_selenium/browser_factory/chrome.rb A lib/mediawiki_selenium/browser_factory/firefox.rb A lib/mediawiki_selenium/browser_factory/phantomjs.rb A lib/mediawiki_selenium/environment.rb A lib/mediawiki_selenium/step_definitions.rb A lib/mediawiki_selenium/support.rb M lib/mediawiki_selenium/support/env.rb A lib/mediawiki_selenium/support/pages.rb M lib/mediawiki_selenium/support/pages/api_page.rb M lib/mediawiki_selenium/support/pages/login_page.rb M lib/mediawiki_selenium/support/pages/random_page.rb M lib/mediawiki_selenium/support/pages/reset_preferences_page.rb M mediawiki_selenium.gemspec A spec/browser_factory/base_spec.rb A spec/environment_spec.rb A spec/spec_helper.rb 20 files changed, 717 insertions(+), 22 deletions(-) Approvals: Dduvall: Looks good to me, approved diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..4e1e0d2 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--color diff --git a/lib/mediawiki_selenium.rb b/lib/mediawiki_selenium.rb index 612105f..2b59a4a 100644 --- a/lib/mediawiki_selenium.rb +++ b/lib/mediawiki_selenium.rb @@ -9,22 +9,9 @@ https://git.wikimedia.org/blob/mediawiki%2Fselenium/HEAD/CREDITS. =end -require "mediawiki_selenium/version" - -require "mediawiki_selenium/support/env" -require "mediawiki_selenium/support/hooks" -require "mediawiki_selenium/support/sauce" - -require "mediawiki_selenium/step_definitions/login_steps" -require "mediawiki_selenium/step_definitions/navigation_steps" -require "mediawiki_selenium/step_definitions/preferences_steps" -require "mediawiki_selenium/step_definitions/resource_loader_steps" -require "mediawiki_selenium/step_definitions/upload_file_steps" - -require "mediawiki_selenium/support/modules/api_helper" -require "mediawiki_selenium/support/modules/url_module" - -require "mediawiki_selenium/support/pages/api_page" -require "mediawiki_selenium/support/pages/login_page" -require "mediawiki_selenium/support/pages/random_page" -require "mediawiki_selenium/support/pages/reset_preferences_page" +module MediawikiSelenium + autoload :VERSION, "mediawiki_selenium/version" + autoload :ApiHelper, "mediawiki_selenium/support/modules/api_helper" + autoload :BrowserFactory, "mediawiki_selenium/browser_factory" + autoload :Environment, "mediawiki_selenium/environment" +end diff --git a/lib/mediawiki_selenium/browser_factory.rb b/lib/mediawiki_selenium/browser_factory.rb new file mode 100644 index 0000000..10660a9 --- /dev/null +++ b/lib/mediawiki_selenium/browser_factory.rb @@ -0,0 +1,21 @@ +module MediawikiSelenium + # Browser factory. + # + module BrowserFactory + autoload :Base, "mediawiki_selenium/browser_factory/base" + autoload :Firefox, "mediawiki_selenium/browser_factory/firefox" + autoload :Chrome, "mediawiki_selenium/browser_factory/chrome" + autoload :Phantomjs, "mediawiki_selenium/browser_factory/phantomjs" + + # Resolves a new factory for the given browser name. + # + # @param name [Symbol] Browser name. + # + # @return [BrowserFactory::Base] + # + def self.new(name) + factory_class = const_get(name.to_s.split("_").map(&:capitalize).join("")) + factory_class.new(name) + end + end +end diff --git a/lib/mediawiki_selenium/browser_factory/base.rb b/lib/mediawiki_selenium/browser_factory/base.rb new file mode 100644 index 0000000..9ba0319 --- /dev/null +++ b/lib/mediawiki_selenium/browser_factory/base.rb @@ -0,0 +1,75 @@ +require "watir-webdriver" + +module MediawikiSelenium + module BrowserFactory + class Base + attr_reader :type + + class << self + def bind(name, &blk) + raise ArgumentError, "no block given" unless block_given? + + default_bindings[name] ||= [] + default_bindings[name] << blk + end + + def bindings + if superclass <= Base + default_bindings.merge(superclass.bindings) { |key, old, new| old + new } + else + default_bindings + end + end + + def default_bindings + @default_bindings ||= {} + end + end + + bind(:browser_timeout) { |value, options| options[:http_client].timeout = value.to_i } + + def initialize(type) + @type = type + @bindings = {} + @browser_cache = {} + end + + def bind(name, &blk) + @bindings[name] ||= [] + @bindings[name] << (blk || proc {}) + end + + def bindings + self.class.bindings.merge(@bindings) { |key, old, new| old + new } + end + + def browser_for(env) + config = env.lookup_all(bindings.keys) + @browser_cache[config] ||= Watir::Browser.new(type, browser_options(config)) + end + + protected + + def capabilities + Selenium::WebDriver::Remote::Capabilities.send(type) + end + + def http_client + Selenium::WebDriver::Remote::Http::Default.new + end + + private + + def browser_options(config) + { http_client: http_client, desired_capabilities: capabilities }.tap do |watir_options| + bindings.each do |(name, bindings_for_option)| + bindings_for_option.each do |binding| + value = config[name] + binding.call(value, watir_options) unless value.nil? || value.to_s.empty? + end + end + end + end + end + end +end diff --git a/lib/mediawiki_selenium/browser_factory/chrome.rb b/lib/mediawiki_selenium/browser_factory/chrome.rb new file mode 100644 index 0000000..b81429a --- /dev/null +++ b/lib/mediawiki_selenium/browser_factory/chrome.rb @@ -0,0 +1,28 @@ +module MediawikiSelenium + module BrowserFactory + class Chrome < Base + def arguments + [].tap do |args| + bind(:browser_user_agent) do |user_agent| + args << "--user-agent=#{user_agent}" + end + end + end + + def capabilities + super.merge( + "chrome.profile" => profile.as_json["zip"], + "chromeOptions" => { "args" => arguments }, + ) + end + + def profile + Selenium::WebDriver::Chrome::Profile.new.tap do |profile| + bind(:browser_language) do |language| + profile["intl.accept_languages"] = language + end + end + end + end + end +end diff --git a/lib/mediawiki_selenium/browser_factory/firefox.rb b/lib/mediawiki_selenium/browser_factory/firefox.rb new file mode 100644 index 0000000..56bc029 --- /dev/null +++ b/lib/mediawiki_selenium/browser_factory/firefox.rb @@ -0,0 +1,34 @@ +module MediawikiSelenium + module BrowserFactory + class Firefox < Base + bind(:browser_timeout) do |timeout, opts| + timeout = timeout.to_i + opts[:desired_capabilities][:firefox_profile]["dom.max_script_run_time"] = timeout + opts[:desired_capabilities][:firefox_profile]["dom.max_chrome_script_run_time"] = timeout + end + + def capabilities + super.tap do |capabilities| + capabilities[:firefox_profile] = Selenium::WebDriver::Firefox::Profile.new + end + end + + def profile + Selenium::WebDriver::Firefox::Profile.new.tap do |profile| + bind(:browser_timeout) do |timeout| + profile["dom.max_script_run_time"] = timeout + profile["dom.max_chrome_script_run_time"] = timeout + end + + bind(:browser_language) do |language| + profile["intl.accept_languages"] = language + end + + bind(:browser_user_agent) do |user_agent| + profile["general.useragent.override"] = user_agent + end + end + end + end + end +end diff --git a/lib/mediawiki_selenium/browser_factory/phantomjs.rb b/lib/mediawiki_selenium/browser_factory/phantomjs.rb new file mode 100644 index 0000000..5a8309b --- /dev/null +++ b/lib/mediawiki_selenium/browser_factory/phantomjs.rb @@ -0,0 +1,17 @@ +module MediawikiSelenium + module BrowserFactory + class Phantomjs < Base + def capabilities + super.tap do |capabilities| + bind(:browser_language) do |language| + capabilities["phantomjs.page.customHeaders.Accept-Language"] = language + end + + bind(:browser_user_agent) do |user_agent| + capabilities["phantomjs.page.settings.userAgent"] = user_agent + end + end + end + end + end +end diff --git a/lib/mediawiki_selenium/environment.rb b/lib/mediawiki_selenium/environment.rb new file mode 100644 index 0000000..58a5da6 --- /dev/null +++ b/lib/mediawiki_selenium/environment.rb @@ -0,0 +1,183 @@ +module MediawikiSelenium + # Provides an abstraction layer between the environmental configuration and + # step definitions. + # + class Environment + include Comparable + + class ConfigurationError < StandardError + attr_reader :name + + def initialize(name) + @name = name + end + + def to_s + "missing configuration for #{name}" + end + end + + CORE_BROWSER_OPTIONS = [ + :mediawiki_url, + :mediawiki_user, + ] + + REQUIRED_CONFIG = [ + :browser, + :mediawiki_api_url, + :mediawiki_password, + :mediawiki_url, + :mediawiki_user, + ] + + attr_reader :config + + def initialize(config) + @config = normalize_config(config) + @factory_cache = {} + end + + def initialize_clone(other) + @config = other.config.clone + end + + # Two environments are considered equal if they have identical + # configuration. + # + def ==(other) + @config == other.config + end + + # Executes the given block within the context of an environment that's + # using the given alternative user and its password. + # + # @param id [Symbol] Alternative user ID. + # + # @yield [env] + # @yieldparam env [Environment] Environment + # + # @return [Environment] + # + def as_user(id, &blk) + with_alternative([:mediawiki_user, :mediawiki_password], id, &blk) + end + + # Binds new possible configuration for the given browser. + # + # @param browser_name [Symbol] Browser name. + # @param option_name [Symbol] Option name. + # + # @yield [] + # + def bind(browser_name, option_name, &blk) + browser_factory(browser_name).bind(option_name, &blk) + end + + # Browser with which to drive tests. + # + # @return [Watir::Browser] + # + def browser + browser_factory.browser_for(self) + end + + def browser_factory(type = browser_name) + type = type.to_s.downcase.to_sym + + @factory_cache[type] ||= BrowserFactory.new(type).tap do |factory| + CORE_BROWSER_OPTIONS.each { |name| factory.bind(name) } + end + end + + # Name of the browser we're using. + # + # @return [Symbol] + # + def browser_name + lookup(:browser).downcase.to_sym + end + + def lookup(key, id = nil) + key = "#{key}_#{id}" unless id.nil? + key = key.to_sym + value = @config[key] + + if value.nil? || value.to_s.empty? + if id.nil? + if REQUIRED_CONFIG.include?(key) + raise ConfigurationError, key + else + nil + end + else + lookup(key) + end + else + value + end + end + + def lookup_all(keys, id = nil) + keys.each.with_object({}) do |key, hash| + value = lookup(key, id) + hash[key] = value unless value.nil? + end + end + + # Executes the given block within the context of an environment that's + # using the given alternative wiki URL and its corresponding API endpoint. + # + # @param id [Symbol] Alternative wiki ID. + # + # @yield [env] + # @yieldparam env [Environment] Environment + # + # @return [Environment] + # + def on_wiki(id, &blk) + with_alternative([:mediawiki_url, :mediawiki_api_url], id, &blk) + end + + # Yields a new environment using the alternative versions of the given + # configuration options. The alternative values are resolved by looking up + # options that correspond to the given ones but have the given ID + # appended. + # + # @example Overwrite :option with the value from :option_b + # # given an environment with { option: "x", option_b: "y", ... } + # env.with_alternative(:option, :b) do |env| + # env # => { option: "y", ... } + # end + # + # @example Overwrite both :option and :other with :option_b and :other_b + # # given { option: "x", option_b: "y", other: "w", other_b: "z" } + # env.with_alternative([:option, :other], :b) do |env| + # env # => { option: "y", other: "z", ... } + # end + # + # @param names [Array|Symbol] Configuration option or options. + # @param id [Symbol] Alternative user ID. + # + # @yield [env] + # @yieldparam env [Environment] The modified environment. + # + # @return [Environment] The modified environment. + # + def with_alternative(names, id, &blk) + with(lookup_all(Array(names), id), &blk) + end + + private + + def normalize_config(hash) + hash.each.with_object({}) { |(k, v), acc| acc[k.to_s.downcase.to_sym] = v } + end + + def with(overrides = {}) + clone.tap do |env| + env.config.merge!(normalize_config(overrides)) + yield env + end + end + end +end diff --git a/lib/mediawiki_selenium/step_definitions.rb b/lib/mediawiki_selenium/step_definitions.rb new file mode 100644 index 0000000..b8608f6 --- /dev/null +++ b/lib/mediawiki_selenium/step_definitions.rb @@ -0,0 +1,5 @@ +require "mediawiki_selenium/step_definitions/login_steps" +require "mediawiki_selenium/step_definitions/navigation_steps" +require "mediawiki_selenium/step_definitions/preferences_steps" +require "mediawiki_selenium/step_definitions/resource_loader_steps" +require "mediawiki_selenium/step_definitions/upload_file_steps" diff --git a/lib/mediawiki_selenium/support.rb b/lib/mediawiki_selenium/support.rb new file mode 100644 index 0000000..f5a6cb6 --- /dev/null +++ b/lib/mediawiki_selenium/support.rb @@ -0,0 +1,3 @@ +require "mediawiki_selenium/support/env" +require "mediawiki_selenium/support/hooks" +require "mediawiki_selenium/support/sauce" diff --git a/lib/mediawiki_selenium/support/env.rb b/lib/mediawiki_selenium/support/env.rb index 35af866..140a908 100644 --- a/lib/mediawiki_selenium/support/env.rb +++ b/lib/mediawiki_selenium/support/env.rb @@ -11,7 +11,6 @@ # before all require "bundler/setup" -require "page-object" require "page-object/page_factory" require "rest_client" require "watir-webdriver" @@ -25,6 +24,8 @@ World(MediawikiSelenium::SauceHelper) World(MediawikiSelenium::StrictPending) +World { MediawikiSelenium::Environment.new(ENV) } + def browser(test_name, configuration = nil) if environment == :saucelabs sauce_browser(test_name, configuration) diff --git a/lib/mediawiki_selenium/support/pages.rb b/lib/mediawiki_selenium/support/pages.rb new file mode 100644 index 0000000..f316bcf --- /dev/null +++ b/lib/mediawiki_selenium/support/pages.rb @@ -0,0 +1,4 @@ +require "mediawiki_selenium/support/pages/api_page" +require "mediawiki_selenium/support/pages/login_page" +require "mediawiki_selenium/support/pages/random_page" +require "mediawiki_selenium/support/pages/reset_preferences_page" diff --git a/lib/mediawiki_selenium/support/pages/api_page.rb b/lib/mediawiki_selenium/support/pages/api_page.rb index ba2ef39..53da9ee 100644 --- a/lib/mediawiki_selenium/support/pages/api_page.rb +++ b/lib/mediawiki_selenium/support/pages/api_page.rb @@ -1,3 +1,4 @@ +require "page-object" require "mediawiki_api" class APIPage diff --git a/lib/mediawiki_selenium/support/pages/login_page.rb b/lib/mediawiki_selenium/support/pages/login_page.rb index 89857bf..44fbf78 100644 --- a/lib/mediawiki_selenium/support/pages/login_page.rb +++ b/lib/mediawiki_selenium/support/pages/login_page.rb @@ -8,6 +8,8 @@ mediawiki_selenium top-level directory and at https://git.wikimedia.org/blob/mediawiki%2Fselenium/HEAD/CREDITS. =end +require "page-object" +require "mediawiki_selenium/support/modules/url_module" class LoginPage include PageObject diff --git a/lib/mediawiki_selenium/support/pages/random_page.rb b/lib/mediawiki_selenium/support/pages/random_page.rb index c181ea3..40e33cd 100644 --- a/lib/mediawiki_selenium/support/pages/random_page.rb +++ b/lib/mediawiki_selenium/support/pages/random_page.rb @@ -8,6 +8,8 @@ mediawiki_selenium top-level directory and at https://git.wikimedia.org/blob/mediawiki%2Fselenium/HEAD/CREDITS. =end +require "page-object" +require "mediawiki_selenium/support/modules/url_module" class RandomPage include PageObject diff --git a/lib/mediawiki_selenium/support/pages/reset_preferences_page.rb b/lib/mediawiki_selenium/support/pages/reset_preferences_page.rb index 255c772..76f2281 100644 --- a/lib/mediawiki_selenium/support/pages/reset_preferences_page.rb +++ b/lib/mediawiki_selenium/support/pages/reset_preferences_page.rb @@ -8,6 +8,8 @@ mediawiki_selenium top-level directory and at https://git.wikimedia.org/blob/mediawiki%2Fselenium/HEAD/CREDITS. =end +require "page-object" +require "mediawiki_selenium/support/modules/url_module" class ResetPreferencesPage include PageObject diff --git a/mediawiki_selenium.gemspec b/mediawiki_selenium.gemspec index 39c1c44..549af4c 100644 --- a/mediawiki_selenium.gemspec +++ b/mediawiki_selenium.gemspec @@ -26,7 +26,10 @@ spec.add_runtime_dependency "rspec-expectations", "~> 2.14", ">= 2.14.4" spec.add_runtime_dependency "syntax", "~> 1.2", ">= 1.2.0" - spec.add_development_dependency "redcarpet" + spec.add_development_dependency "bundler", "~> 1.6", ">= 1.6.3" + spec.add_development_dependency "yard", "~> 0.8", ">= 0.8.7.4" + spec.add_development_dependency "redcarpet", "~> 3.2", ">= 3.2.0" spec.add_development_dependency "rubocop", "~> 0.26.1" - spec.add_development_dependency "yard" + spec.add_development_dependency "rspec-core", "~> 2.14", ">= 2.14.4" + spec.add_development_dependency "rspec-mocks", "~> 2.14", ">= 2.14.4" end diff --git a/spec/browser_factory/base_spec.rb b/spec/browser_factory/base_spec.rb new file mode 100644 index 0000000..e53cda5 --- /dev/null +++ b/spec/browser_factory/base_spec.rb @@ -0,0 +1,166 @@ +require "spec_helper" + +module MediawikiSelenium::BrowserFactory + describe Base do + let(:factory_class) { Class.new(Base) } + let(:factory) { factory_class.new(browser_type) } + let(:browser_type) { :lynx } + + describe ".bind" do + subject { factory_class.bind(option_name, &block) } + + let(:option_name) { :foo } + let(:block) { proc { } } + + it "adds a new default binding for the given option" do + subject + expect(factory_class.default_bindings).to include(option_name) + expect(factory_class.default_bindings[option_name]).to include(block) + end + + context "given no block" do + subject { factory_class.bind(option_name) } + + it "raises an ArgumentError" do + expect { subject }.to raise_error(ArgumentError) + end + end + end + + describe ".bindings" do + subject { factory_class.bindings } + + before do + factory_class.bind(:foo) {} + end + + it "includes the base class's default bindings" do + expect(subject).to include(Base.default_bindings) + end + + it "includes its own bindings" do + expect(subject).to include(factory_class.default_bindings) + end + end + + describe ".default_bindings" do + subject { factory_class.default_bindings } + + it { is_expected.to be_a(Hash) } + + context "before any bindings are defined" do + it { is_expected.to be_empty } + end + + context "when bindings are defined" do + before do + factory_class.bind(:foo) {} + factory_class.bind(:bar) {} + factory_class.bind(:bar) {} + end + + it "includes all defined bindings" do + expect(subject).to include(:foo, :bar) + end + + it "includes all bindings for the same option name" do + expect(subject[:bar].length).to be(2) + end + end + end + + describe "#bind" do + subject { factory.bind(option_name, &block) } + before { subject } + + let(:option_name) { :foo } + let(:block) { proc { } } + + it "adds a new binding for the given option" do + expect(factory.bindings).to include(option_name) + expect(factory.bindings[option_name]).to include(block) + end + + context "given no block" do + subject { factory.bind(option_name) } + + it "will default to an empty block" do + expect(factory.bindings[option_name]).not_to include(nil) + end + end + end + + describe "#bindings" do + subject { factory.bindings } + + before do + factory_class.bind(:foo) {} + factory.bind(:bar) + end + + it "includes the class-level bindings" do + expect(subject).to include(factory_class.bindings) + end + + it "includes its own bindings" do + expect(subject).to include(:bar) + end + end + + describe "#browser_for" do + subject { factory.browser_for(env) } + + let(:env) { MediawikiSelenium::Environment.new(foo: "x", bar: "y") } + + let(:watir_browser) { double(Watir::Browser) } + let(:capabilities) { double(Selenium::WebDriver::Remote::Capabilities) } + + before do + factory.bind(:foo) + + expect(Selenium::WebDriver::Remote::Capabilities).to receive(browser_type). + at_least(:once).and_return(capabilities) + end + + it "creates a new Watir::Browser" do + expect(Watir::Browser).to receive(:new).once.and_return(watir_browser) + expect(subject).to be(watir_browser) + end + + context "called more than once" do + let(:env1) { env } + + context "with differing env configuration" do + context "that is bound" do + let(:env2) { MediawikiSelenium::Environment.new(foo: "z") } + + it "returns two distinct browsers" do + expect(Watir::Browser).to receive(:new).twice + + factory.browser_for(env1) + factory.browser_for(env2) + end + end + + context "that is not bound" do + let(:env2) { MediawikiSelenium::Environment.new(foo: "x", bar: "z") } + + it "returns a cached browser" do + expect(Watir::Browser).to receive(:new).once.and_return(watir_browser) + expect(factory.browser_for(env1)).to be(factory.browser_for(env2)) + end + end + end + + context "with the same env configuration" do + let(:env2) { MediawikiSelenium::Environment.new(foo: "x") } + + it "returns a cached browser" do + expect(Watir::Browser).to receive(:new).once.and_return(watir_browser) + expect(factory.browser_for(env1)).to be(factory.browser_for(env2)) + end + end + end + end + end +end diff --git a/spec/environment_spec.rb b/spec/environment_spec.rb new file mode 100644 index 0000000..bd9d341 --- /dev/null +++ b/spec/environment_spec.rb @@ -0,0 +1,156 @@ +require "spec_helper" + +module MediawikiSelenium + describe Environment do + subject { env } + + let(:env) { Environment.new(config) } + let(:config) { minimum_config } + + let(:minimum_config) do + { + browser: browser, + mediawiki_api_url: mediawiki_api_url, + mediawiki_url: mediawiki_url, + mediawiki_user: mediawiki_user, + mediawiki_password: mediawiki_password, + } + end + + let(:browser) { "firefox" } + let(:mediawiki_api_url) { "http://an.example/wiki/api.php" } + let(:mediawiki_url) { "http://an.example/wiki/" } + let(:mediawiki_user) { "mw user" } + let(:mediawiki_password) { "mw password" } + + describe "#==" do + subject { env == other } + + context "given an environment with the same configuration" do + let(:other) { Environment.new(env.config) } + + it "considers them equal" do + expect(subject).to be(true) + end + end + + context "given an environment with different configuration" do + let(:other) { Environment.new(env.config.merge(some: "extra")) } + + it "considers them not equal" do + expect(subject).to be(false) + end + end + end + + describe "#as_user" do + let(:config) do + { + mediawiki_user: "user", + mediawiki_password: "pass", + mediawiki_user_b: "user b", + mediawiki_password_b: "pass b", + } + end + + it "yields a new environment for the alternative user and its password" do + expected_env = Environment.new(config.merge( + mediawiki_user: "user b", + mediawiki_password: "pass b", + )) + expect { |block| env.as_user(:b, &block) }.to yield_with_args(expected_env) + end + end + + describe "#browser_factory" do + subject { env.browser_factory } + + it "is a factory for the configured browser" do + expect(subject).to be_a(BrowserFactory::Firefox) + end + + it "binds the core browser options" do + expect(subject.bindings).to include(*Environment::CORE_BROWSER_OPTIONS) + end + + context "given a type" do + subject { env.browser_factory(:chrome) } + + it "is a factory for that type" do + expect(subject).to be_a(BrowserFactory::Chrome) + end + end + end + + describe "#browser_name" do + subject { env.browser_name } + + let(:browser) { "Firefox" } + + it "is always a lowercase symbol" do + expect(subject).to be(:firefox) + end + + context "missing browser configuration" do + let(:browser) { nil } + + it "raises a ConfigurationError" do + expect { subject }.to raise_error(Environment::ConfigurationError) + end + end + end + + describe "#on_wiki" do + let(:config) do + { + mediawiki_url: "http://an.example/wiki", + mediawiki_url_b: "http://alt.example/wiki", + mediawiki_api_url: "http://an.example/api", + mediawiki_api_url_b: "http://alt.example/api", + } + end + + it "yields a new environment for the alternative wiki and API urls" do + expected_env = Environment.new(config.merge( + mediawiki_url: "http://alt.example/wiki", + mediawiki_api_url: "http://alt.example/api", + )) + expect { |block| env.on_wiki(:b, &block) }.to yield_with_args(expected_env) + end + end + + describe "#with_alternative" do + let(:config) do + { + mediawiki_url: "http://an.example/wiki", + mediawiki_url_b: "http://alt.example/wiki", + mediawiki_api_url: "http://an.example/api", + mediawiki_api_url_b: "http://alt.example/api", + } + end + + context "given one option name and an ID" do + let(:names) { :mediawiki_url } + + it "yields an environment that substitutes it using the alternative" do + expected_env = Environment.new(config.merge( + mediawiki_url: "http://alt.example/wiki", + )) + expect { |block| env.with_alternative(names, :b, &block) }.to yield_with_args(expected_env) + end + end + + context "given multiple option names and an ID" do + let(:names) { [:mediawiki_url, :mediawiki_api_url] } + + it "yields an environment that substitutes both using the alternatives" do + expected_env = Environment.new(config.merge( + mediawiki_url: "http://alt.example/wiki", + mediawiki_api_url: "http://alt.example/api", + )) + expect { |block| env.with_alternative(names, :b, &block) }.to yield_with_args(expected_env) + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..f92911d --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,4 @@ +require "bundler/setup" +require "mediawiki_selenium" + +Bundler.require(:development) -- To view, visit https://gerrit.wikimedia.org/r/169850 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1a3aa64b8fdca73aff2778ec12143a8789e4843f Gerrit-PatchSet: 2 Gerrit-Project: mediawiki/selenium Gerrit-Branch: env-abstraction-layer Gerrit-Owner: Dduvall <[email protected]> Gerrit-Reviewer: Dduvall <[email protected]> Gerrit-Reviewer: Hashar <[email protected]> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
