kameleon 0.0.9 → 0.2.0.alpha.2

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.
Files changed (81) hide show
  1. data/LICENCE +1 -1
  2. data/README.textile +157 -0
  3. data/lib/kameleon.rb +3 -18
  4. data/lib/kameleon/dsl.rb +98 -0
  5. data/lib/kameleon/dsl/act/form.rb +106 -0
  6. data/lib/kameleon/dsl/act/mouse.rb +59 -0
  7. data/lib/kameleon/dsl/context/scope.rb +95 -0
  8. data/lib/kameleon/dsl/utils/debug.rb +10 -0
  9. data/lib/kameleon/dsl/verify/absence.rb +151 -0
  10. data/lib/kameleon/dsl/verify/presence.rb +327 -0
  11. data/lib/kameleon/ext/active_record/shared_single_connection.rb +42 -0
  12. data/lib/kameleon/ext/capybara/server.rb +21 -0
  13. data/lib/kameleon/ext/capybara/session_pool.rb +63 -0
  14. data/lib/kameleon/ext/rspec/all.rb +4 -0
  15. data/lib/kameleon/ext/rspec/dsl.rb +39 -0
  16. data/lib/kameleon/ext/rspec/garbage_collector.rb +11 -0
  17. data/lib/kameleon/ext/rspec/headless.rb +12 -0
  18. data/lib/kameleon/ext/rspec/pool.rb +7 -0
  19. data/lib/kameleon/ext/rspec/transactional_examples.rb +10 -0
  20. data/{spec/support/deferred_garbage_collection.rb → lib/kameleon/ext/ruby/deferred_garbage_collector.rb} +0 -0
  21. data/lib/kameleon/ext/session/devise.rb +28 -0
  22. data/lib/kameleon/session.rb +23 -0
  23. data/lib/kameleon/utils/configuration.rb +5 -5
  24. data/lib/kameleon/utils/debug_console.rb +31 -0
  25. metadata +73 -77
  26. data/lib/kameleon/dsl/act.rb +0 -107
  27. data/lib/kameleon/dsl/see.rb +0 -155
  28. data/lib/kameleon/session/capybara.rb +0 -33
  29. data/lib/kameleon/session/server.rb +0 -21
  30. data/lib/kameleon/session/session_pool.rb +0 -45
  31. data/lib/kameleon/user/abstract.rb +0 -142
  32. data/lib/kameleon/user/base.rb +0 -36
  33. data/lib/kameleon/user/guest.rb +0 -10
  34. data/lib/kameleon/utils/helpers.rb +0 -11
  35. data/lib/patch/capybara_selenium_driver.rb +0 -7
  36. data/spec/sample_rack_app/hey.rb +0 -24
  37. data/spec/spec_helper.rb +0 -41
  38. data/spec/unit/act/click_spec.rb +0 -52
  39. data/spec/unit/act/fill_in/attach_file_spec.rb +0 -36
  40. data/spec/unit/act/fill_in/checkbox_spec.rb +0 -87
  41. data/spec/unit/act/fill_in/date_ranges_spec.rb +0 -24
  42. data/spec/unit/act/fill_in/multiple_select_spec.rb +0 -42
  43. data/spec/unit/act/fill_in/radio_button_spec.rb +0 -35
  44. data/spec/unit/act/fill_in/select_spec.rb +0 -35
  45. data/spec/unit/act/fill_in/text_area_spec.rb +0 -52
  46. data/spec/unit/act/fill_in/text_input_spec.rb +0 -52
  47. data/spec/unit/act/on_hover_spec.rb +0 -34
  48. data/spec/unit/dsl/not_see/form_elements/fields/empty_spec.rb +0 -38
  49. data/spec/unit/dsl/not_see/form_elements/fields/readonly_spec.rb +0 -38
  50. data/spec/unit/dsl/not_see/form_elements/fields_spec.rb +0 -24
  51. data/spec/unit/dsl/not_see/form_elements/textareas_spec.rb +0 -25
  52. data/spec/unit/dsl/not_see/form_elements/texts_spec.rb +0 -26
  53. data/spec/unit/dsl/not_see/in_scopes_spec.rb +0 -63
  54. data/spec/unit/dsl/not_see/special_elements/buttons_spec.rb +0 -24
  55. data/spec/unit/dsl/not_see/special_elements/error_message_for_spec.rb +0 -24
  56. data/spec/unit/dsl/not_see/special_elements/images_spec.rb +0 -24
  57. data/spec/unit/dsl/not_see/special_elements/links_spec.rb +0 -46
  58. data/spec/unit/dsl/not_see/special_elements/ordered_texts_spec.rb +0 -21
  59. data/spec/unit/dsl/not_see/special_selectors_spec.rb +0 -39
  60. data/spec/unit/dsl/not_see/texts_spec.rb +0 -24
  61. data/spec/unit/dsl/see/counted_elements_spec.rb +0 -26
  62. data/spec/unit/dsl/see/form_elements/checkboxes_spec.rb +0 -45
  63. data/spec/unit/dsl/see/form_elements/fields/disabled_spec.rb +0 -30
  64. data/spec/unit/dsl/see/form_elements/fields/empty_spec.rb +0 -28
  65. data/spec/unit/dsl/see/form_elements/fields/readonly_spec.rb +0 -38
  66. data/spec/unit/dsl/see/form_elements/fields_spec.rb +0 -28
  67. data/spec/unit/dsl/see/form_elements/multiple_selects_spec.rb +0 -47
  68. data/spec/unit/dsl/see/form_elements/radio_buttons_spec.rb +0 -35
  69. data/spec/unit/dsl/see/form_elements/selects_spec.rb +0 -40
  70. data/spec/unit/dsl/see/form_elements/textareas_spec.rb +0 -29
  71. data/spec/unit/dsl/see/form_elements/texts_spec.rb +0 -29
  72. data/spec/unit/dsl/see/in_scopes_spec.rb +0 -85
  73. data/spec/unit/dsl/see/special_elements/buttons_spec.rb +0 -28
  74. data/spec/unit/dsl/see/special_elements/error_message_for_spec.rb +0 -24
  75. data/spec/unit/dsl/see/special_elements/images_spec.rb +0 -28
  76. data/spec/unit/dsl/see/special_elements/links_spec.rb +0 -55
  77. data/spec/unit/dsl/see/special_elements/ordered_texts_spec.rb +0 -21
  78. data/spec/unit/dsl/see/special_selectors_spec.rb +0 -57
  79. data/spec/unit/dsl/see/texts_spec.rb +0 -24
  80. data/spec/unit/guest_spec.rb +0 -46
  81. data/spec/unit/user_base_spec.rb +0 -13
