chemistrykit 3.7.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rubocop.yml CHANGED
@@ -3,3 +3,9 @@ LineLength:
3
3
 
4
4
  MethodLength:
5
5
  Max: 30
6
+
7
+ SymbolName:
8
+ AllowCamelCase: false
9
+
10
+ Documentation:
11
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ #3.8.0 (2013-08-05)
2
+ Implemented the chemists feature to add user data management as well as a simplified strategy for loading formulas
3
+
4
+ - Bumped version to 3.8.0 to prepare for release.
5
+ - cleaned up docs a little and ensured the default chemists csv file wouldnt cause a failure if left unpopulated
6
+ - added a uuid injector for a token in csv files
7
+ - integrated the fomula lab into the suite runner and added basic cucumber tests
8
+ - built out the rest of the formula lab and integration tests
9
+ - updated templates to create the default chemists folder
10
+ - upped the flog limits... still need to understand how that will help
11
+ - updated chemist and chemist repository to include key, as well as selection by key, random, or first of type
12
+ - updated documentation of new changes
13
+ - built out the rest of the csv chemist repository
14
+ - started building out the user data repository from a csv file.
15
+ - built out a basic chemist class
16
+ - added module to define chemist aware for the formulas to include
17
+ - added reek and rubocop to the guard file
18
+ - moved dev dependencies to gemfile, added in new code quality checks, fixed some code issues found by rubocop, updated rake tasks
19
+ - updated selenium connect version
20
+ - added animoto mention to friends section
21
+
1
22
  #3.7.0 (2013-07-24)
2
23
  updated evidence to put in test based folders and added configuration for the retry functionality
3
24
 
data/Gemfile CHANGED
@@ -2,3 +2,16 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify dependencies in chemistrykit.gemspec
4
4
  gemspec
5
+
6
+ gem 'aruba', '~> 0.5.1'
7
+ gem 'cucumber', '~> 1.2.1'
8
+ gem 'rake', '~> 10.0.3'
9
+ gem 'rubocop', git: 'https://github.com/bbatsov/rubocop.git', branch: 'master'
10
+ gem 'guard-rspec', '~> 3.0.2'
11
+ gem 'coveralls', '~> 0.6.7'
12
+ gem 'flog'
13
+ gem 'flay'
14
+ gem 'ruby2ruby'
15
+ gem 'reek', '1.3.1'
16
+ gem 'guard-reek'
17
+ gem 'guard-rubocop'
data/Guardfile CHANGED
@@ -1,5 +1,14 @@
1
+ # Encoding: utf-8
2
+
1
3
  guard :rspec do
2
4
  watch(%r{^spec/unit/.+_spec\.rb$})
5
+ watch(%r{^spec/integration/.+_spec\.rb$})
3
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/lib/#{m[1]}_spec.rb" }
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/integration/lib/#{m[1]}_spec.rb" }
4
8
  watch('spec/spec_helper.rb') { "spec" }
5
9
  end
