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. (See Environment.load and Environment.load_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.



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

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.



57
58
59
# File 'lib/mediawiki_selenium/environment.rb', line 57

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.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/mediawiki_selenium/environment.rb', line 66

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:



87
88
89
# File 'lib/mediawiki_selenium/environment.rb', line 87

def load_default
  load(ENV['MEDIAWIKI_ENVIRONMENT'], 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)


106
107
108
# File 'lib/mediawiki_selenium/environment.rb', line 106

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:



118
119
120
# File 'lib/mediawiki_selenium/environment.rb', line 118

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.



136
137
138
139
140
141
# File 'lib/mediawiki_selenium/environment.rb', line 136

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)


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

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:



157
158
159
160
161
162
163
164
# File 'lib/mediawiki_selenium/environment.rb', line 157

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

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

#browser_nameSymbol

Name of the browser we're using.

Returns:

  • (Symbol)


170
171
172
# File 'lib/mediawiki_selenium/environment.rb', line 170

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)


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

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.



219
220
221
222
223
224
225
# File 'lib/mediawiki_selenium/environment.rb', line 219

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)


229
230
231
# File 'lib/mediawiki_selenium/environment.rb', line 229

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)


251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/mediawiki_selenium/environment.rb', line 251

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:



279
280
281
282
283
# File 'lib/mediawiki_selenium/environment.rb', line 279

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.



299
300
301
# File 'lib/mediawiki_selenium/environment.rb', line 299

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)


310
311
312
# File 'lib/mediawiki_selenium/environment.rb', line 310

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

#remote?Boolean

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

Returns:

  • (Boolean)


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

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.



336
337
338
339
340
341
342
343
344
345
# File 'lib/mediawiki_selenium/environment.rb', line 336

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)


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

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)


371
372
373
# File 'lib/mediawiki_selenium/environment.rb', line 371

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)


382
383
384
# File 'lib/mediawiki_selenium/environment.rb', line 382

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.



393
394
395
396
397
398
# File 'lib/mediawiki_selenium/environment.rb', line 393

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"


411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/mediawiki_selenium/environment.rb', line 411

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.



454
455
456
# File 'lib/mediawiki_selenium/environment.rb', line 454

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