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
107
# File 'lib/mediawiki_selenium/environment.rb', line 103

def initialize(*configs)
  @_config = configs.map { |config| normalize_config(config) }.reduce(:merge)
  @_factory_cache = {}
  @_current_alternatives = {}
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)


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

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:



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

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.



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

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

#browserWatir::Browser

Browser with which to drive tests.

Returns:

  • (Watir::Browser)


154
155
156
# File 'lib/mediawiki_selenium/environment.rb', line 154

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:



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

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)


177
178
179
# File 'lib/mediawiki_selenium/environment.rb', line 177

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

#current_alternative(key) ⇒ Object

Returns the current alternate ID for the given configuration key.

Parameters:

  • key (Symbol)

    Configuration key.



185
186
187
# File 'lib/mediawiki_selenium/environment.rb', line 185

def current_alternative(key)
  @_current_alternatives[key]
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)


200
201
202
# File 'lib/mediawiki_selenium/environment.rb', line 200

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.



234
235
236
237
238
239
240
# File 'lib/mediawiki_selenium/environment.rb', line 234

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)


244
245
246
# File 'lib/mediawiki_selenium/environment.rb', line 244

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)


266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/mediawiki_selenium/environment.rb', line 266

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:



294
295
296
297
298
# File 'lib/mediawiki_selenium/environment.rb', line 294

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.



314
315
316
# File 'lib/mediawiki_selenium/environment.rb', line 314

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)


325
326
327
# File 'lib/mediawiki_selenium/environment.rb', line 325

def password(id = nil)
  lookup(:mediawiki_password, id: id, default: -> { lookup(:mediawiki_password) })
end

#remote?Boolean

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

Returns:

  • (Boolean)


334
335
336
# File 'lib/mediawiki_selenium/environment.rb', line 334

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.



351
352
353
354
355
356
357
358
359
360
# File 'lib/mediawiki_selenium/environment.rb', line 351

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)


368
369
370
371
372
373
374
375
376
377
# File 'lib/mediawiki_selenium/environment.rb', line 368

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)


386
387
388
# File 'lib/mediawiki_selenium/environment.rb', line 386

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)


397
398
399
# File 'lib/mediawiki_selenium/environment.rb', line 397

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.



408
409
410
411
412
413
# File 'lib/mediawiki_selenium/environment.rb', line 408

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"


426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/mediawiki_selenium/environment.rb', line 426

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.



469
470
471
472
473
474
475
476
477
478
# File 'lib/mediawiki_selenium/environment.rb', line 469

def with_alternative(names, id, &blk)
  names = Array(names)

  original_alts = @_current_alternatives.dup
  @_current_alternatives.merge!(names.each.with_object({}) { |n, alts| alts[n] = id })

  with(lookup_all(names, id: id), &blk)
ensure
  @_current_alternatives = original_alts
end