Class: MediawikiSelenium::Environment

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/mediawiki_selenium/environment.rb

Overview

Provides an interface that unifies environmental configuration, page objects, and browser setup. Additionally, it provides a DSL for switching between user/wiki/browser contexts in ways that help to decouple test implementation from the target wikis.

Default configuration for various resources (wiki URLs, users, etc.) is typically loaded from an environments.yml YAML file in the current working directory. It should contain defaults for each environment in which the tests are expected to run, indexed by environment name.

beta:
  mediawiki_url: http://en.wikipedia.beta.wmflabs.org/wiki/
  mediawiki_user: Selenium_user
test2:
  mediawiki_url: http://test2.wikipedia.org/wiki/
  mediawiki_user: Selenium_user

Which default set to use is determined by the value of the MEDIAWIKI_ENVIRONMENT environment variable, or an entry called "default" if none is set. (See Environment.load and Environment.load_default.) The easiest way to designate such a default set is to use a YAML anchor like so.

beta: &default
  mediawiki_url: http://en.wikipedia.beta.wmflabs.org/wiki/
  mediawiki_user: Selenium_user
test2:
  mediawiki_url: http://test2.wikipedia.org/wiki/
  mediawiki_user: Selenium_user
default: *default

Any additional configuration specified via environment variables overrides what is specified in the YAML file. For example, the following would use the default configuration as specified under beta in the YAML file but define mediawiki_user as Other_user instead of Selenium_user.

export MEDIAWIKI_ENVIRONMENT=beta MEDIAWIKI_USER=Other_user
bundle exec cucumber ...

There are various methods that allow you to perform actions in the context of some alternative resource, for example as a different user using #as_user, or on different wiki using #on_wiki. Instead of referencing the exact user names or URLs for these resources, you reference them by an ID which corresponds to configuration made in environments.yml.

# environments.yml:
beta:
  # ...
  mediawiki_user_b: Selenium_user2

# step definition:
Given(/^user B has linked to a page I created$/) do
  as_user(:b) { api.create_page(...) }
end

This level of abstraction is intended to reduce coupling between tests and test environments, and should promote step definitions that are more readable and congruent with the natural-language steps they implement.

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*configs) ⇒ Environment

Returns a new instance of Environment.



103
104
105
106
# File 'lib/mediawiki_selenium/environment.rb', line 103

def initialize(*configs)
  @_config = configs.map { |config| normalize_config(config) }.reduce(:merge)
  @_factory_cache = {}
end

Class Attribute Details

.default_configurationObject

Returns the value of attribute default_configuration.



66
67
68
# File 'lib/mediawiki_selenium/environment.rb', line 66

def default_configuration
  @default_configuration
end

Class Method Details

.load(name, extra = {}) ⇒ Object

Instantiates a new environment using the given set of default configuration from environments.yml in the current working directory, and the additional hash of environment variables.

Parameters:

  • name (String)

    Name of the environment.

  • extra (Hash) (defaults to: {})

    Additional configuration to use.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/mediawiki_selenium/environment.rb', line 75

def load(name, extra = {})
  name = name.to_s
  configs = []

  unless name.empty?
    envs = YAML.load_file(default_configuration)
    raise ConfigurationError, "unknown environment `#{name}`" unless envs.include?(name)
    configs << envs[name]
  end

  configs << extra

  new(*configs)
end

.load_defaultObject

Instantiates a new environment from the values of ENV and the default configuration corresponding to ENV["MEDIAWIKI_ENVIRONMENT"], if one is defined.

See Also:



96
97
98
# File 'lib/mediawiki_selenium/environment.rb', line 96

def load_default
  load(ENV['MEDIAWIKI_ENVIRONMENT'] || 'default', ENV)
end

Instance Method Details

#==(other) ⇒ Boolean

Whether the given environment is equal to this one. Two environments are considered equal if they have identical configuration.

Parameters:

Returns:

  • (Boolean)


115
116
117
# File 'lib/mediawiki_selenium/environment.rb', line 115

def ==(other)
  config == other.config
end

#[](key) ⇒ String

Returns the configured value for the given env variable name.