data/LICENCE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Michał Czyż <http://linkd.in/michalczyz> @cs3b
1
+ Copyright (c) 2011..2012 Michał Czyż <http://linkd.in/michalczyz> @cs3b
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,157 @@
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>
@@ -1,22 +1,7 @@
1
1
  require 'kameleon/utils/configuration'
2
- require 'kameleon/utils/helpers'
3
-
4
- require 'kameleon/session/capybara'
5
- require 'kameleon/session/server'
6
- require 'kameleon/session/session_pool'
7
- require 'kameleon/dsl/see'
8
- require 'kameleon/dsl/act'
9
-
10
- require 'kameleon/user/abstract'
11
- require 'kameleon/user/base'
12
- require 'kameleon/user/guest'
13
-
14
2
 
15
3
  module Kameleon
16
- extend Utils::Configuration
17
- end
18
-
19
- # Set up default configuration for Kameleon
20
- Kameleon.configure do |config|
21
- config.default_file_path = 'spec/dummy'
4
+ def self.configure(&block)
5
+ yield(Kameleon::Utils::Configuration)
6
+ end
22
7
  end
@@ -0,0 +1,98 @@
1
+ require 'kameleon/dsl/verify/presence'
2
+ require 'kameleon/dsl/verify/absence'
3
+
4
+ require 'kameleon/dsl/act/mouse'
5
+ require 'kameleon/dsl/act/form'
6
+
7
+ require 'kameleon/ext/capybara/session_pool'
8
+ require 'kameleon/session'
9
+
10
+ require 'kameleon/dsl/context/scope'
11
+
12
+ module Kameleon
13
+ module DSL
14
+
15
+ def see(*args)
16
+ Kameleon::DSL::Verify::Presence.new(*args).tap do |presence|
17
+ presence.conditions.each do |condition|
18
+ if block = condition.block
19
+ instance_exec(*condition.params, &block)
20
+ else
21
+ page.should send(condition.method, *condition.params)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ #! hmn - very similar to see
28
+ def not_see(*args)
29
+ Kameleon::DSL::Verify::Absence.new(*args).tap do |absence|
30
+ absence.conditions.each do |condition|
31
+ if condition.block
32
+ instance_eval(condition.block)
33
+ else
34
+ page.should send(condition.method, *condition.params)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def create_session(name = :default)
41
+ Kameleon::Session.new(name).tap { |ks| act_as(ks.name) }
42
+ end
43
+
44
+ def act_as(name)
45
+ if block_given?
46
+ using_session(name) do
47
+ yield
48
+ end
49
+ else
50
+ Capybara.session_name = name
51
+ end
52
+ self
53
+ end
54
+
55
+ def within(*scope, &block)
56
+ if block_given?
57
+ super(*parse_selector(scope).selector)
58
+ else
59
+ #! we need proxy object to make it working
60
+ raise 'not impelemented'
61
+ end
62
+ end
63
+
64
+ def click(*args)
65
+ Kameleon::DSL::Act::Mouse::Click.new(*args).tap do |click|
66
+ click.actions.each do |action|
67
+ if block = action.block
68
+ instance_exec(*action.params, &block)
69
+ else
70
+ page.send(action.method, *action.params)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def fill_in(args)
77
+ Kameleon::DSL::Act::Form.new(args).tap do |form|
78
+ form.actions.each do |action|
79
+ if block = action.block
80
+ instance_exec(*action.params, &block)
81
+ else
82
+ page.send(action.method, *action.params)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def refresh_page
89
+ visit current_url
90
+ end
91
+
92
+ private
93
+
94
+ def parse_selector(scope)
95
+ Kameleon::DSL::Context::Scope.new(scope)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,106 @@
1
+ module Kameleon
2
+ module DSL
3
+ module Act
4
+ class Form
5
+ attr_reader :params
6
+ attr_reader :actions
7
+
8
+ def initialize(params)
9
+ raise "not supported" unless params.kind_of?(Hash)
10
+ @params = params
11
+ @actions = []
12
+ parse_actions
13
+ end
14
+
15
+ private
16
+
17
+ def parse_actions
18
+ prepare_actions(params)
19
+ end
20
+
21
+ def prepare_actions(param)
22
+ param.each_pair do |value, identifier|
23
+ case identifier
24
+ when Array
25
+ identifier.each { |identifier| prepare_actions(value => identifier) }
26
+ else
27
+ case value
28
+ when Fixnum, String
29
+ actions << Action.new(:fill_in, identifier, :with => value)
30
+ when :check, :choose, :uncheck
31
+ actions << Action.new(value, identifier)
32
+ when :select, :unselect
33
+ actions.concat SelectTag.new(value, identifier).actions
34
+ when :attach
35
+ actions.concat AttachFileTag.new(identifier).actions
36
+ else
37
+ raise "not implemented"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class SelectTag
45
+ attr_reader :actions, :action
46
+
47
+ def initialize(action, params)
48
+ raise "not implemented" unless params.kind_of?(Hash)
49
+ @action = action
50
+ @actions = []
51
+ parse_params(params)
52
+ end
53
+
54
+ def parse_params(params)
55
+ params.each_pair do |option, id|
56
+ if option.kind_of?(Array)
57
+ option.each do |o|
58
+ parse_params(o => id)
59
+ end
60
+ else
61
+ actions << Action.new(action, option, :from => id)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ class AttachFileTag
68
+ attr_reader :actions
69
+
70
+ def initialize(params)
71
+ raise "not implemented" unless params.kind_of?(Hash)
72
+ @actions = []
73
+ parse_params(params)
74
+ end
75
+
76
+ private
77
+
78
+ def parse_params(params)
79
+ params.each_pair do |filename, identifier|
80
+ if identifier.kind_of?(Array)
81
+ identifier.each do |id|
82
+ parse_params(filename => id)
83
+ end
84
+ else
85
+ actions << Action.new(:attach_file, identifier, full_path(filename))
86
+ end
87
+ end
88
+ end
89
+
90
+ def full_path(filename)
91
+ if File.file?(filename)
92
+ filename
93
+ else
94
+ prepare_full_path(filename)
95
+ end
96
+ end
97
+
98
+ def prepare_full_path(filename)
99
+ File.join(Kameleon::Utils::Configuration.assets_dir, filename)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ #! in future should we check in RackTest - does field is editable (detect readonly, disabled attributes) ?
@@ -0,0 +1,59 @@
1
+ module Kameleon
2
+ module DSL
3
+ module Act
4
+ module Mouse
5
+ class Click
6
+ attr_reader :params
7
+ attr_reader :actions
8
+
9
+ def initialize(*params)
10
+ @params = params
11
+ @actions = []
12
+ parse_actions
13
+ end
14
+
15
+ private
16
+
17
+ def parse_actions
18
+ prepare_actions(params)
19
+ end
20
+
21
+ def prepare_actions(param)
22
+ case param
23
+ when String
24
+ actions << Action.new(:click_on, param)
25
+ when Hash
26
+ param.each_pair do |type, values|
27
+ case type
28
+ when :non_implemented
29
+ raise "not implemented"
30
+ else
31
+ raise "not implemented"
32
+ end
33
+ end
34
+ when Array
35
+ params.each { |parameter| prepare_actions(parameter) }
36
+ else
37
+ raise "not implemented"
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ class Action
44
+ attr_accessor :method, :params, :block
45
+
46
+ def initialize(method, *params, &block)
47
+ @method = method
48
+ @params = params
49
+ @block = block
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ #! click and dismiss
57
+ #! click and accept
58
+ #! click on image
59
+ #! click on element