page_magic 1.2.8 → 2.0.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 (101) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +23 -4
  3. data/.simplecov +5 -3
  4. data/.zsh_config +6 -0
  5. data/Dockerfile +11 -0
  6. data/Gemfile +13 -13
  7. data/Gemfile.lock +136 -148
  8. data/Makefile +17 -0
  9. data/README.md +26 -5
  10. data/Rakefile +12 -2
  11. data/VERSION +1 -1
  12. data/circle.yml +3 -1
  13. data/lib/active_support/core_ext/object/to_query.rb +84 -0
  14. data/lib/page_magic.rb +31 -24
  15. data/lib/page_magic/class_methods.rb +5 -2
  16. data/lib/page_magic/comparator.rb +37 -0
  17. data/lib/page_magic/comparator/fuzzy.rb +23 -0
  18. data/lib/page_magic/comparator/literal.rb +22 -0
  19. data/lib/page_magic/comparator/null.rb +26 -0
  20. data/lib/page_magic/comparator/parameter_map.rb +52 -0
  21. data/lib/page_magic/driver.rb +3 -0
  22. data/lib/page_magic/drivers.rb +6 -5
  23. data/lib/page_magic/drivers/poltergeist.rb +2 -0
  24. data/lib/page_magic/drivers/rack_test.rb +3 -1
  25. data/lib/page_magic/drivers/selenium.rb +4 -2
  26. data/lib/page_magic/element.rb +35 -15
  27. data/lib/page_magic/element/locators.rb +8 -5
  28. data/lib/page_magic/element/not_found.rb +38 -0
  29. data/lib/page_magic/element/query.rb +21 -20
  30. data/lib/page_magic/element/query/multiple_results.rb +21 -0
  31. data/lib/page_magic/element/query/prefetched_result.rb +26 -0
  32. data/lib/page_magic/element/query/single_result.rb +20 -0
  33. data/lib/page_magic/element/selector.rb +41 -16
  34. data/lib/page_magic/element/selector/methods.rb +18 -0
  35. data/lib/page_magic/element/selector/model.rb +21 -0
  36. data/lib/page_magic/element_context.rb +7 -21
  37. data/lib/page_magic/element_definition_builder.rb +20 -24
  38. data/lib/page_magic/elements.rb +65 -69
  39. data/lib/page_magic/elements/config.rb +103 -0
  40. data/lib/page_magic/elements/inheritance_hooks.rb +15 -0
  41. data/lib/page_magic/elements/types.rb +25 -0
  42. data/lib/page_magic/exceptions.rb +6 -1
  43. data/lib/page_magic/instance_methods.rb +8 -3
  44. data/lib/page_magic/mapping.rb +79 -0
  45. data/lib/page_magic/session.rb +15 -35
  46. data/lib/page_magic/session_methods.rb +3 -1
  47. data/lib/page_magic/transitions.rb +49 -0
  48. data/lib/page_magic/utils/string.rb +18 -0
  49. data/lib/page_magic/utils/url.rb +20 -0
  50. data/lib/page_magic/wait_methods.rb +3 -0
  51. data/lib/page_magic/watcher.rb +12 -17
  52. data/lib/page_magic/watchers.rb +31 -15
  53. data/page_magic.gemspec +15 -11
  54. data/spec/lib/active_support/core_ext/object/to_query_test.rb +78 -0
  55. data/spec/page_magic/class_methods_spec.rb +66 -37
  56. data/spec/page_magic/comparator/fuzzy_spec.rb +44 -0
  57. data/spec/page_magic/comparator/literal_spec.rb +41 -0
  58. data/spec/page_magic/comparator/null_spec.rb +35 -0
  59. data/spec/page_magic/comparator/parameter_map_spec.rb +75 -0
  60. data/spec/page_magic/driver_spec.rb +26 -28
  61. data/spec/page_magic/drivers/poltergeist_spec.rb +6 -7
  62. data/spec/page_magic/drivers/rack_test_spec.rb +6 -9
  63. data/spec/page_magic/drivers/selenium_spec.rb +11 -12
  64. data/spec/page_magic/drivers_spec.rb +38 -29
  65. data/spec/page_magic/element/locators_spec.rb +28 -25
  66. data/spec/page_magic/element/not_found_spec.rb +24 -0
  67. data/spec/page_magic/element/query/multiple_results_spec.rb +14 -0
  68. data/spec/page_magic/element/query/single_result_spec.rb +21 -0
  69. data/spec/page_magic/element/query_spec.rb +26 -45
  70. data/spec/page_magic/element/selector_spec.rb +120 -110
  71. data/spec/page_magic/element_context_spec.rb +47 -87
  72. data/spec/page_magic/element_definition_builder_spec.rb +14 -71
  73. data/spec/page_magic/element_spec.rb +256 -0
  74. data/spec/page_magic/elements/config_spec.rb +203 -0
  75. data/spec/page_magic/elements_spec.rb +90 -134
  76. data/spec/page_magic/instance_methods_spec.rb +65 -63
  77. data/spec/page_magic/mapping_spec.rb +181 -0
  78. data/spec/page_magic/session_methods_spec.rb +29 -25
  79. data/spec/page_magic/session_spec.rb +109 -199
  80. data/spec/page_magic/transitions_spec.rb +43 -0
  81. data/spec/page_magic/utils/string_spec.rb +29 -0
  82. data/spec/page_magic/utils/url_spec.rb +9 -0
  83. data/spec/page_magic/wait_methods_spec.rb +16 -22
  84. data/spec/page_magic/watcher_spec.rb +22 -0
  85. data/spec/page_magic/watchers_spec.rb +58 -62
  86. data/spec/page_magic_spec.rb +37 -29
  87. data/spec/spec_helper.rb +9 -2
  88. data/spec/support/shared_contexts.rb +3 -1
  89. data/spec/support/shared_examples.rb +17 -17
  90. metadata +101 -48
  91. data/lib/page_magic/element/query_builder.rb +0 -48
  92. data/lib/page_magic/element/selector_methods.rb +0 -13
  93. data/lib/page_magic/matcher.rb +0 -121
  94. data/spec/element_spec.rb +0 -249
  95. data/spec/page_magic/element/query_builder_spec.rb +0 -108
  96. data/spec/page_magic/matcher_spec.rb +0 -336
  97. data/spec/support/shared_contexts/files_context.rb +0 -7
  98. data/spec/support/shared_contexts/nested_elements_html_context.rb +0 -16
  99. data/spec/support/shared_contexts/rack_application_context.rb +0 -9
  100. data/spec/support/shared_contexts/webapp_fixture_context.rb +0 -39
  101. data/spec/watcher_spec.rb +0 -61
