From de106543aa872518e79fee1c965e889b3cfd5963 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Mon, 6 Mar 2017 20:03:58 +0000 Subject: [PATCH] Add ability to pass in custom values via config Currently the config Quke exposes is for controlling how it runs and behaves. However feedback has shown that projects based on Quke often require their own configuration values. A common example is usernames and passwords for accounts when testing features that require authentication. Now there are options for dealing with this; setting environment variables and then reading them in within the project being the most obvious solution. You could even write your own configuration mechanism if you had the necessary skills. However Quke is specifically targeted at team members who may not have development backgrounds, and therefore aims to make creating acceptance tests as simple as possible. For this reason, and from speaking with current users we have chosen to extend the existing configuration functionality in Quke to allow users to add their own custom values, which they can then access from the Quke config class within their projects. This means users only have to think about one source for all the configuration within their projects, the `.config.yml` file. This change adds a new method to `Quke::Configuration` called `custom()`, which returns whatever values have been set under a root node called `custom` in the `.config.yml` file. Being YAML, this could be simple key value pairs, or a whole new object in itself. We have also expanded the section on configuration in the README.md to cover this, providing examples of the things you can add and how to access them from your projects. --- .config.example.yml | 27 ++++++++++++- README.md | 8 +++- lib/quke/configuration.rb | 9 ++++- lib/quke/version.rb | 2 +- spec/data/.custom_complex_object.yml | 15 +++++++ spec/data/.custom_key_value_pair.yml | 2 + spec/data/.custom_key_value_pairs.yml | 6 +++ spec/quke/configuration_spec.rb | 58 ++++++++++++++++++++++++++- 8 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 spec/data/.custom_complex_object.yml create mode 100644 spec/data/.custom_key_value_pair.yml create mode 100644 spec/data/.custom_key_value_pairs.yml diff --git a/.config.example.yml b/.config.example.yml index bfaf38e..3878f30 100644 --- a/.config.example.yml +++ b/.config.example.yml @@ -31,6 +31,31 @@ stop_on_error: 1 # elements that take some time to load you can increase this default. max_wait_time: 5 +# Anything you place under the 'custom' node in the `.config.yml` file will be +# available within your steps and page objects by calling +# `Quke::Quke.config.custom`. So using the example below we could access its +# values in the following ways +# +# Quke::Quke.config.custom["default_org_name"] # = "Testy Ltd" +# Quke::Quke.config.custom["accounts"]["account2"]["username"] # = "john.doe@example.gov.uk" +# Quke::Quke.config.custom["urls"]["front_office"] # = "http://myservice.service.gov.uk" +# +# As long as what you add is valid YAML (check with a tool like +# http://www.yamllint.com/) Quke will be able to pick it up and make it +# available in your tests. +custom: + default_org_name: "Testy Ltd" + accounts: + account1: + username: jane.doe@example.gov.uk + password: Av3rystr*ngone + account2: + username: john.doe@example.gov.uk + password: An*th3rstrongone + urls: + front_office: "http://myservice.service.gov.uk" + back_office: "http://admin-myservice.service.gov.uk" + # If you are running Quke behind a proxy you can configure the proxy details # here. You'll need either the hostname or IP of the proxy server (don't include # the http:// bit) and the port number (typically 8080). Currently proxy @@ -64,7 +89,7 @@ browserstack: project: 'Adding browserstack support' # Allows you to specify an identifier for the test run. - # If you intend to repeat a test this might not be that aplicable, but in the + # If you intend to repeat a test this might not be that applicable, but in the # case of one off tests it might be useful name: 'Testing google search' diff --git a/README.md b/README.md index 929ffe3..ca65ee1 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,13 @@ Or install it yourself as ## Configuration -You can use configuration to drive Quke. You can configure Quke using `.config.yml` files. See [.config.example.yml](.config.example.yml) for details of the options to include in your `.config.yml`. +You can configure Quke using a `.config.yml` file. What can be configured essentially falls into one of 3 groups. + +- **Quke configuration** - things like which driver to use, whether to pause between steps, or if Quke should stop in the event of an error +- **Custom configuration** - add your own custom values using the `custom:` node +- **Browserstack configuration** - specify exactly how you want your Browserstack session to run, for example which browser and OS to test against, the project name, and your credentials. + +See [.config.example.yml](.config.example.yml) for full details of the options you can specify in your `.config.yml`. ### Multiple configs diff --git a/lib/quke/configuration.rb b/lib/quke/configuration.rb index 01ab52e..202d7a9 100644 --- a/lib/quke/configuration.rb +++ b/lib/quke/configuration.rb @@ -135,6 +135,10 @@ def use_proxy? proxy['host'] == '' ? false : true end + def custom + @data['custom'] + end + # Override to_s to output the contents of Config as a readable string rather # than the standard object output you get. def to_s @@ -152,6 +156,7 @@ def load_data # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/CyclomaticComplexity + # rubocop:disable Metrics/PerceivedComplexity def default_data!(data) data.merge( 'features_folder' => (data['features'] || 'features').downcase.strip, @@ -159,11 +164,13 @@ def default_data!(data) 'driver' => (data['driver'] || 'phantomjs').downcase.strip, 'pause' => (data['pause'] || '0').to_s.downcase.strip.to_i, 'stop_on_error' => (data['stop_on_error'] || 'false').to_s.downcase.strip, - 'max_wait_time' => (data['max_wait_time'] || Capybara.default_max_wait_time).to_s.downcase.strip.to_i + 'max_wait_time' => (data['max_wait_time'] || Capybara.default_max_wait_time).to_s.downcase.strip.to_i, + 'custom' => (data['custom'] || nil) ) end # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/CyclomaticComplexity + # rubocop:enable Metrics/PerceivedComplexity # rubocop:disable Metrics/MethodLength def browserstack_data(data) diff --git a/lib/quke/version.rb b/lib/quke/version.rb index 9974c9f..e930740 100644 --- a/lib/quke/version.rb +++ b/lib/quke/version.rb @@ -1,3 +1,3 @@ module Quke #:nodoc: - VERSION = '0.2.8'.freeze + VERSION = '0.3.0'.freeze end diff --git a/spec/data/.custom_complex_object.yml b/spec/data/.custom_complex_object.yml new file mode 100644 index 0000000..8002ccf --- /dev/null +++ b/spec/data/.custom_complex_object.yml @@ -0,0 +1,15 @@ +custom: + my_key: my_value + accounts: + account1: + username: yoda + password: greenisgood + account2: + username: vadar + password: redrules + account3: + username: luke + password: fatherissues + troop_numbers: + dark_side_count: 1000000 + light_side_count: 5 diff --git a/spec/data/.custom_key_value_pair.yml b/spec/data/.custom_key_value_pair.yml new file mode 100644 index 0000000..1f4016b --- /dev/null +++ b/spec/data/.custom_key_value_pair.yml @@ -0,0 +1,2 @@ +custom: + my_key: my_value diff --git a/spec/data/.custom_key_value_pairs.yml b/spec/data/.custom_key_value_pairs.yml new file mode 100644 index 0000000..467a789 --- /dev/null +++ b/spec/data/.custom_key_value_pairs.yml @@ -0,0 +1,6 @@ +custom: + my_key1: my_value1 + my_key2: my_value2 + my_key3: my_value3 + my_key4: my_value4 + my_key5: my_value5 diff --git a/spec/quke/configuration_spec.rb b/spec/quke/configuration_spec.rb index 7658615..609eeb0 100644 --- a/spec/quke/configuration_spec.rb +++ b/spec/quke/configuration_spec.rb @@ -120,7 +120,7 @@ describe '#proxy' do context 'when NOT specified in the config file' do - it 'defaults to a blank values' do + it 'defaults to blank values' do Quke::Configuration.file_location = data_path('.no_file.yml') expect(subject.proxy).to eq('host' => '', 'port' => 0, 'no_proxy' => '') end @@ -186,12 +186,66 @@ end end + describe '#custom' do + context 'when NOT specified in the config file' do + it 'defaults to nothing' do + Quke::Configuration.file_location = data_path('.no_file.yml') + expect(subject.custom).to be(nil) + end + end + + context "when 'custom' in the config file holds simple key value pairs" do + it 'returns the key value pair if there is just one' do + Quke::Configuration.file_location = data_path('.custom_key_value_pair.yml') + expect(subject.custom).to eq('my_key' => 'my_value') + end + + it 'returns all key value pairs if there are multiples' do + Quke::Configuration.file_location = data_path('.custom_key_value_pairs.yml') + expect(subject.custom).to eq( + 'my_key1' => 'my_value1', + 'my_key2' => 'my_value2', + 'my_key3' => 'my_value3', + 'my_key4' => 'my_value4', + 'my_key5' => 'my_value5' + ) + end + end + + context "when 'custom' in the config file holds a hierachical object" do + it 'returns a representation of the object' do + Quke::Configuration.file_location = data_path('.custom_complex_object.yml') + expect(subject.custom).to eq( + 'my_key' => 'my_value', + 'accounts' => { + 'account1' => { + 'username' => 'yoda', + 'password' => 'greenisgood' + }, + 'account2' => { + 'username' => 'vadar', + 'password' => 'redrules' + }, + 'account3' => { + 'username' => 'luke', + 'password' => 'fatherissues' + } + }, + 'troop_numbers' => { + 'dark_side_count' => 1_000_000, + 'light_side_count' => 5 + } + ) + end + end + end + describe '#to_s' do it 'return the values held by the instance and not an instance ID' do Quke::Configuration.file_location = data_path('.no_file.yml') # rubocop:disable Style/StringLiterals expect(subject.to_s).to eq( - "{\"features_folder\"=>\"features\", \"app_host\"=>\"\", \"driver\"=>\"phantomjs\", \"pause\"=>0, \"stop_on_error\"=>\"false\", \"max_wait_time\"=>2, \"browserstack\"=>{\"username\"=>\"\", \"auth_key\"=>\"\"}, \"proxy\"=>{\"host\"=>\"\", \"port\"=>0, \"no_proxy\"=>\"\"}}" + "{\"features_folder\"=>\"features\", \"app_host\"=>\"\", \"driver\"=>\"phantomjs\", \"pause\"=>0, \"stop_on_error\"=>\"false\", \"max_wait_time\"=>2, \"custom\"=>nil, \"browserstack\"=>{\"username\"=>\"\", \"auth_key\"=>\"\"}, \"proxy\"=>{\"host\"=>\"\", \"port\"=>0, \"no_proxy\"=>\"\"}}" ) # rubocop:enable Style/StringLiterals end