Parameters:

  • key (Symbol)

    Environment variable name.

Returns:

  • (String)

See Also:



127
128
129
# File 'lib/mediawiki_selenium/environment.rb', line 127

def [](key)
  lookup(key)
end

#as_user(id) {|user, password| ... } ⇒ Object

Executes the given block within the context of an environment that's using the given alternative user and its password.

Examples:

Given(/^user B has linked to a page I created$/) do
  as_user(:b) { api.create_page(...) }
end

Parameters:

  • id (Symbol)

    Alternative user ID.

Yields:

Yield Parameters:

  • user (String)

    Alternative MediaWiki user.

  • password (String)

    Alternative MediaWiki password.



145
146
147
148
149
150
# File 'lib/mediawiki_selenium/environment.rb', line 145

def as_user(id, &blk)
  user = lookup(:mediawiki_user, id: id)
  password = lookup(:mediawiki_password, id: id, default: -> { lookup(:mediawiki_password) })

  with(mediawiki_user: user, mediawiki_password: password, &blk)
end

#browserWatir::Browser

Browser with which to drive tests.

Returns:

  • (Watir::Browser)


156
157
158
# File 'lib/mediawiki_selenium/environment.rb', line 156

def browser
  browser_factory.browser_for(browser_config)
end

#browser_factory(browser = browser_name) ⇒ BrowserFactory::Base

Factory used to instantiate and open new browsers.

Parameters:

  • browser (Symbol) (defaults to: browser_name)

    Browser name.

Returns:



166
167
168
169
170
171
172
173
# File 'lib/mediawiki_selenium/environment.rb', line 166

def browser_factory(browser = browser_name)
  browser = browser.to_s.downcase.to_sym

  @_factory_cache[[remote?, browser]] ||= BrowserFactory.new(browser).tap do |factory|
    factory.configure(:_browser_session)
    factory.extend(RemoteBrowserFactory) if remote?
  end
end

#browser_nameSymbol

Name of the browser we're using.

Returns:

  • (Symbol)


179
180
181
# File 'lib/mediawiki_selenium/environment.rb', line 179

def browser_name
  lookup(:browser, default: 'firefox').downcase.to_sym
end

#envself

A reference to this environment. Can be used in conjunction with #[] for syntactic sugar in looking up environment configuration where self would otherwise seem ambiguous.

Examples:

Then(/^I see my username on the page$/) do
  expect(on(SomePage).html).to include(env[:mediawiki_user])
end

Returns:

  • (self)


194
195
196
# File 'lib/mediawiki_selenium/environment.rb', line 194

def env
  self
end

#in_browser(id, overrides = {}) {|*args| ... } ⇒ Object

Executes the given block within the context of an environment that uses a unique browser session and possibly different configuration. Note that any given configuration overrides are scoped with a :browser_ prefix.

Examples:

Implement a "logged out" step following some authenticated one

When(/^I do something while logged in$/) do
  in_browser(:a) do
    # perform action in logged in session
  end
end

When(/^I do something else after logging out$/) do
  in_browser(:b) do
    # perform action in logged out session without actually logging
    # out since that would affect all auth sessions for the user
  end
end

Perform a subsequent step requiring a different browser language

When(/^I visit the same page with my browser in Spanish$/) do |scenario, block|
  in_browser(:a, language: "es") do
    # test that it now serves up Spanish text
  end
end

Parameters:

  • id (Symbol)

    Browser session ID.

  • overrides (Hash) (defaults to: {})

    Browser configuration overrides.

Yields:

  • (*args)

    Overridden browser configuration.



228
229
230
231
232
233
234
# File 'lib/mediawiki_selenium/environment.rb', line 228

def in_browser(id, overrides = {}, &blk)
  overrides = overrides.each.with_object({}) do |(name, value), hash|
    hash["browser_#{name}".to_sym] = value
  end

  with(overrides.merge(_browser_session: id), &blk)
end

#keep_browser_open?Boolean

Whether browsers should be left open after each scenario completes.

Returns:

  • (Boolean)


238
239
240
# File 'lib/mediawiki_selenium/environment.rb', line 238