@@ -1,36 +1,40 @@
1
- module PageMagic
2
- describe SessionMethods do
3
- include_context :webapp_fixture
4
- let(:session) { PageMagic.session(application: rack_app, url: '/page1') }
5
- subject do
6
- OpenStruct.new(session: session).tap do |o|
7
- o.extend(described_class)
8
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+ RSpec.describe PageMagic::SessionMethods do
5
+ subject(:session_methods) do
6
+ OpenStruct.new(session: session).tap do |o|
7
+ o.extend(described_class)
9
8
  end
9
+ end
10
+
11
+ let(:session) do
12
+ rack_app = instance_double(Proc, call: [200, {}, ['<html><head><title>page1</title></head></html>']])
13
+ PageMagic.session(application: rack_app, url: '/page1')
14
+ end
10
15
 
11
- describe '#execute_script' do
12
- it 'returns the output of Session#execute_script' do
13
- expect(session.raw_session).to receive(:execute_script).with(:script).and_return(:result)
14
- expect(subject.execute_script(:script)).to eq(:result)
15
- end
16
+ describe '#execute_script' do
17
+ it 'returns the output of Session#execute_script' do
18
+ allow(session.raw_session).to receive(:execute_script).with(:script).and_return(:result)
19
+ expect(session_methods.execute_script(:script)).to eq(:result)
16
20
  end