10
+
11
+ guard :rubocop, all_on_start: false do
12
+ watch(%r{^spec/.+\.rb$})
13
+ watch(%r{^lib/.+\.rb$})
14
+ end
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- #ChemistryKit 3.7.0 (2013-07-24)
1
+ #ChemistryKit 3.8.0 (2013-08-05)
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/chemistrykit.png)](http://badge.fury.io/rb/chemistrykit) [![Build Status](https://travis-ci.org/arrgyle/chemistrykit.png?branch=develop)](https://travis-ci.org/jrobertfox/chef-broiler-platter) [![Code Climate](https://codeclimate.com/github/arrgyle/chemistrykit.png)](https://codeclimate.com/github/arrgyle/chemistrykit) [![Coverage Status](https://coveralls.io/repos/arrgyle/chemistrykit/badge.png?branch=develop)](https://coveralls.io/r/arrgyle/chemistrykit?branch=develop)
4
4
 
5
5
  ### A simple and opinionated web testing framework for Selenium WebDriver
6
6
 
7
- This framework was designed to help you get started with Selenium WebDriver quickly, to grow as needed, and to avoid common pitfalls by following convention over configuration. To checkout the user group go [here](https://groups.google.com/forum/#!forum/chemistrykit-users).
7
+ This framework was designed to help you get started with Selenium WebDriver quickly, to grow as needed, and to avoid common pitfalls by following convention over configuration. To checkout the user group go [here](https://groups.google.com/forum/#!forum/chemistrykit-users). For more usage examples check out our [Friends](#friends) section!
8
8
 
9
9
 
10
10
  ChemistryKit's inspiration comes from the Saunter Selenium framework which is available in Python and PHP. You can find more about it [here](http://element34.ca/products/saunter).
@@ -17,6 +17,7 @@ All the documentation for ChemistryKit can be found in this README, organized as
17
17
  - [Command Line Usage](#command-line-usage)
18
18
  - [Contribution Guidelines](#contribution-guidelines)
19
19
  - [Deployment](#deployment)
20
+ - [Friends](#friends)
20
21
 
21
22
  ##Getting Started
22
23
 
@@ -84,6 +85,54 @@ Code in the `formula` directory can be used to build out page objects and helper
84
85
 
85
86
  So for example if you have a `alpha_page.rb` file in your formulas directory that depends on a `helpers.rb` file, then you best put the `helpers.rb` file in the `lib` directory so it is loaded before the file that depends on it.
86
87
 
88
+ ###Chemists! The Users of your System Under Test
89
+ With ChemistryKit we made it simple to encapsulate data about a particular user that is "using" your application that you are testing. We call them chemists. When you create a new test harness there will be a `chemists` folder that contains an empty `chemists.csv` file with only the words `key` and `type` at the top. In this folder you can create any number of files with arbitrary user data, **just make sure to include the `key` and`type` headings!** such as:
90
+
91
+ /chemists/my_valid_users.csv
92
+ /chemists/my_bad_users.csv
93
+
94
+ An example file might look like this:
95
+
96
+ Key,Type,Email,Name,Password
97
+ admin1,admin,admin@email.com,Mr. Admin,abc123$
98
+ normal1,normal,normal@email.com,Ms. Normal,test123%
99
+ normal2,normal,normal2@email.com,Ms. Normals,test123%
100
+
101
+ The `key` should be unique so you can pick a specific user, the type, allows you to group users to aid in their selection ad detailed below.
102
+
103
+ You can also put a special token in your csv files: `{{UUID}}` which will be replaced on runtime with a unique identifier. This can be helpful for ensuring certain date is unique across your tests, especially with concurrent runs.
104
+
105
+ Chemists are made available to your formulas simply by including the `ChemistAware` module in your formula, and loading the formula with the instance of `FormulaLab` provided to your beakers:
106
+
107
+ ```Ruby
108
+ # my_formula.rb
109
+ module Formulas
110
+ class MyFormula < Formula
111
+ include ChemistryKit::Formula::ChemistAware
112
+ ...
113
+ if chemist.email ...
114
+ ...
115
+ end
116
+ end
117
+
118
+
119
+ # my_beaker.rb
120
+ describe "my beaker", :depth => 'shallow' do
121
+ let(:my_formula) { @formula_lab.using('my_formula').with('admin1').mix }
122
+ # or
123
+ let(:my_other_formula) { @formula_lab.formula.mix('my_other_formula') }
124
+ ...
125
+ end
126
+ ```
127
+
128
+ Here is a summary of the other methods available:
129
+
130
+ - `.with(key)` - Load a specific chemist by the key.
131
+ - `.with_random(type)` - Load a chemist at random from all those matching `type`
132
+ - `.with_first(type)` - Load whatever chemist is first matched by `type`
133
+
134
+ The FormulaLab will handle the heavy lifting of assembling your formal with a driver and correct user (if the formula needs one).
135
+
87
136
  ###Execution Order
88
137
  Chemistry Kit executes specs in a random order. This is intentional. Knowing the order a spec will be executed in allows for dependencies between them to creep in. Sometimes unintentionally. By having them go in a random order parallelization becomes a much easier.
89
138
 
@@ -211,3 +260,11 @@ And another to finish the release:
211
260
  rake release_finish['A helpful tag message that will be included in the gemspec.']
212
261
 
213
262
  This handles updating the change log, committing, and tagging the release.
263
+
264
+ ## Friends
265
+
266
+ Below you can find some honorable mentions of those friends that are using ChemistryKit:
267
+
268
+ ![image](http://d14f1fnryngsxt.cloudfront.net/images/logo/animotologotext_f78c60cbbd36837c7aad596e3b3bb019.svg)
269
+
270
+ We are proud that [Animoto](http://animoto.com/) uses ChemistryKit to help them test their awesome web app.
data/Rakefile CHANGED
@@ -4,14 +4,21 @@ require 'bundler/gem_tasks'
4
4
  require 'cucumber'
5
5
  require 'cucumber/rake/task'
6
6
  require 'rspec/core/rake_task'
7
+ require 'rubocop/rake_task'
8
+ require 'flog_task'
9
+ require 'flay_task'
10
+ require 'reek/rake/task'
7
11
 
8
12
  task default: :build
9
13
 
10
14
  desc 'Runs standard build activities.'
11
- task build: [:clean, :prepare, :rubocop, :unit, :integration]
15
+ task build: [:clean, :prepare, :quality, :unit, :integration]
12
16
 
13
- desc 'Runs standard build activities for ci server.'
14
- task build_full: [:clean, :prepare, :rubocop, :unit, :integration, :system]
17
+ desc 'Runs full build activities.'
18
+ task build_full: [:clean, :prepare, :quality, :unit, :integration, :system]
19
+
20
+ desc 'Runs quality checks.'
21
+ task quality: [:rubocop, :reek, :flog_total, :flog_average, :flay]
15
22
 
16
23
  desc 'Removes the build directory.'
17
24
  task :clean do
@@ -40,9 +47,30 @@ end
40
47
 
41
48
  Cucumber::Rake::Task.new(:system)
42
49
 
43
- desc 'Runs code quality check'
44
- task :rubocop do
45
- sh 'rubocop'
50
+ Rubocop::RakeTask.new
51
+
52
+ # TODO: lower the quality score and improve the code!
53
+ FlogTask.new :flog_total, 2000 do |t|
54
+ t.method = :total_score
55
+ t.verbose = true
56
+ end
57
+
58
+ # TODO: lower the quality score and improve the code!
59
+ FlogTask.new :flog_average, 20 do |t|
60
+ t.method = :average
61
+ t.verbose = true
62
+ end
63
+
64
+ # TODO: lower the quality score and improve the code!
65
+ FlayTask.new :flay, 2000 do |t|
66
+ t.verbose = true
67
+ end
68
+
69
+ # TODO: fix all the smells and turn on failing on error
70
+ Reek::Rake::Task.new do |t|
71
+ t.fail_on_error = false
72
+ t.verbose = false
73
+ t.reek_opts = '--quiet'
46
74
  end
47
75
 
48
76
  # TODO This could probably be more cleanly automated
data/chemistrykit.gemspec CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'chemistrykit'
5
- s.version = '3.7.0'
5
+ s.version = '3.8.0'
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.authors = ['Dave Haeffner', 'Jason Fox']
8
8
  s.email = ['dave@arrgyle.com', 'jason@arrgyle.com']
9
9
  s.homepage = 'https://github.com/arrgyle/chemistrykit'
10
10
  s.summary = 'A simple and opinionated web testing framework for Selenium that follows convention over configuration.'
11
- s.description = 'updated evidence to put in test based folders and added configuration for the retry functionality'
11
+ s.description = 'Implemented the chemists feature to add user data management as well as a simplified strategy for loading formulas'
12
12
  s.license = 'MIT'
13
13
 
14
14
  s.files = `git ls-files`.split($/)
@@ -24,16 +24,8 @@ Gem::Specification.new do |s|
24
24
  s.add_dependency 'builder', '~> 3.2.2'
25
25
  s.add_dependency 'selenium-webdriver', '~> 2.29.0'
26
26
  s.add_dependency 'rest-client', '~> 1.6.7'
27
- s.add_dependency 'selenium-connect', '~> 3.3.1'
27
+ s.add_dependency 'selenium-connect', '~> 3.4.0'
28
28
  s.add_dependency 'parallel_tests', '~> 0.15.0'
29
29
  s.add_dependency 'parallel', '~> 0.7.0'
30
30
  s.add_dependency 'rspec-retry', '~> 0.2.1'
31
-
32
- s.add_development_dependency 'rspec', '~> 2.14.1'
33
- s.add_development_dependency 'aruba', '~> 0.5.1'
34
- s.add_development_dependency 'cucumber', '~> 1.2.1'
35
- s.add_development_dependency 'rake', '~> 10.0.3'
36
- s.add_development_dependency 'rubocop', '~> 0.9.0'
37
- s.add_development_dependency 'guard-rspec', '~> 3.0.2'
38
- s.add_development_dependency 'coveralls', '~> 0.6.7'
39
31
  end
data/exceptions.reek ADDED
@@ -0,0 +1,3 @@
1
+ FeatureEnvy:
2
+ exclude:
3
+ - method_missing # the very nature of handling the method name requires this
@@ -0,0 +1,123 @@
1
+ Feature: Brewing a ChemistryKit project
2
+
3
+ Background: Setup the project
4
+ Given I run `ckit new chemists-test`
5
+ And I cd to "chemists-test"
6
+ And a file named "chemists/chemists.csv" with:
7
+ """
8
+ Key,Type,Email,Name,Password
9
+ admin1,admin,admin@email.com,Mr. Admin,abc123$
10
+ normal1,normal,normal@email.com,Ms. Normal,test123%
11
+ ran1,random,normal@email.com,Ms. Normal,test123%
12
+ ran2,random,normal@email.com,Ms. Normal,test123%
13
+ """
14
+ And a file named "formulas/basic_formula.rb" with:
15
+ """
16
+ module Formulas
17
+ class BasicFormula < Formula
18
+ def open(url)
19
+ @driver.get url
20
+ end
21
+ end
22
+ end
23
+ """
24
+ And a file named "formulas/chemist_formula.rb" with:
25
+ """
26
+ require 'chemistrykit/formula/chemist_aware'
27
+
28
+ module Formulas
29
+ class ChemistFormula < Formula
30
+ include ChemistryKit::Formula::ChemistAware
31
+ def open(url)
32
+ @driver.get url
33
+ end
34
+
35
+ def search
36
+ search_box = find id: 'gbqfq'
37
+ search_box.send_keys chemist.type
38
+ search_box.send_keys :enter
39
+ end
40
+
41
+ def search_results_found?
42
+ wait_for(5) { displayed? id: 'search' }
43
+ search_results = find id: 'search'
44
+ search_results.text.include?(chemist.type)
45
+ end
46
+ end
47
+ end
48
+ """
49
+ And a file named "formulas/lib/formula.rb" with:
50
+ """
51
+ module Formulas
52
+ class Formula < ChemistryKit::Formula::Base
53
+
54
+ def open(url)
55
+ @driver.get url
56
+ end
57
+
58
+ def find(locator)
59
+ @driver.find_element locator
60
+ end
61
+
62
+ def displayed?(locator)
63
+ begin
64
+ find(locator).displayed?
65
+ rescue
66
+ false
67
+ end
68
+ end
69
+
70
+ def wait_for(seconds=2)
71
+ Selenium::WebDriver::Wait.new(:timeout => seconds).until { yield }
72
+ end
73
+ end
74
+ end
75
+ """
76
+
77
+ Scenario: Simple formula loading with formula_lab
78
+ Given a file named "beakers/chemist_beaker.rb" with:
79
+ """
80
+ describe "Chemist Beaker", :depth => 'shallow' do
81
+ let(:basic) { @formula_lab.mix('basic_formula') }
82
+
83
+ it "loads an external web page" do
84
+ basic.open "http://www.google.com"
85
+ end
86
+ end
87
+ """
88
+ When I run `ckit brew`
89
+ Then the stdout should contain "1 example, 0 failures"
90
+
91
+ Scenario: Chemist formula loading with formula_lab
92
+ Given a file named "beakers/chemist_beaker.rb" with:
93
+ """
94
+ describe "Chemist Beaker", :depth => 'shallow' do
95
+ let(:chem) { @formula_lab.using('chemist_formula').with('admin1').mix }
96
+
97
+ it "loads an external web page" do
98
+ chem.open "http://www.google.com"
99
+ chem.search
100
+ chem.search_results_found?.should eq true
101
+ end
102
+ end
103
+ """
104
+ When I run `ckit brew`
105
+ Then the stdout should contain "1 example, 0 failures"
106
+
107
+ Scenario: Loading multiple formulas in a beaker
108
+ Given a file named "beakers/chemist_beaker.rb" with:
109
+ """
110
+ describe "Chemist Beaker", :depth => 'shallow' do
111
+ let(:basic) { @formula_lab.mix('basic_formula') }
112
+ let(:chem) { @formula_lab.using('chemist_formula').with('admin1').mix }
113
+
114
+ it "loads an external web page" do
115
+ basic.open "http://www.google.com"
116
+ chem.search
117
+ chem.search_results_found?.should eq true
118
+ end
119
+ end
120
+ """
121
+ When I run `ckit brew`
122
+ Then the stdout should contain "1 example, 0 failures"
123
+
data/features/new.feature CHANGED
@@ -13,6 +13,8 @@ Feature: ckit new
13
13
  | formulas/lib |
14
14
  | formulas/lib/catalysts |
15
15
  | evidence |
16
+ | chemists |
16
17
  And the following files should exist:
17
18
  | config.yaml |
18
19
  | formulas/lib/formula.rb |
20
+ | chemists/chemists.csv |
@@ -0,0 +1,34 @@
1
+ # Encoding: utf-8
2
+
3
+ module ChemistryKit
4
+ # representation of the user object for interacting with the system under test
5
+ class Chemist
6
+
7
+ attr_reader :key, :type, :data
8
+
9
+ def initialize(key, type)
10
+ @key = key.to_s
11
+ @type = type.to_s
12
+ @data = {}
13
+ end
14
+
15
+ def data=(data)
16
+ data.each do |key, value|
17
+ send("#{key}=", value)
18
+ end
19
+ end
20
+
21
+ # allow this object to be set with arbitrary key value data
22
+ def method_missing(name, *arguments)
23
+ value = arguments[0]
24
+ name = name.to_s
25
+ if name[-1, 1] == '='
26
+ key = name[/(.+)\s?=/, 1]
27
+ @data[key.to_sym] = value unless instance_variables.include? "@#{key}".to_sym
28
+ else
29
+ @data[name.to_sym]
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,79 @@
1
+ # Encoding: utf-8
2
+
3
+ require 'csv'
4
+ require 'securerandom'
5
+ require 'chemistrykit/chemist'
6
+
7
+ module ChemistryKit
8
+ # Chemist namespace
9
+ class Chemist
10
+ module Repository
11
+ # Provides the ability to load a chemist by type identifyer from a csv file
12
+ class CsvChemistRepository
13
+
14
+ def initialize(csv_path)
15
+ @tables = []
16
+ files = csv_path.kind_of?(String) ? [csv_path] : csv_path
17
+
18
+ files.each do |file|
19
+ raise ArgumentError, 'Supplied file does not exist!' unless File.exist? file
20
+ table = CSV.read(file, { headers: true, converters: :all, header_converters: :symbol })
21
+ [:key, :type].each do |header|
22
+ unless table.headers.include?(header) || table.headers.length == 0
23
+ raise ArgumentError, "You must define a #{header.to_s} field!"
24
+ end
25
+ end
26
+ @tables.push table
27
+ end
28
+ end
29
+
30
+ # Required Method
31
+ # Load a specific chemist by the unique key
32
+ def load_chemist_by_key(key)
33
+ @tables.each do |table|
34
+ chemist_data = table.find { |row| row[:key] == key }
35
+ return make_chemist(key, chemist_data[:type], chemist_data) if chemist_data
36
+ end
37
+ raise ArgumentError, "Chemist for type \"#{key}\" not found!"
38
+ end
39
+
40
+ # Required Method
41
+ # Load the first chemist found for a given type
42
+ def load_first_chemist_of_type(type)
43
+ load_chemists_of_type(type)[0]
44
+ end
45
+
46
+ # Required Method
47
+ # Loads a chemist at random from all those found with a given type
48
+ def load_random_chemist_of_type(type)
49
+ load_chemists_of_type(type).sample
50
+ end
51
+
52
+ protected
53
+
54
+ def load_chemists_of_type(type)
55
+ chemists = []
56
+ @tables.each do |table|
57
+ chemist_data = table.select { |row| row[:type] == type }
58
+ if chemist_data
59
+ chemist_data.each do |data|
60
+ chemists << make_chemist(data[:key], type, data)
61
+ end
62
+ end
63
+ end
64
+ raise ArgumentError, "Chemist for type \"#{type}\" not found!" if chemists.empty?
65
+ chemists
66
+ end
67
+
68
+ def make_chemist(key, type, data)
69
+ chemist = Chemist.new(key, type)
70
+ data_hash = data.to_hash
71
+ data_hash.map { |index, value| value.gsub!(/{{UUID}}/, SecureRandom.uuid) }
72
+ chemist.data = data_hash
73
+ chemist
74
+ end
75
+
76
+ end
77
+ end
78
+ end
79
+ end