chemistrykit 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -1
- data/.travis.yml +0 -4
- data/CHANGELOG.md +29 -0
- data/Rakefile +66 -53
- data/bin/ckit +3 -1
- data/chemistrykit.gemspec +8 -3
- data/features/brew.feature +18 -17
- data/features/catalyst.feature +4 -4
- data/features/concurrency.feature +61 -0
- data/features/exit_status.feature +5 -5
- data/features/global-config.feature +26 -0
- data/features/load_page_objects.feature +4 -4
- data/features/multi-config.feature +10 -10
- data/features/step_definitions/steps.rb +5 -3
- data/features/support/env.rb +4 -2
- data/lib/chemistrykit.rb +2 -0
- data/lib/chemistrykit/catalyst.rb +5 -4
- data/lib/chemistrykit/cli/beaker.rb +3 -0
- data/lib/chemistrykit/cli/cli.rb +54 -28
- data/lib/chemistrykit/cli/formula.rb +5 -2
- data/lib/chemistrykit/cli/helpers/formula_loader.rb +32 -19
- data/lib/chemistrykit/cli/new.rb +5 -2
- data/lib/chemistrykit/configuration.rb +54 -0
- data/lib/chemistrykit/formula/base.rb +6 -3
- data/lib/chemistrykit/parallel_tests_mods.rb +55 -0
- data/lib/chemistrykit/shared_context.rb +13 -4
- data/lib/templates/chemistrykit/config.yaml.tt +22 -17
- data/lib/templates/chemistrykit/formulas/lib/formula.rb +5 -2
- data/spec/chemistrykit/catalyst_spec.rb +5 -9
- data/spec/chemistrykit/cli/helpers/formula_loader_spec.rb +15 -13
- data/spec/chemistrykit/configuration_spec.rb +56 -0
- data/spec/chemistrykit/formula/base_spec.rb +4 -2
- data/spec/spec_helper.rb +4 -1
- data/spec/support/config.yaml +7 -0
- metadata +65 -6
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: Initialize a global configuration
|
2
|
+
In order to configure chemistrykit
|
3
|
+
As a chemistry kit harness developer
|
4
|
+
I want to specify configurations in a central file so they are available in test scripts
|
5
|
+
|
6
|
+
Scenario: Use a configuration option in a beaker
|
7
|
+
Given I run `ckit new global-config-test`
|
8
|
+
And I cd to "global-config-test"
|
9
|
+
And a file named "beakers/test_beaker.rb" with:
|
10
|
+
"""
|
11
|
+
describe "Cheese", :depth => 'shallow' do
|
12
|
+
it "loads an external web page" do
|
13
|
+
@driver.get @config.base_url
|
14
|
+
@driver.title.should include("Google")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
And I overwrite config.yaml with:
|
19
|
+
"""
|
20
|
+
base_url: http://www.google.com
|
21
|
+
selenium_connect:
|
22
|
+
log: 'evidence'
|
23
|
+
host: 'localhost'
|
24
|
+
"""
|
25
|
+
When I run `ckit brew`
|
26
|
+
Then the stdout should contain "1 example, 0 failures"
|
@@ -6,9 +6,9 @@ Formulas should be loaded in the correct order with thier dependencies
|
|
6
6
|
And I cd to "big-project"
|
7
7
|
And a file named "config.yaml" with:
|
8
8
|
"""
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
selenium_connect:
|
10
|
+
log: 'evidence'
|
11
|
+
host: 'localhost'
|
12
12
|
"""
|
13
13
|
And a file named "formulas/big.rb" with:
|
14
14
|
"""
|
@@ -34,7 +34,7 @@ Formulas should be loaded in the correct order with thier dependencies
|
|
34
34
|
end
|
35
35
|
"""
|
36
36
|
|
37
|
-
And a file named "
|
37
|
+
And a file named "beakers/big_beaker.rb" with:
|
38
38
|
"""
|
39
39
|
describe "Big", :depth => 'shallow' do
|
40
40
|
let(:book) { Formulas::BigProject.new(@driver) }
|
@@ -6,7 +6,7 @@ Feature: Support for multiple configuration files
|
|
6
6
|
Background:
|
7
7
|
Given I run `ckit new config-test`
|
8
8
|
And I cd to "config-test"
|
9
|
-
And a file named "
|
9
|
+
And a file named "beakers/test_beaker.rb" with:
|
10
10
|
"""
|
11
11
|
describe "Cheese", :depth => 'shallow' do
|
12
12
|
it "loads an external web page" do
|
@@ -19,9 +19,9 @@ Feature: Support for multiple configuration files
|
|
19
19
|
Given a directory named "evidence_config"
|
20
20
|
When I overwrite config.yaml with:
|
21
21
|
"""
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
selenium_connect:
|
23
|
+
log: 'evidence_config'
|
24
|
+
host: 'localhost'
|
25
25
|
"""
|
26
26
|
When I run `ckit brew`
|
27
27
|
Then the stdout should contain "1 example, 0 failures"
|
@@ -32,9 +32,9 @@ Feature: Support for multiple configuration files
|
|
32
32
|
Given a directory named "evidence_alternate"
|
33
33
|
And a file named "alternate.yaml" with:
|
34
34
|
"""
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
selenium_connect:
|
36
|
+
log: 'evidence_alternate'
|
37
|
+
host: 'localhost'
|
38
38
|
"""
|
39
39
|
When I run `ckit brew --config alternate.yaml`
|
40
40
|
Then the stdout should contain "1 example, 0 failures"
|
@@ -45,9 +45,9 @@ Feature: Support for multiple configuration files
|
|
45
45
|
Given a directory named "evidence_alternate"
|
46
46
|
And a file named "alternate.yaml" with:
|
47
47
|
"""
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
selenium_connect:
|
49
|
+
log: 'evidence_alternate'
|
50
|
+
host: 'localhost'
|
51
51
|
"""
|
52
52
|
When I run `ckit brew -c alternate.yaml`
|
53
53
|
Then the stdout should contain "1 example, 0 failures"
|
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
3
|
+
When /^I overwrite ([^"]*) with:$/ do | file_name, file_content |
|
4
|
+
# Modified from https://github.com/cucumber/aruba/blob/master/lib/aruba/cucumber.rb
|
3
5
|
require 'erb'
|
4
6
|
data = ERB.new(file_content)
|
5
7
|
overwrite_file(file_name, data.result)
|
6
8
|
end
|
7
9
|
|
8
10
|
Then(/^the exit code should be (\d+)$/) do |exit_status|
|
9
|
-
|
11
|
+
@last_exit_status.should == exit_status.to_i
|
10
12
|
end
|
data/features/support/env.rb
CHANGED
data/lib/chemistrykit.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
1
3
|
require 'csv'
|
2
4
|
|
3
5
|
module ChemistryKit
|
6
|
+
# Serves as a hash wrapper class for injecting data into formulas
|
4
7
|
class Catalyst
|
5
|
-
#this class serves as a standard container for data that can be injected into a formula
|
8
|
+
# this class serves as a standard container for data that can be injected into a formula
|
6
9
|
|
7
10
|
def initialize(data_file)
|
8
11
|
@data = {}
|
@@ -24,9 +27,7 @@ module ChemistryKit
|
|
24
27
|
private
|
25
28
|
|
26
29
|
def validate_key(key)
|
27
|
-
unless @data.has_key?(key.to_sym)
|
28
|
-
raise "Unknown \"#{key}\""
|
29
|
-
end
|
30
|
+
raise "Unknown \"#{key}\"" unless @data.has_key?(key.to_sym)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
data/lib/chemistrykit/cli/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require 'rspec'
|
3
5
|
require 'ci/reporter/rake/rspec_loader'
|
@@ -7,45 +9,55 @@ require 'chemistrykit/cli/beaker'
|
|
7
9
|
require 'chemistrykit/cli/helpers/formula_loader'
|
8
10
|
require 'chemistrykit/catalyst'
|
9
11
|
require 'chemistrykit/formula/base'
|
12
|
+
require 'selenium-connect'
|
13
|
+
require 'chemistrykit/configuration'
|
10
14
|
|
11
15
|
module ChemistryKit
|
12
16
|
module CLI
|
13
17
|
|
18
|
+
# Registers the formula and beaker commands
|
14
19
|
class Generate < Thor
|
15
20
|
register(ChemistryKit::CLI::FormulaGenerator, 'formula', 'formula [NAME]', 'generates a page object')
|
16
21
|
register(ChemistryKit::CLI::BeakerGenerator, 'beaker', 'beaker [NAME]', 'generates a beaker')
|
17
22
|
end
|
18
23
|
|
24
|
+
# Main Chemistry Kit CLI Class
|
19
25
|
class CKitCLI < Thor
|
20
26
|
|
21
27
|
register(ChemistryKit::CLI::New, 'new', 'new [NAME]', 'Creates a new ChemistryKit project')
|
22
28
|
check_unknown_options!
|
23
29
|
default_task :help
|
24
30
|
|
25
|
-
desc
|
26
|
-
subcommand
|
31
|
+
desc 'generate SUBCOMMAND', 'generate <formula> or <beaker> [NAME]'
|
32
|
+
subcommand 'generate', Generate
|
27
33
|
|
28
|
-
desc
|
29
|
-
method_option :params, :
|
30
|
-
method_option :tag, :
|
31
|
-
method_option :config, :
|
32
|
-
#TODO there should be a facility to simply pass a path to this command
|
33
|
-
method_option :
|
34
|
+
desc 'brew', 'Run ChemistryKit'
|
35
|
+
method_option :params, type: :hash
|
36
|
+
method_option :tag, default: ['depth:shallow'], type: :array
|
37
|
+
method_option :config, default: 'config.yaml', aliases: '-c', desc: 'Supply alternative config file.'
|
38
|
+
# TODO there should be a facility to simply pass a path to this command
|
39
|
+
method_option :beakers, type: :array
|
40
|
+
# This is set if the thread is being run in parallel so as not to trigger recursive concurency
|
41
|
+
method_option :parallel, default: false
|
34
42
|
|
35
43
|
def brew
|
36
|
-
load_config
|
37
|
-
|
44
|
+
config = load_config options['config']
|
45
|
+
# TODO perhaps the params should be rolled into the available
|
46
|
+
# config object injected into the system?
|
38
47
|
pass_params if options['params']
|
39
48
|
turn_stdout_stderr_on_off
|
40
49
|
set_logs_dir
|
41
50
|
load_page_objects
|
42
51
|
setup_tags
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
52
|
+
# configure rspec
|
53
|
+
rspec_config(config)
|
54
|
+
# get those beakers that should be executed
|
55
|
+
beakers = options['beakers'] ? options['beakers'] : Dir.glob(File.join(Dir.getwd, 'beakers/*'))
|
56
|
+
# based on concurrency parameter run tests
|
57
|
+
if config.concurrency > 1 && ! options['parallel']
|
58
|
+
run_in_parallel beakers, config.concurrency
|
47
59
|
else
|
48
|
-
run_rspec
|
60
|
+
run_rspec beakers
|
49
61
|
end
|
50
62
|
end
|
51
63
|
|
@@ -59,7 +71,7 @@ module ChemistryKit
|
|
59
71
|
|
60
72
|
def load_page_objects
|
61
73
|
loader = ChemistryKit::CLI::Helpers::FormulaLoader.new
|
62
|
-
loader.get_formulas(File.join(Dir.getwd, 'formulas')).each {|file| require file }
|
74
|
+
loader.get_formulas(File.join(Dir.getwd, 'formulas')).each { |file| require file }
|
63
75
|
end
|
64
76
|
|
65
77
|
def set_logs_dir
|
@@ -70,10 +82,9 @@ module ChemistryKit
|
|
70
82
|
ENV['CI_CAPTURE'] = 'on'
|
71
83
|
end
|
72
84
|
|
73
|
-
def load_config
|
74
|
-
|
75
|
-
|
76
|
-
ENV['CONFIG_FILE'] = options['config']
|
85
|
+
def load_config(file_name)
|
86
|
+
config_file = File.join(Dir.getwd, file_name)
|
87
|
+
ChemistryKit::Configuration.initialize_with_yaml config_file
|
77
88
|
end
|
78
89
|
|
79
90
|
def setup_tags
|
@@ -91,25 +102,40 @@ module ChemistryKit
|
|
91
102
|
end
|
92
103
|
end
|
93
104
|
|
94
|
-
def rspec_config #Some of these bits work and others don't
|
105
|
+
def rspec_config(config) # Some of these bits work and others don't
|
106
|
+
SeleniumConnect.configure do |c|
|
107
|
+
c.populate_with_hash config.selenium_connect
|
108
|
+
end
|
95
109
|
RSpec.configure do |c|
|
96
110
|
c.treat_symbols_as_metadata_keys_with_true_values = true
|
97
111
|
c.filter_run @tags[:filter] unless @tags[:filter].nil?
|
98
112
|
c.filter_run_excluding @tags[:exclusion_filter] unless @tags[:exclusion_filter].nil?
|
99
|
-
c.
|
113
|
+
c.before(:each) do
|
114
|
+
@driver = SeleniumConnect.start
|
115
|
+
@config = config
|
116
|
+
end
|
117
|
+
c.after(:each) do
|
118
|
+
@driver.quit
|
119
|
+
end
|
120
|
+
c.after(:all) do
|
121
|
+
SeleniumConnect.finish
|
122
|
+
end
|
100
123
|
c.order = 'random'
|
101
124
|
c.default_path = 'beakers'
|
102
125
|
c.pattern = '**/*_beaker.rb'
|
103
126
|
end
|
104
127
|
end
|
105
128
|
|
106
|
-
def
|
107
|
-
|
108
|
-
|
129
|
+
def run_in_parallel(beakers, concurrency)
|
130
|
+
require 'parallel_tests'
|
131
|
+
require 'chemistrykit/parallel_tests_mods'
|
132
|
+
ParallelTests::CLI.new.run(%w(--type rspec) + ['-n', concurrency.to_s] + %w(-o --beakers=) + beakers)
|
133
|
+
end
|
109
134
|
|
135
|
+
def run_rspec(beakers)
|
110
136
|
RSpec::Core::Runner.run(beakers)
|
111
137
|
end
|
112
138
|
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
139
|
+
end # CkitCLI
|
140
|
+
end # CLI
|
141
|
+
end # ChemistryKit
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
1
3
|
require 'thor/group'
|
2
4
|
|
3
5
|
module ChemistryKit
|
4
6
|
module CLI
|
7
|
+
# Creates a starting formula from a template
|
5
8
|
class FormulaGenerator < Thor::Group
|
6
9
|
include Thor::Actions
|
7
10
|
|
@@ -12,8 +15,8 @@ module ChemistryKit
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def copy_file
|
15
|
-
template
|
16
|
-
template
|
18
|
+
template 'formula.tt', "./formulas/#{name}.rb"
|
19
|
+
template 'beaker_with_formula.tt', "./beakers/#{name}_beaker.rb"
|
17
20
|
end
|
18
21
|
|
19
22
|
end
|
@@ -1,16 +1,18 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
1
3
|
module ChemistryKit
|
2
4
|
module CLI
|
3
5
|
module Helpers
|
6
|
+
# this loader returns the formulas found in a path according to these rules:
|
7
|
+
# - directories loaded in alpha order
|
8
|
+
# - children directories loaded before parents
|
9
|
+
# - files loaded in alpha order
|
10
|
+
# - lib directories are loaded before any other
|
11
|
+
# - rules stack
|
4
12
|
class FormulaLoader
|
5
|
-
#this loader returns the formulas found in a path according to these rules:
|
6
|
-
#- directories loaded in alpha order
|
7
|
-
#- children directories loaded before parents
|
8
|
-
#- files loaded in alpha order
|
9
|
-
#- lib directories are loaded before any other
|
10
|
-
#- rules stack
|
11
13
|
|
12
14
|
def initialize
|
13
|
-
@formulas =
|
15
|
+
@formulas = []
|
14
16
|
end
|
15
17
|
|
16
18
|
def get_formulas(path)
|
@@ -19,25 +21,36 @@ module ChemistryKit
|
|
19
21
|
end
|
20
22
|
|
21
23
|
private
|
24
|
+
|
22
25
|
def gather(path)
|
23
|
-
#get all the directories for path
|
24
|
-
directories =
|
26
|
+
# get all the directories for path
|
27
|
+
directories = get_directories path
|
25
28
|
if directories.empty?
|
26
|
-
#if there are no directories is empty just get all the files and add it to the instance variable
|
27
|
-
@formulas.concat Dir.glob(File.join(path,
|
29
|
+
# if there are no directories is empty just get all the files and add it to the instance variable
|
30
|
+
@formulas.concat Dir.glob(File.join(path, '*.rb')).sort
|
28
31
|
else
|
29
|
-
#otherwise for each of them recurse into it
|
32
|
+
# otherwise for each of them recurse into it
|
30
33
|
directories.sort!
|
31
|
-
|
32
|
-
#if there is a lib directory, put that at the front
|
33
|
-
directories.delete('lib')
|
34
|
-
directories.unshift('lib')
|
35
|
-
end
|
34
|
+
move_lib_to_top_of_stack directories
|
36
35
|
directories.each do |directory|
|
37
36
|
gather(File.join(path, directory))
|
38
37
|
end
|
39
|
-
#and then after add the files in the original parent
|
40
|
-
@formulas.concat Dir.glob(File.join(path,
|
38
|
+
# and then after add the files in the original parent
|
39
|
+
@formulas.concat Dir.glob(File.join(path, '*.rb')).sort
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_directories(path)
|
44
|
+
Dir.entries(path).select do |entry|
|
45
|
+
(File.directory? File.join(path, entry)) && !(entry == '.' || entry == '..')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def move_lib_to_top_of_stack(directories)
|
50
|
+
if directories.include? 'lib'
|
51
|
+
# if there is a lib directory, put that at the front
|
52
|
+
directories.delete('lib')
|
53
|
+
directories.unshift('lib')
|
41
54
|
end
|
42
55
|
end
|
43
56
|
end
|
data/lib/chemistrykit/cli/new.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
1
3
|
require 'thor/group'
|
2
4
|
|
3
5
|
module ChemistryKit
|
4
6
|
module CLI
|
7
|
+
# Creates a new test harness
|
5
8
|
class New < Thor::Group
|
6
9
|
include Thor::Actions
|
7
10
|
|
@@ -12,11 +15,11 @@ module ChemistryKit
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def create_project
|
15
|
-
directory
|
18
|
+
directory 'templates/chemistrykit', File.join(Dir.getwd, name)
|
16
19
|
end
|
17
20
|
|
18
21
|
def notify
|
19
|
-
say
|
22
|
+
say 'Your test harness has been created.'
|
20
23
|
end
|
21
24
|
|
22
25
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module ChemistryKit
|
6
|
+
# Default configuration class
|
7
|
+
class Configuration
|
8
|
+
|
9
|
+
attr_accessor :concurrency,
|
10
|
+
:base_url,
|
11
|
+
:selenium_connect
|
12
|
+
|
13
|
+
def initialize(hash)
|
14
|
+
# set defaults
|
15
|
+
@concurrency = 1
|
16
|
+
|
17
|
+
# overide with argument
|
18
|
+
populate_with_hash hash
|
19
|
+
validate_config
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.initialize_with_yaml(file)
|
23
|
+
self.new symbolize_keys YAML.load_file file
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def populate_with_hash(hash)
|
29
|
+
hash.each do |key, value|
|
30
|
+
begin
|
31
|
+
self.send "#{key}=", value unless value.nil?
|
32
|
+
rescue NoMethodError
|
33
|
+
raise ArgumentError.new "The config key: \"#{key}\" is unknown!"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_config
|
39
|
+
if @selenium_connect && @selenium_connect[:host] != 'saucelabs' && @concurrency > 1
|
40
|
+
raise ArgumentError.new 'Concurrency is only supported for the host: "saucelabs"!'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# private static to symbolize recursivly a loaded yaml
|
45
|
+
def self.symbolize_keys(hash)
|
46
|
+
hash.reduce({}) do |result, (key, value)|
|
47
|
+
new_key = key.class == String ? key.to_sym : key
|
48
|
+
new_value = value.class == Hash ? symbolize_keys(value) : value
|
49
|
+
result[new_key] = new_value
|
50
|
+
result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|