21
+ end
17
22
 
18
- describe '#page' do
19
- it 'returns the current page of the session' do
20
- expect(subject.page).to eq(session.current_page)
21
- end
23
+ describe '#page' do
24
+ it 'returns the current page of the session' do
25
+ expect(session_methods.page).to eq(session.current_page)
22
26
  end
27
+ end
23
28
 
24
- describe '#path' do
25
- it 'returns the path of the session' do
26
- expect(subject.path).to eq(session.current_path)
27
- end
29
+ describe '#path' do
30
+ it 'returns the path of the session' do
31
+ expect(session_methods.path).to eq(session.current_path)
28
32
  end
33
+ end
29
34
 
30
- describe '#url' do
31
- it 'returns the url of the session' do
32
- expect(subject.url).to eq(session.current_url)
33
- end
35
+ describe '#url' do
36
+ it 'returns the url of the session' do
37
+ expect(session_methods.url).to eq(session.current_url)
34
38
  end
35
39
  end
36
40
  end
@@ -1,254 +1,164 @@
1
- # rubocop:disable Metrics/ModuleLength
2
- module PageMagic
3
- describe Session do
4
- let(:page) do
5
- Class.new do
6
- include PageMagic
7
- url 'http://url.com'
8
- end
9
- end
10
-
11
- subject { described_class.new(browser) }
1
+ # frozen_string_literal: true
12
2
 
13
- let(:url) { page.url }
14
- let(:browser) { double('browser', current_url: "#{url}/somepath", visit: nil, current_path: :current_path) }
3
+ RSpec.describe PageMagic::Session do
4
+ subject(:session) { described_class.new(browser, 'http://base.url') }
15
5
 
16
- describe '#current_page' do
17
- let(:another_page_class) do
18
- Class.new do
19
- include PageMagic
20
- url 'http://www.example.com/another_page1'
21
- end
22
- end
6
+ let(:page) do
7
+ Class.new do
8
+ include PageMagic
9
+ end
10
+ end
23
11
 
24
- before do
25
- subject.define_page_mappings '/another_page1' => another_page_class
26
- subject.visit(page, url: url)
12
+ let(:browser) do
13
+ rack_application = Class.new do
14
+ def self.call(_env)
15
+ [200, {}, ['ok']]
27
16
  end
17
+ end
28
18
 
29
- context 'page url has not changed' do
30
- it 'returns the original page' do
31
- allow(browser).to receive(:current_url).and_return(page.url)
32
- expect(subject.current_page).to be_an_instance_of(page)
33
- end
34
- end
19
+ Capybara::Session.new(:rack_test, rack_application)
20
+ end
35
21
 
36
- context 'page url has changed' do
37
- it 'returns the mapped page object' do
38
- allow(browser).to receive(:current_url).and_return(another_page_class.url)
39
- expect(subject.current_page).to be_an_instance_of(another_page_class)
40
- end
22
+ describe '#current_page' do
23
+ let(:another_page_class) do
24
+ Class.new do
25
+ include PageMagic
41
26
  end
42
27
  end
43
28
 
44
- describe '#current_path' do
45
- it "returns the browser's current path" do
46
- expect(subject.current_path).to eq(browser.current_path)
47
- end
29
+ it 'returns the current page' do
30
+ session.visit(page, url: 'http://base.url')
31
+ expect(session.current_page).to be_an_instance_of(page)
48
32
  end
49
33
 
50
- describe '#current_url' do
51
- it "returns the browser's current url" do
52
- expect(subject.current_url).to eq(browser.current_url)
34
+ context 'when the page url has changed' do
35
+ it 'returns the mapped page object' do
36
+ session.define_page_mappings '/another_page1' => another_page_class
37
+ browser.visit('/another_page1')
38
+ expect(session.current_page).to be_an_instance_of(another_page_class)
53
39
  end
54
40
  end
41
+ end
55
42
 
