kameleon 0.2.0.alpha.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +247 -0
- data/lib/kameleon/dsl.rb +14 -2
- data/lib/kameleon/dsl/act/mouse.rb +36 -5
- data/lib/kameleon/dsl/context/scope.rb +30 -2
- data/lib/kameleon/dsl/verify/presence.rb +31 -0
- metadata +84 -39
- data/README.textile +0 -157
data/README.md
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
# Kameleon [chameleon]
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/cs3b/kameleon.png?branch=master)](http://travis-ci.org/cs3b/kameleon)
|
4
|
+
|
5
|
+
Kameleon is a high abstraction dsl for better writing acceptance and integrationtests using Capybara.
|
6
|
+
And "better" means: more easily to mimic how user interact with browser.
|
7
|
+
|
8
|
+
## Setup
|
9
|
+
|
10
|
+
Kameleon requires:
|
11
|
+
|
12
|
+
* rspec
|
13
|
+
* capybara
|
14
|
+
|
15
|
+
Optionally is nice to use:
|
16
|
+
|
17
|
+
* selenium-wedriver && capybara-webkit for testing together with javascript
|
18
|
+
* thin (if you want run your specs faster - add gem 'thin' to Gemfile in your app)
|
19
|
+
|
20
|
+
Gemfile
|
21
|
+
|
22
|
+
``` ruby
|
23
|
+
gem 'kameleon', '>= 0.2.0.alpha.2'
|
24
|
+
```
|
25
|
+
|
26
|
+
Before you start using Kameleon ensure that capybara is properly loaded (in your test helper file)
|
27
|
+
|
28
|
+
``` ruby
|
29
|
+
require 'kameleon/ext/rspec/all'
|
30
|
+
```
|
31
|
+
|
32
|
+
or just components you want; `kemeleon/ext/rspec/all` by default loads:
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
require 'kameleon/ext/rspec/dsl'
|
36
|
+
require 'kameleon/ext/rspec/garbage_collector'
|
37
|
+
require 'kameleon/ext/rspec/headless'
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
checkout this [slides from wrocloverb 2012](http://www.slideshare.net/cs3b/2012wrocloverbuserperspectivetestingusingruby)
|
43
|
+
|
44
|
+
### Example One [listing products > should list existing products with correct sorting ]
|
45
|
+
|
46
|
+
kameleon
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
click "Products"
|
50
|
+
within('table.index') do
|
51
|
+
see :ordered => [ "apache baseball cap",
|
52
|
+
"zomg shirt" ]
|
53
|
+
end
|
54
|
+
click "admin_products_listing_name_title"
|
55
|
+
within('table.index') do
|
56
|
+
see :ordered => [ "zomg shirt",
|
57
|
+
"apache baseball cap"]
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
vs capybara
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
click_link "Products"
|
65
|
+
within('table.index tr:nth-child(2)') { page.should have_content("apache baseball cap") }
|
66
|
+
within('table.index tr:nth-child(3)') { page.should have_content("zomg shirt") }
|
67
|
+
click_link "admin_products_listing_name_title"
|
68
|
+
within('table.index tr:nth-child(2)') { page.should have_content("zomg shirt") }
|
69
|
+
within('table.index tr:nth-child(3)') { page.should have_content("apache baseball cap") }
|
70
|
+
```
|
71
|
+
|
72
|
+
taken from: https://github.com/spree/spree/blob/master/core/spec/requests/admin/products/products_spec.rb#L22
|
73
|
+
|
74
|
+
|
75
|
+
### Example Two [should allow an admin to create a new product and variants from a prototype]
|
76
|
+
|
77
|
+
kameleon
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
click "Products",
|
81
|
+
"admin_new_product"
|
82
|
+
fill_in "Baseball Cap" => "product_name",
|
83
|
+
"B100" => "product_sku",
|
84
|
+
"100" => "product_price",
|
85
|
+
"2012/01/24" => "product_available_on",
|
86
|
+
:select => { "Size" => "Prototype" },
|
87
|
+
:check => "Large"
|
88
|
+
click "Create"
|
89
|
+
see "successfully created!"
|
90
|
+
```
|
91
|
+
|
92
|
+
vs capybara
|
93
|
+
|
94
|
+
``` ruby
|
95
|
+
click_link "Products"
|
96
|
+
click_link "admin_new_product"
|
97
|
+
fill_in "product_name", :with => "Baseball Cap"
|
98
|
+
fill_in "product_sku", :with => "B100"
|
99
|
+
fill_in "product_price", :with => "100"
|
100
|
+
fill_in "product_available_on", :with => "2012/01/24"
|
101
|
+
select "Size", :from => "Prototype"
|
102
|
+
check "Large"
|
103
|
+
click_button "Create"
|
104
|
+
page.should have_content("successfully created!")
|
105
|
+
```
|
106
|
+
|
107
|
+
taken from: https://github.com/spree/spree/blob/master/core/spec/requests/admin/products/products_spec.rb#L77
|
108
|
+
|
109
|
+
|
110
|
+
### Example Three [admin editing an adjustment > successfully > should update the adjustment]
|
111
|
+
|
112
|
+
kameleon
|
113
|
+
|
114
|
+
``` ruby
|
115
|
+
within(:row => 2).click "Edit"
|
116
|
+
fill_in 99 => "adjustment_amount",
|
117
|
+
"rebate 99" => "adjustment_label"
|
118
|
+
click "Continue"
|
119
|
+
see "successfully updated!",
|
120
|
+
"rebate 99",
|
121
|
+
"$99.00"
|
122
|
+
```
|
123
|
+
|
124
|
+
vs capybara
|
125
|
+
|
126
|
+
``` ruby
|
127
|
+
within(:css, 'table.index tr:nth-child(2)') { click_link "Edit" }
|
128
|
+
fill_in "adjustment_amount", :with => "99"
|
129
|
+
fill_in "adjustment_label", :with => "rebate 99"
|
130
|
+
click_button "Continue"
|
131
|
+
page.should have_content("successfully updated!")
|
132
|
+
page.should have_content("rebate 99")
|
133
|
+
page.should have_content("$99.00")
|
134
|
+
```
|
135
|
+
|
136
|
+
taken from: https://github.com/spree/spree/blob/master/core/spec/requests/admin/orders/adjustments_spec.rb#L48
|
137
|
+
|
138
|
+
### Example Four [payment method > should be able to list, edit, and create payment methods for an order]
|
139
|
+
|
140
|
+
kameleon
|
141
|
+
|
142
|
+
``` ruby
|
143
|
+
click "Orders"
|
144
|
+
within('table#listing_orders', :row => 1).click_link "R100"
|
145
|
+
click "Payments"
|
146
|
+
within('#payment_status').see "Payment: balance due"
|
147
|
+
within(:cell => [2, 2]).see "$39.98"
|
148
|
+
within(:cell => [2, 3]).see "Credit Card"
|
149
|
+
within(:cell => [2, 4]).see "pending"
|
150
|
+
```
|
151
|
+
vs capybara
|
152
|
+
|
153
|
+
``` ruby
|
154
|
+
visit spree.admin_path
|
155
|
+
click_link "Orders"
|
156
|
+
within('table#listing_orders tbody tr:nth-child(1)') { click_link "R100" }
|
157
|
+
click_link "Payments"
|
158
|
+
within('#payment_status') { page.should have_content("Payment: balance due") }
|
159
|
+
find('table.index tbody tr:nth-child(2) td:nth-child(2)').text.should == "$39.98"
|
160
|
+
find('table.index tbody tr:nth-child(2) td:nth-child(3)').text.should == "Credit Card"
|
161
|
+
find('table.index tbody tr:nth-child(2) td:nth-child(4)').text.should == "pending"
|
162
|
+
```
|
163
|
+
|
164
|
+
#### part II
|
165
|
+
|
166
|
+
kameleon
|
167
|
+
|
168
|
+
``` ruby
|
169
|
+
click "Void"
|
170
|
+
within('#payment_status').see "Payment: balance due"
|
171
|
+
see "Payment Updated"
|
172
|
+
within(:cell => [2,2]).see "$39.98"
|
173
|
+
within(:cell => [2,3]).see "Credit Card"
|
174
|
+
within(:cell => [2,4]).see "void"
|
175
|
+
```
|
176
|
+
|
177
|
+
vs capybara
|
178
|
+
|
179
|
+
``` ruby
|
180
|
+
click_button "Void"
|
181
|
+
within('#payment_status') { page.should have_content("Payment: balance due") }
|
182
|
+
page.should have_content("Payment Updated")
|
183
|
+
find('table.index tbody tr:nth-child(2) td:nth-child(2)').text.should == "$39.98"
|
184
|
+
find('table.index tbody tr:nth-child(2) td:nth-child(3)').text.should == "Credit Card"
|
185
|
+
find('table.index tbody tr:nth-child(2) td:nth-child(4)').text.should == "void"
|
186
|
+
```
|
187
|
+
|
188
|
+
#### part III
|
189
|
+
|
190
|
+
kameleon
|
191
|
+
|
192
|
+
``` ruby
|
193
|
+
click "New Payment"
|
194
|
+
see "New Payment"
|
195
|
+
click "Continue",
|
196
|
+
"Capture"
|
197
|
+
within('#payment_status').see "Payment: paid"
|
198
|
+
see :element => '#new_payment_section'
|
199
|
+
|
200
|
+
click "Shipments",
|
201
|
+
"New Shipment"
|
202
|
+
within('table.index', :row => 2).check "#inventory_unit"
|
203
|
+
|
204
|
+
click "Create"
|
205
|
+
see "successfully created!"
|
206
|
+
```
|
207
|
+
vs capybara
|
208
|
+
|
209
|
+
``` ruby
|
210
|
+
click_on "New Payment"
|
211
|
+
page.should have_content("New Payment")
|
212
|
+
click_button "Continue"
|
213
|
+
click_button "Capture"
|
214
|
+
within('#payment_status') { page.should have_content("Payment: paid") }
|
215
|
+
page.should_not have_css('#new_payment_section')
|
216
|
+
|
217
|
+
click_link "Shipments"
|
218
|
+
click_on "New Shipment"
|
219
|
+
within('table.index tbody tr:nth-child(2)') { check "#inventory_unit" }
|
220
|
+
click_button "Create"
|
221
|
+
page.should have_content("successfully created!")
|
222
|
+
```
|
223
|
+
|
224
|
+
taken from: https://github.com/spree/spree/blob/master/core/spec/requests/admin/orders/payments_spec.rb#L32
|
225
|
+
|
226
|
+
|
227
|
+
## Tips & Tricks
|
228
|
+
|
229
|
+
* You have access to page variable. So if you think that something cannot be accomplished by the Kameleon DSL, you can just write using RSpec matchers and page variable. Like this: `page.should have_css("li.banner_message", :count => 10)`. Of course, after you've submitted feature request to the owner of the original repository ;)
|
230
|
+
* It is handy to define a common set of areas, that user often follows navigating on the site. Here is an example
|
231
|
+
|
232
|
+
``` ruby
|
233
|
+
Kameleon::Session.defined_areas.merge!({ :menu => [:xpath, "//nav/ul"],
|
234
|
+
:main => '.main_body',
|
235
|
+
:right_column => '.col_aside',
|
236
|
+
:ordered_list => '.ordered_list',
|
237
|
+
:favourites => '.favourites_list',
|
238
|
+
:gallery_tiny => '.gallery_tiny',
|
239
|
+
:gallery_list => '.gallery_list',
|
240
|
+
:content => '.col_content',
|
241
|
+
:col_aside => '.col_aside' })
|
242
|
+
```
|
243
|
+
|
244
|
+
## Credits
|
245
|
+
* [Michał Czyż](http://selleo.com/people/michal-czyz)
|
246
|
+
* [Radosław Jędryszczak](http://selleo.com/people/radoslaw-jedryszczak)
|
247
|
+
* [Szymon Kieloch](http://selleo.com/people)
|
data/lib/kameleon/dsl.rb
CHANGED
@@ -56,8 +56,7 @@ module Kameleon
|
|
56
56
|
if block_given?
|
57
57
|
super(*parse_selector(scope).selector)
|
58
58
|
else
|
59
|
-
|
60
|
-
raise 'not impelemented'
|
59
|
+
ScopeProxy.new(self, scope)
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
@@ -94,5 +93,18 @@ module Kameleon
|
|
94
93
|
def parse_selector(scope)
|
95
94
|
Kameleon::DSL::Context::Scope.new(scope)
|
96
95
|
end
|
96
|
+
|
97
|
+
|
98
|
+
class ScopeProxy < Struct.new(:world, :scope)
|
99
|
+
|
100
|
+
Kameleon::DSL.instance_methods.each do |method|
|
101
|
+
define_method method do |args|
|
102
|
+
world.within(scope) do
|
103
|
+
world.send(method, args)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
97
109
|
end
|
98
110
|
end
|
@@ -22,11 +22,44 @@ module Kameleon
|
|
22
22
|
case param
|
23
23
|
when String
|
24
24
|
actions << Action.new(:click_on, param)
|
25
|
+
when Symbol
|
26
|
+
prepare_actions(:element => param)
|
25
27
|
when Hash
|
26
28
|
param.each_pair do |type, values|
|
27
29
|
case type
|
28
|
-
when :
|
29
|
-
|
30
|
+
when :element
|
31
|
+
actions << Action.new(:block, values) do |element|
|
32
|
+
selector = Kameleon::DSL::Context::Scope.new(element).selector
|
33
|
+
find(*selector).click
|
34
|
+
end
|
35
|
+
|
36
|
+
when :image
|
37
|
+
#! Looks like Rack Test doesn't propagate click event up - this is simply hack
|
38
|
+
# It's not perfect it would work only when "a" is parent of image (not one of ancestor)
|
39
|
+
prepend_text = "/.." if ::Capybara.current_driver == :rack_test
|
40
|
+
|
41
|
+
prepare_actions(:element => [:xpath, ".//img[@alt=\"#{values}\"]#{prepend_text}"])
|
42
|
+
|
43
|
+
when :and_accept
|
44
|
+
unless Capybara.current_driver == :rack_test
|
45
|
+
#! js hack - problem with selenium native alerts usage
|
46
|
+
#! it switch to alert window but no reaction on accept or dismiss
|
47
|
+
actions << Action.new(:block, values) do |element|
|
48
|
+
evaluate_script("window.alert = function(msg) { return true; }")
|
49
|
+
evaluate_script("window.confirm = function(msg) { return true; }")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
prepare_actions(values)
|
53
|
+
|
54
|
+
when :and_dismiss
|
55
|
+
unless Capybara.current_driver == :rack_test
|
56
|
+
actions << Action.new(:block, values) do |element|
|
57
|
+
evaluate_script("window.alert = function(msg) { return true; }")
|
58
|
+
evaluate_script("window.confirm = function(msg) { return false; }")
|
59
|
+
end
|
60
|
+
prepare_actions(values)
|
61
|
+
end
|
62
|
+
|
30
63
|
else
|
31
64
|
raise "not implemented"
|
32
65
|
end
|
@@ -54,6 +87,4 @@ module Kameleon
|
|
54
87
|
end
|
55
88
|
|
56
89
|
#! click and dismiss
|
57
|
-
#! click and accept
|
58
|
-
#! click on image
|
59
|
-
#! click on element
|
90
|
+
#! click and accept
|
@@ -44,8 +44,10 @@ module Kameleon
|
|
44
44
|
normalize(default_selector)
|
45
45
|
elsif params.size == 1
|
46
46
|
[::Capybara.default_selector] + params
|
47
|
-
|
47
|
+
elsif [:xpath, :css].include?(params.first)
|
48
48
|
params
|
49
|
+
else
|
50
|
+
JoinInToOne.new(params, self).selector
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
@@ -72,7 +74,7 @@ module Kameleon
|
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
75
|
-
def self.
|
77
|
+
def self.cell(param)
|
76
78
|
value_in_row, value_in_column = *param
|
77
79
|
if value_in_row.is_a?(Fixnum) and value_in_column.is_a?(Fixnum)
|
78
80
|
[:xpath, "//tbody/tr[#{value_in_row}]/td[#{value_in_column}]"]
|
@@ -89,6 +91,32 @@ module Kameleon
|
|
89
91
|
# return [:xpath, ".//table//th[#{position + 1}] | .//table//td[#{position + 1}]", :select_multiple]
|
90
92
|
end
|
91
93
|
end
|
94
|
+
|
95
|
+
class JoinInToOne < Struct.new(:params, :scope)
|
96
|
+
|
97
|
+
def selector
|
98
|
+
[:xpath, xpath_selector]
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def xpath_selector
|
104
|
+
([xpaths.first] + cleanup(xpaths[1..-1])).join("//")
|
105
|
+
end
|
106
|
+
|
107
|
+
def cleanup(others)
|
108
|
+
others.collect { |xpath| xpath.gsub(/^\.+/, '').gsub(/^\/+/, '') }
|
109
|
+
end
|
110
|
+
|
111
|
+
def xpaths
|
112
|
+
@xpaths ||= normalized.map { |param| param.first == :css ? ::Nokogiri::CSS.xpath_for(param.last).first : param.last }
|
113
|
+
end
|
114
|
+
|
115
|
+
# TODO detect_selector should be in module
|
116
|
+
def normalized
|
117
|
+
params.map { |param| scope.send(:detect_selector, param) }
|
118
|
+
end
|
119
|
+
end
|
92
120
|
end
|
93
121
|
end
|
94
122
|
end
|
@@ -22,6 +22,8 @@ module Kameleon
|
|
22
22
|
case param
|
23
23
|
when String
|
24
24
|
conditions << Condition.new(:have_content, param)
|
25
|
+
when Symbol
|
26
|
+
prepare_conditions(:element => param)
|
25
27
|
when Hash
|
26
28
|
param.each_pair do |type, values|
|
27
29
|
case type
|
@@ -47,6 +49,8 @@ module Kameleon
|
|
47
49
|
conditions.concat TextInput.new(nil, values).conditions
|
48
50
|
when :empty
|
49
51
|
conditions.concat EmptyInput.new(values).conditions
|
52
|
+
when :element
|
53
|
+
conditions.concat Element.new(values).conditions
|
50
54
|
else
|
51
55
|
raise "not implemented"
|
52
56
|
end
|
@@ -123,6 +127,33 @@ module Kameleon
|
|
123
127
|
end
|
124
128
|
end
|
125
129
|
|
130
|
+
class Element
|
131
|
+
attr_reader :conditions, :expression
|
132
|
+
|
133
|
+
def initialize(expression)
|
134
|
+
@expression = expression
|
135
|
+
@conditions = [condition]
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def condition
|
141
|
+
Condition.new(have_selector_method, selector_expression)
|
142
|
+
end
|
143
|
+
|
144
|
+
def have_selector_method
|
145
|
+
"have_#{selector.first}".to_sym
|
146
|
+
end
|
147
|
+
|
148
|
+
def selector
|
149
|
+
@selector ||= Kameleon::DSL::Context::Scope.new(expression).selector
|
150
|
+
end
|
151
|
+
|
152
|
+
def selector_expression
|
153
|
+
selector.last
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
126
157
|
class Quantity
|
127
158
|
attr_reader :conditions
|
128
159
|
attr_reader :quantity
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kameleon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Michał Czyż [cs3b]
|
@@ -11,11 +11,11 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-03
|
14
|
+
date: 2012-05-03 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rspec
|
18
|
-
requirement:
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
21
|
- - ! '>='
|
@@ -23,10 +23,15 @@ dependencies:
|
|
23
23
|
version: '0'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
|
-
version_requirements:
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
27
32
|
- !ruby/object:Gem::Dependency
|
28
33
|
name: capybara
|
29
|
-
requirement:
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
30
35
|
none: false
|
31
36
|
requirements:
|
32
37
|
- - ! '>='
|
@@ -34,10 +39,15 @@ dependencies:
|
|
34
39
|
version: '0'
|
35
40
|
type: :runtime
|
36
41
|
prerelease: false
|
37
|
-
version_requirements:
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
38
48
|
- !ruby/object:Gem::Dependency
|
39
49
|
name: headless
|
40
|
-
requirement:
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
41
51
|
none: false
|
42
52
|
requirements:
|
43
53
|
- - ! '>='
|
@@ -45,10 +55,15 @@ dependencies:
|
|
45
55
|
version: '0'
|
46
56
|
type: :runtime
|
47
57
|
prerelease: false
|
48
|
-
version_requirements:
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
49
64
|
- !ruby/object:Gem::Dependency
|
50
65
|
name: rspec
|
51
|
-
requirement:
|
66
|
+
requirement: !ruby/object:Gem::Requirement
|
52
67
|
none: false
|
53
68
|
requirements:
|
54
69
|
- - ! '>='
|
@@ -56,10 +71,15 @@ dependencies:
|
|
56
71
|
version: '0'
|
57
72
|
type: :development
|
58
73
|
prerelease: false
|
59
|
-
version_requirements:
|
74
|
+
version_requirements: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
60
80
|
- !ruby/object:Gem::Dependency
|
61
81
|
name: capybara
|
62
|
-
requirement:
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
63
83
|
none: false
|
64
84
|
requirements:
|
65
85
|
- - ! '>='
|
@@ -67,10 +87,15 @@ dependencies:
|
|
67
87
|
version: '0'
|
68
88
|
type: :development
|
69
89
|
prerelease: false
|
70
|
-
version_requirements:
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
71
96
|
- !ruby/object:Gem::Dependency
|
72
97
|
name: headless
|
73
|
-
requirement:
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
74
99
|
none: false
|
75
100
|
requirements:
|
76
101
|
- - ! '>='
|
@@ -78,10 +103,15 @@ dependencies:
|
|
78
103
|
version: '0'
|
79
104
|
type: :development
|
80
105
|
prerelease: false
|
81
|
-
version_requirements:
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
82
112
|
- !ruby/object:Gem::Dependency
|
83
113
|
name: guard-rspec
|
84
|
-
requirement:
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
85
115
|
none: false
|
86
116
|
requirements:
|
87
117
|
- - ! '>='
|
@@ -89,10 +119,15 @@ dependencies:
|
|
89
119
|
version: '0'
|
90
120
|
type: :development
|
91
121
|
prerelease: false
|
92
|
-
version_requirements:
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
93
128
|
- !ruby/object:Gem::Dependency
|
94
129
|
name: selenium-webdriver
|
95
|
-
requirement:
|
130
|
+
requirement: !ruby/object:Gem::Requirement
|
96
131
|
none: false
|
97
132
|
requirements:
|
98
133
|
- - ! '>='
|
@@ -100,10 +135,15 @@ dependencies:
|
|
100
135
|
version: '0'
|
101
136
|
type: :development
|
102
137
|
prerelease: false
|
103
|
-
version_requirements:
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
104
144
|
- !ruby/object:Gem::Dependency
|
105
145
|
name: capybara-webkit
|
106
|
-
requirement:
|
146
|
+
requirement: !ruby/object:Gem::Requirement
|
107
147
|
none: false
|
108
148
|
requirements:
|
109
149
|
- - ! '>='
|
@@ -111,7 +151,12 @@ dependencies:
|
|
111
151
|
version: '0'
|
112
152
|
type: :development
|
113
153
|
prerelease: false
|
114
|
-
version_requirements:
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ! '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
115
160
|
description: high abstraction dsl for end user perspective tests, kameleon - it's
|
116
161
|
a polish word for chameleon
|
117
162
|
email: michalczyz@gmail.com
|
@@ -119,30 +164,30 @@ executables: []
|
|
119
164
|
extensions: []
|
120
165
|
extra_rdoc_files: []
|
121
166
|
files:
|
122
|
-
- lib/kameleon
|
167
|
+
- lib/kameleon.rb
|
123
168
|
- lib/kameleon/utils/debug_console.rb
|
169
|
+
- lib/kameleon/utils/configuration.rb
|
170
|
+
- lib/kameleon/session.rb
|
171
|
+
- lib/kameleon/dsl.rb
|
172
|
+
- lib/kameleon/dsl/verify/presence.rb
|
173
|
+
- lib/kameleon/dsl/verify/absence.rb
|
174
|
+
- lib/kameleon/dsl/utils/debug.rb
|
124
175
|
- lib/kameleon/dsl/act/form.rb
|
125
176
|
- lib/kameleon/dsl/act/mouse.rb
|
126
177
|
- lib/kameleon/dsl/context/scope.rb
|
127
|
-
- lib/kameleon/dsl/utils/debug.rb
|
128
|
-
- lib/kameleon/dsl/verify/absence.rb
|
129
|
-
- lib/kameleon/dsl/verify/presence.rb
|
130
|
-
- lib/kameleon/dsl.rb
|
131
|
-
- lib/kameleon/ext/active_record/shared_single_connection.rb
|
132
|
-
- lib/kameleon/ext/capybara/server.rb
|
133
178
|
- lib/kameleon/ext/capybara/session_pool.rb
|
134
|
-
- lib/kameleon/ext/
|
135
|
-
- lib/kameleon/ext/
|
179
|
+
- lib/kameleon/ext/capybara/server.rb
|
180
|
+
- lib/kameleon/ext/session/devise.rb
|
181
|
+
- lib/kameleon/ext/ruby/deferred_garbage_collector.rb
|
136
182
|
- lib/kameleon/ext/rspec/garbage_collector.rb
|
137
|
-
- lib/kameleon/ext/rspec/
|
183
|
+
- lib/kameleon/ext/rspec/dsl.rb
|
138
184
|
- lib/kameleon/ext/rspec/pool.rb
|
139
185
|
- lib/kameleon/ext/rspec/transactional_examples.rb
|
140
|
-
- lib/kameleon/ext/
|
141
|
-
- lib/kameleon/ext/
|
142
|
-
- lib/kameleon/
|
143
|
-
- lib/kameleon.rb
|
186
|
+
- lib/kameleon/ext/rspec/headless.rb
|
187
|
+
- lib/kameleon/ext/rspec/all.rb
|
188
|
+
- lib/kameleon/ext/active_record/shared_single_connection.rb
|
144
189
|
- LICENCE
|
145
|
-
- README.
|
190
|
+
- README.md
|
146
191
|
homepage: https://github.com/cs3b/kameleon
|
147
192
|
licenses:
|
148
193
|
- MIT
|
@@ -159,12 +204,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
159
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
205
|
none: false
|
161
206
|
requirements:
|
162
|
-
- - ! '
|
207
|
+
- - ! '>='
|
163
208
|
- !ruby/object:Gem::Version
|
164
|
-
version:
|
209
|
+
version: '0'
|
165
210
|
requirements: []
|
166
211
|
rubyforge_project:
|
167
|
-
rubygems_version: 1.8.
|
212
|
+
rubygems_version: 1.8.24
|
168
213
|
signing_key:
|
169
214
|
specification_version: 3
|
170
215
|
summary: high abstraction dsl for end user perspective tests
|
data/README.textile
DELETED
@@ -1,157 +0,0 @@
|
|
1
|
-
h1. Kameleon [chameleon]
|
2
|
-
|
3
|
-
"!https://secure.travis-ci.org/cs3b/kameleon.png?branch=master!":http://travis-ci.org/cs3b/kameleon
|
4
|
-
|
5
|
-
Kameleon is a high abstraction dsl for better writing acceptance and integrationtests using Capybara.
|
6
|
-
And "better" means: more easily to mimic how user interact with browser.
|
7
|
-
|
8
|
-
h2. Setup:
|
9
|
-
|
10
|
-
Kameleon requires:
|
11
|
-
* rspec
|
12
|
-
* capybara
|
13
|
-
|
14
|
-
Optional is nice to use
|
15
|
-
* devise ( Kameleon uses paths suplied by devise. So if you're using some other authentication solution, you might want to overwrite it )
|
16
|
-
* selenium-wedriver && capybara-webkit for testing full site
|
17
|
-
* thin (if you want run your specs faster - add gem 'thin' to Gemfile in your app)
|
18
|
-
|
19
|
-
Gemfile
|
20
|
-
|
21
|
-
<pre>
|
22
|
-
gem 'kameleon', '>= 0.2.0'
|
23
|
-
</pre>
|
24
|
-
|
25
|
-
Before you start using Kameleon ensure that capybara is properly loaded (in your test helper file)
|
26
|
-
|
27
|
-
<pre>
|
28
|
-
require 'kameleon/ext/rspec/all'
|
29
|
-
</pre>
|
30
|
-
|
31
|
-
or just components you want, kemeleon/ext/rspec/all by default loads:
|
32
|
-
|
33
|
-
<pre>
|
34
|
-
require 'kameleon/ext/rspec/dsl'
|
35
|
-
require 'kameleon/ext/rspec/garbage_collector'
|
36
|
-
require 'kameleon/ext/rspec/headless'
|
37
|
-
</pre>
|
38
|
-
|
39
|
-
h2. Usage:
|
40
|
-
|
41
|
-
below few examples for more please check spec/integration tests suite
|
42
|
-
|
43
|
-
bc. background do
|
44
|
-
@admin = Factory.create(:user)
|
45
|
-
end
|
46
|
-
|
47
|
-
scenario "opening list of news", :status => 'done' do
|
48
|
-
click "homepage",
|
49
|
-
"community",
|
50
|
-
"news"
|
51
|
-
|
52
|
-
see "new medicine developed last weekend"
|
53
|
-
within(:row => 'some cell text') do
|
54
|
-
see 'see all values in row that contains that text'
|
55
|
-
end
|
56
|
-
see :ordered => 'Z', 'X', 'Y'
|
57
|
-
click :and_confirm => "Cancel", :and_dismiss => "Cancel"
|
58
|
-
end
|
59
|
-
|
60
|
-
Another example
|
61
|
-
|
62
|
-
bc. feature "Products", :driver => :selenium do
|
63
|
-
background do
|
64
|
-
@admin = Factory.create(:user)
|
65
|
-
create_session(:admin)
|
66
|
-
visit spree.admin_path
|
67
|
-
end
|
68
|
-
|
69
|
-
context "listing products" do
|
70
|
-
scenario "products sorting" do
|
71
|
-
Factory(:product, :name => 'apache baseball cap',
|
72
|
-
:available_on => '2011-01-06 18:21:13:',
|
73
|
-
:count_on_hand => '0')
|
74
|
-
Factory(:product, :name => 'zomg shirt',
|
75
|
-
:available_on => '2125-01-06 18:21:13',
|
76
|
-
:count_on_hand => '5')
|
77
|
-
|
78
|
-
act_as(:admin) do
|
79
|
-
click "Products"
|
80
|
-
see :ordered => ["apache baseball cap", "zomg shirt"]
|
81
|
-
|
82
|
-
click "admin_products_listing_name_title"
|
83
|
-
see :ordered => ["zomg shirt", "apache baseball cap"]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
And more complex example, with two user sessions:
|
89
|
-
|
90
|
-
bc. scenario "admin adds a product and user buys it", :status => "done", :driver => :selenium do
|
91
|
-
create_session(:admin)
|
92
|
-
@admin = Factory.create(:admin_user)
|
93
|
-
create_session(:user)
|
94
|
-
|
95
|
-
act_as(:admin) do
|
96
|
-
visit spree.admin_path
|
97
|
-
click "Products", "admin_new_product"
|
98
|
-
within('#new_product') do
|
99
|
-
see "SKU"
|
100
|
-
end
|
101
|
-
fill_in "product_name" => "Baseball Cap",
|
102
|
-
"product_sku" => "B100",
|
103
|
-
"product_price"=> "100",
|
104
|
-
"product_available_on"=> "2012/01/24"
|
105
|
-
|
106
|
-
click "Create"
|
107
|
-
see "successfully created!"
|
108
|
-
|
109
|
-
fill_in "product_on_hand" => "100"
|
110
|
-
click "Update"
|
111
|
-
see "successfully updated!"
|
112
|
-
end
|
113
|
-
|
114
|
-
act_as(:user) do
|
115
|
-
visit spree.root_path
|
116
|
-
click "Baseball Cap", "add-to-cart-button", "Checkout"
|
117
|
-
within("span.out-of-stock") do
|
118
|
-
see "Baseball Cap added to your cart"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
|
124
|
-
h2. Tips & Tricks:
|
125
|
-
* You have access to page variable. So if you think that something cannot be accomplished by the Kameleon DSL, you can just write using RSpec matchers and page variable. Like this: <pre> page.should have_css("li.banner_message", :count => 10) </pre> Of course, after you've submitted feature request to the owner of the original repository ;)
|
126
|
-
* It is handy to define a common set of areas, that user often follows navigating on the site. Here is an example:
|
127
|
-
<pre> Kameleon::Session.defined_areas.merge!({
|
128
|
-
:menu => [:xpath, "//nav/ul"],
|
129
|
-
:main => '.main_body',
|
130
|
-
:right_column => '.col_aside',
|
131
|
-
:ordered_list => '.ordered_list',
|
132
|
-
:favourites => '.favourites_list',
|
133
|
-
:gallery_tiny => '.gallery_tiny',
|
134
|
-
:gallery_list => '.gallery_list',
|
135
|
-
:content => '.col_content',
|
136
|
-
:col_aside => '.col_aside'
|
137
|
-
})
|
138
|
-
|
139
|
-
</pre>
|
140
|
-
|
141
|
-
soon we will merge with new capybara approach for that
|
142
|
-
|
143
|
-
h2. Tips & Tricks:
|
144
|
-
|
145
|
-
h3. Session pooling
|
146
|
-
|
147
|
-
Kameleon has useful technique of session pooling implemented, that can speed up test suite greatly.
|
148
|
-
In order to enable it, you need to pass this to the RSpec.configure block, in spec_helper.rb:
|
149
|
-
|
150
|
-
bc. config.after(:each) do
|
151
|
-
::SessionPool.release_all
|
152
|
-
end
|
153
|
-
|
154
|
-
h2. Credits:
|
155
|
-
* <a href="http://selleo.com/people/michal-czyz">Michał Czyż</a>
|
156
|
-
* <a href="http://selleo.com/people/radoslaw-jedryszczak">Radosław Jędryszczak</a>
|
157
|
-
* <a href="http://selleo.com/people">Szymon Kieloch</a>
|