def keep_browser_open?
  lookup(:keep_browser_open, default: 'false') == 'true'
end

#lookup(key, options = {}) ⇒ String

Returns the configured value for the given env variable name.

Examples:

Value of :browser_language and fail if it wasn't provided

env.lookup(:browser_language)

Value of :browser_language alternative :b

env.lookup(:browser_language, id: :b)

Value of :browser_language or try :browser_lang

env.lookup(:browser_language, default: -> { env.lookup(:browser_lang) })

Parameters:

  • key (Symbol)

    Environment variable name.

  • options (Hash) (defaults to: {})

    Options.

Options Hash (options):

  • :id (Symbol)

    Alternative ID.

  • :default (Object, Proc)

    Default value or promise of a value.

Returns:

  • (String)


260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/mediawiki_selenium/environment.rb', line 260

def lookup(key, options = {})
  key = "#{key}_#{options[:id]}" if options.fetch(:id, nil)
  key = normalize_key(key)

  value = config[key]

  if value.nil? || value.to_s.empty?
    if options.include?(:default)
      options[:default].is_a?(Proc) ? options[:default].call : options[:default]
    else
      raise ConfigurationError, "missing configuration for `#{key}`"
    end
  else
    value
  end
end

#lookup_all(keys, options = {}) ⇒ Array<String>

Returns the configured values for the given env variable names.

Parameters:

  • keys (Array<Symbol>)

    Environment variable names.

  • options (Hash) (defaults to: {})

    Options.

Options Hash (options):

  • :id (Symbol)

    Alternative ID.

  • :default (Object)

    Default if no configuration is found.

Returns:

  • (Array<String>)

See Also:



288
289
290
291
292
# File 'lib/mediawiki_selenium/environment.rb', line 288

def lookup_all(keys, options = {})
  keys.each.with_object({}) do |key, hash|
    hash[key] = lookup(key, options)
  end
end

#on_wiki(id) {|wiki_url| ... } ⇒ Object

Executes the given block within the context of an environment that's using the given alternative wiki URL and its corresponding API endpoint.

If no API URL is explicitly defined for the given alternative, one is constructed relative to the wiki URL.

Examples:

Visit a random page on wiki B

on_wiki(:b) { visit(RandomPage) }

Parameters:

  • id (Symbol)

    Alternative wiki ID.

Yields:

Yield Parameters:

  • wiki_url (String)

    Alternative wiki URL.



308
309
310
# File 'lib/mediawiki_selenium/environment.rb', line 308

def on_wiki(id, &blk)
  with_alternative(:mediawiki_url, id, &blk)
end

#password(id = nil) ⇒ String

Returns the current value for :mediawiki_password or the value for the given alternative.

Parameters:

  • id (Symbol) (defaults to: nil)

    Alternative user ID.

Returns:

  • (String)


319
320
321
# File 'lib/mediawiki_selenium/environment.rb', line 319

def password(id = nil)
  lookup(:mediawiki_password, id: id)
end

#remote?Boolean

Whether this environment has been configured to use remote browser sessions.

Returns:

  • (Boolean)


328
329
330
# File 'lib/mediawiki_selenium/environment.rb', line 328

def remote?
  RemoteBrowserFactory::REQUIRED_CONFIG.all? { |name| lookup(name, default: false) }
end

#teardown(status = :passed) {|browser| ... } ⇒ Object

Executes teardown tasks including instructing all browser factories to close any open browsers and perform their own teardown tasks.

Examples:

Teardown environment resources after each scenario completes

After do
  teardown(scenario.status)
end

Parameters:

  • status (Symbol) (defaults to: :passed)

    Status of the executed scenario.

Yields:

Yield Parameters:

  • browser (Watir::Browser)

    Browser object, before it's closed.



345
346
347
348
349
350
351
352
353
354
# File 'lib/mediawiki_selenium/environment.rb', line 345

def teardown(status = :passed)
  @_factory_cache.each do |(_, browser_name), factory|
    factory.each do |browser|
      yield browser if block_given?
      browser.close unless keep_browser_open? && browser_name != :phantomjs
    end

    factory.teardown(self, status)
  end