56
- describe '#define_page_mappings' do
57
- context 'mapping includes a literal' do
58
- it 'creates a matcher to contain the specification' do
59
- subject.define_page_mappings path: :page
60
- expect(subject.transitions).to eq(Matcher.new(:path) => :page)
61
- end
62
- end
43
+ describe '#current_path' do
44
+ it "returns the browser's current path" do
45
+ browser.visit('/url')
46
+ expect(session.current_path).to eq(browser.current_path)
47
+ end
48
+ end
63
49
 
64
- context 'mapping is a matcher' do
65
- it 'leaves it intact' do
66
- expected_matcher = Matcher.new(:page)
67
- subject.define_page_mappings expected_matcher => :page
68
- expect(subject.transitions.key(:page)).to be(expected_matcher)
69
- end
70
- end
50
+ describe '#current_url' do
51
+ it "returns the browser's current url" do
52
+ browser.visit('/url')
53
+ expect(session.current_url).to eq(session.current_url)
71
54
  end
55
+ end
72
56
 
73
- describe '#execute_script' do
74
- it 'calls the execute script method on the capybara session' do
75
- expect(browser).to receive(:execute_script).with(:script).and_return(:result)
76
- expect(subject.execute_script(:script)).to be(:result)
57
+ describe '#define_page_mappings' do
58
+ context 'when the mapping includes a literal' do
59
+ it 'creates a matcher to contain the specification' do
60
+ session.define_page_mappings path: :page
61
+ expect(session.transitions.to_h).to include(PageMagic::Mapping.new(:path) => :page)
77
62
  end
63
+ end
78
64
 
79
- context 'Capybara session does not support #execute_script' do
80
- let(:browser) { Capybara::Driver::Base.new }
81
- it 'raises an error' do
82
- expected_message = described_class::UNSUPPORTED_OPERATION_MSG
83
- expect { subject.execute_script(:script) }.to raise_error(NotSupportedException, expected_message)
84
- end
65
+ context 'when the mapping is a matcher' do
66
+ it 'leaves it intact' do
67
+ expected_matcher = PageMagic::Mapping.new(:page)
68
+ session.define_page_mappings expected_matcher => :page
69
+ expect(session.transitions.key(:page)).to be(expected_matcher)
85
70
  end
86
71
  end
72
+ end
87
73
 
88
- describe '#find_mapped_page' do
89
- subject do
90
- described_class.new(nil)
91
- end
74
+ describe '#execute_script' do
75
+ it 'calls the execute script method on the capybara session' do
76
+ allow(browser).to receive(:execute_script).with(:script).and_return(:result)
77
+ expect(session.execute_script(:script)).to be(:result)
78
+ end
92
79
 
93
- context 'match found' do
94
- it 'returns the page class' do
95
- subject.define_page_mappings '/page' => :mapped_page_using_string
96
- expect(subject.instance_eval { find_mapped_page('/page') }).to be(:mapped_page_using_string)
97
- end
80
+ context 'when the Capybara session does not support #execute_script' do
81
+ let(:browser) { Capybara::Driver::Base.new }
98
82
 
99
- context 'more than one match is found' do
100
- it 'returns the most specific match' do
101
- subject.define_page_mappings %r{/page} => :mapped_page_using_regex, '/page' => :mapped_page_using_string
102
- expect(subject.instance_eval { find_mapped_page('/page') }).to eq(:mapped_page_using_string)
103
- end
104
- end
83
+ it 'raises an error' do
84
+ expected_message = described_class::UNSUPPORTED_OPERATION_MSG
85
+ expect { session.execute_script(:script) }.to raise_error(PageMagic::NotSupportedException, expected_message)
105
86
  end
87
+ end
88
+ end
106
89
 
107
- context 'mapping is not found' do
108
- it 'returns nil' do
109
- expect(subject.instance_eval { find_mapped_page('/fake_page') }).to be(nil)
90
+ describe '#method_missing' do
91
+ before do
92
+ page.class_eval do
93
+ def my_method
94
+ :called
110
95
  end
111
96
  end
112
97
  end
113
98
 
114
- describe '#matches' do
115
- subject do
116
- described_class.new(nil)
117
- end
118
-
119
- it 'returns matching page mappings' do
120
- subject.define_page_mappings '/page' => :mapped_page_using_string
121
- expect(subject.instance_eval { matches('/page') }).to eq([:mapped_page_using_string])
122
- end
99
+ it 'delegates to current page' do
100
+ session = described_class.new(browser).visit(page, url: 'http://base.url')
101
+ expect(session.my_method).to be(:called)
102
+ end
103
+ end
123
104
 
124
- context 'more than one match on path' do
125
- it 'orders the results by specificity ' do
126
- subject.define_page_mappings %r{/page} => :mapped_page_using_regex, '/page' => :mapped_page_using_string
127
- expected_result = [:mapped_page_using_string, :mapped_page_using_regex]
128
- expect(subject.instance_eval { matches('/page') }).to eq(expected_result)
129
- end
130
- end
105
+ describe '#respond_to?' do
106
+ it 'checks self' do
107
+ session = described_class.new(browser)
108
+ expect(session.respond_to?(:current_url)).to eq(true)
131
109
  end
132
110
 
133
- describe '#method_missing' do
134
- it 'should delegate to current page' do
111
+ context 'when method is not on self' do
112
+ before do
135
113
  page.class_eval do
136
114
  def my_method
137
115
  :called
138
116
  end
139
117
  end
140
-
141
- session = PageMagic::Session.new(browser).visit(page, url: url)
142
- expect(session.my_method).to be(:called)
143
- end
144
- end
145
-
146
- context '#respond_to?' do
147
- subject do
148
- PageMagic::Session.new(browser).tap do |s|
149
- allow(s).to receive(:current_page).and_return(page.new)
150
- end
151
- end
152
- it 'checks self' do
153
- expect(subject.respond_to?(:current_url)).to eq(true)
154
118
  end
155
119
 
156
120
  it 'checks the current page' do
157
- page.class_eval do
158
- def my_method
159
- end
160
- end
161
- expect(subject.respond_to?(:my_method)).to eq(true)
121
+ session = described_class.new(browser)
122
+ session.visit(page, url: '/')
123
+ expect(session.respond_to?(:my_method)).to eq(true)
162
124
  end
163
125
  end
126
+ end
164
127
 
165
- describe '#url' do
166
- let!(:base_url) { 'http://example.com' }
167
- let!(:path) { 'home' }
168
- let!(:expected_url) { "#{base_url}/#{path}" }
169
-
170
- context 'base_url has a / on the end' do
171
- before do
172
- base_url << '/'
173
- end
174
-
175
- context 'path has / at the beginning' do
176
- it 'produces compound url' do
177
- expect(subject.send(:url, base_url, path)).to eq(expected_url)
178
- end
179
- end
180
-
181
- context 'path does not have / at the beginning' do
182
- it 'produces compound url' do
183
- expect(subject.send(:url, base_url, "/#{path}")).to eq(expected_url)
184
- end
185
- end
186
- end
187
-
188
- context 'current_url does not have a / on the end' do
189
- context 'path has / at the beginning' do
190
- it 'produces compound url' do
191
- expect(subject.send(:url, base_url, "/#{path}")).to eq(expected_url)
192
- end
193
- end
194
-
195
- context 'path does not have / at the beginning' do
196
- it 'produces compound url' do
197
- expect(subject.send(:url, base_url, path)).to eq(expected_url)
198
- end
199
- end
128
+ describe '#visit' do
129
+ context 'when a page is supplied' do
130
+ it 'sets the current page' do
131
+ session.define_page_mappings '/page' => page
132
+ session.visit(page)
133
+ expect(session.current_page).to be_a(page)
200
134
  end
201
- end
202
135
 
203
- describe '#visit' do
204
- let(:session) do
205
- allow(browser).to receive(:visit)
206
- PageMagic::Session.new(browser, url)
136
+ it 'uses the base url and the path in the page mappings' do
137
+ session = described_class.new(browser, 'http://base.url')
138
+ session.define_page_mappings '/page' => page
139
+ session.visit(page)
140
+ expect(session.current_url).to eq('http://base.url/page')
207
141
  end