end

#test_name(scenario) ⇒ String

Returns a name from the given scenario.

Parameters:

  • scenario (Cucumber::Ast::Scenario)

Returns:

  • (String)


362
363
364
365
366
367
368
369
370
371
# File 'lib/mediawiki_selenium/environment.rb', line 362

def test_name(scenario)
  if scenario.respond_to? :feature
    "#{scenario.feature.title}: #{scenario.title}"
  elsif scenario.respond_to? :scenario_outline
    outline = scenario.scenario_outline
    "#{outline.feature.title}: #{outline.title}: #{scenario.name}"
  else
    scenario.name
  end
end

#user(id = nil) ⇒ String

Returns the current value for :mediawiki_user or the value for the given alternative.

Parameters:

  • id (Symbol) (defaults to: nil)

    Alternative user ID.

Returns:

  • (String)


380
381
382
# File 'lib/mediawiki_selenium/environment.rb', line 380

def user(id = nil)
  lookup(:mediawiki_user, id: id)
end

#user_label(id = nil) ⇒ String

Returns the current user, or the one for the given alternative, with all "_" replaced with " ".

Parameters:

  • id (Symbol) (defaults to: nil)

    Alternative user ID.

Returns:

  • (String)


391
392
393
# File 'lib/mediawiki_selenium/environment.rb', line 391

def user_label(id = nil)
  user(id).gsub('_', ' ')
end

#visit_wiki(id) {|url| ... } ⇒ Object

Navigates the current browser to the given wiki.

Parameters:

  • id (Symbol)

    Alternative wiki ID.

Yields:

  • (url)

Yield Parameters:

  • url (String)

    Wiki URL.



402
403
404
405
406
407
# File 'lib/mediawiki_selenium/environment.rb', line 402

def visit_wiki(id)
  on_wiki(id) do |url|
    browser.goto url
    yield url if block_given?
  end
end

#wiki_url(path = nil) ⇒ Object

Qualifies any given relative path using the configured :mediawiki_url. Absolute URLs are left untouched.

Examples:

env = Environment.new(mediawiki_url: "http://an.example/wiki/")

env.wiki_url # => "http://an.example/wiki/"
env.wiki_url("page") # => "http://an.example/wiki/page"
env.wiki_url("/page") # => "http://an.example/page"
env.wiki_url("http://other.example") # => "http://other.example"


420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/mediawiki_selenium/environment.rb', line 420

def wiki_url(path = nil)
  url = lookup(:mediawiki_url)

  if path
    # Prefixing relative paths with an explicit "./" guarantees proper
    # parsing of paths like "Special:Page" that would otherwise be
    # confused for URI schemes.
    if path.include?(':')
      path_uri = URI.parse(path)
      path = "./#{path}" if path_uri.class == URI::Generic && !path.start_with?('/')
    end

    url = URI.parse(url).merge(path).to_s
  end

  url
end

#with_alternative(names, id) {|*args| ... } ⇒ Object

Executes the given block within the context of a new environment configured using the alternative versions of the given options. The alternative configuration values are resolved using the given ID and passed to the block as arguments.

Examples:

Overwrite :foo with the :b alternative

# given an environment with config { foo: "x", foo_b: "y", ... }
with_alternative(:foo, :b) do |foo|
  self # => #<Environment @config = { foo: "y", ... }>
  foo # => "y"
end

Overwrite both :foo and :bar with the :b alternatives

# given an environment with config { foo: "x", foo_b: "y", bar: "w", bar_b: "z" }
with_alternative([:foo, :bar], :b) do |foo, bar|
  self # => #<Environment @config = { foo: "y", bar: "z", ... }>
  foo # => "y"
  bar # => "z"
end

Parameters:

  • names (Symbol|Array<Symbol>)

    Configuration option or options.

  • id (Symbol)

    Alternative user ID.

Yields:

  • (*args)

    Values of the overridden configuration.



463
464
465
# File 'lib/mediawiki_selenium/environment.rb', line 463

def with_alternative(names, id, &blk)
  with(lookup_all(Array(names), id: id), &blk)
end