208
142
 
209
- context 'page supplied' do
210
- it 'sets the current page' do
211
- session.define_page_mappings '/page' => page
143
+ it 'raises an error when page no page mappings are found' do
144
+ expect do
212
145
  session.visit(page)
213
- expect(session.current_page).to be_a(page)
214
- end
215
-
216
- it 'uses the base url and the path in the page mappings' do
217
- session.define_page_mappings '/page' => page
218
- expect(browser).to receive(:visit).with("#{url}/page")
219
- session.visit(page)
220
- end
221
-
222
- context 'no mappings found' do
223
- it 'raises an error' do
224
- expect { session.visit(page) }.to raise_exception InvalidURLException, described_class::URL_MISSING_MSG
225
- end
226
- end
227
-
228
- context 'mapping is a regular expression' do
229
- it 'raises an error' do
230
- session.define_page_mappings(/mapping/ => page)
231
- expect { session.visit(page) }.to raise_exception InvalidURLException, described_class::REGEXP_MAPPING_MSG
232
- end
233
- end
146
+ end.to raise_exception PageMagic::InvalidURLException, described_class::URL_MISSING_MSG
147
+ end
234
148
 
235
- it 'calls the onload hook' do
236
- on_load_hook_called = false
237
- page.on_load do
238
- on_load_hook_called = true
239
- end
240
- session.define_page_mappings('/page' => page)
241
- session.visit(page)
242
- expect(on_load_hook_called).to eq(true)
243
- end
149
+ it 'calls the onload hook' do
150
+ on_load_hook_called = false
151
+ page.on_load { on_load_hook_called = true }
152
+ session.visit(page, url: 'http://base.url')
153
+ expect(on_load_hook_called).to eq(true)
244
154
  end
155
+ end
245
156
 
246
- context 'url supplied' do
247
- it 'visits that url' do
248
- expected_url = 'http://url.com/page'
249
- expect(browser).to receive(:visit).with(expected_url)
250
- session.visit(url: expected_url)
251
- end
157
+ context 'when `url` is supplied' do
158
+ it 'visits that url' do
159
+ expected_url = 'http://base.url/page'
160
+ session.visit(url: expected_url)
161
+ expect(browser.current_url).to eq(expected_url)
252
162
  end
253
163
  end
254
164
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe PageMagic::Transitions do
4
+ describe '#mapped_page' do
5
+ context 'when a match is match found' do
6
+ it 'returns the page class' do
7
+ mappings = described_class.new('/page' => :mapped_page_using_string)
8
+ expect(mappings.mapped_page('/page')).to be(:mapped_page_using_string)
9
+ end
10
+ end
11
+
12
+ context 'when more than one match is found' do
13
+ it 'returns the most specific match' do
14
+ mappings = described_class.new(%r{/page} => :mapped_page_using_regex, '/page' => :mapped_page_using_string)
15
+ expect(mappings.mapped_page('/page')).to eq(:mapped_page_using_string)
16
+ end
17
+ end
18
+
19
+ context 'when a mapping is not found' do
20
+ it 'returns nil' do
21
+ mappings = described_class.new({})
22
+ expect(mappings.mapped_page('/unmapped_page')).to be(nil)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#url_for' do
28
+ it 'returns the url for the mapped page' do
29
+ page = Object.new
30
+ mappings = described_class.new('/mapping' => page)
31
+ expect(mappings.url_for(page, base_url: 'http://base.url')).to eq('http://base.url/mapping')
32
+ end
33
+
34
+ context 'when page mapping is a regular expression' do
35
+ it 'raises an error' do
36
+ page = Object.new
37
+ mappings = described_class.new(/mapping/ => page)
38
+ expect { mappings.url_for(page, base_url: 'http://base.url') }
39
+ .to raise_exception PageMagic::InvalidURLException, described_class::REGEXP_MAPPING_MSG
40
+ end
41
+ end
42
+ end
43
+ end