page_magic 2.0.0.alpha1 → 2.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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -2
  3. data/.zsh_config +5 -5
  4. data/Dockerfile +2 -1
  5. data/Gemfile +2 -1
  6. data/Gemfile.lock +9 -5
  7. data/Makefile +7 -3
  8. data/README.md +16 -4
  9. data/VERSION +1 -1
  10. data/lib/active_support/core_ext/object/to_query.rb +6 -6
  11. data/lib/page_magic.rb +15 -16
  12. data/lib/page_magic/class_methods.rb +1 -1
  13. data/lib/page_magic/comparator.rb +37 -0
  14. data/lib/page_magic/comparator/fuzzy.rb +23 -0
  15. data/lib/page_magic/comparator/literal.rb +22 -0
  16. data/lib/page_magic/comparator/null.rb +26 -0
  17. data/lib/page_magic/comparator/parameter_map.rb +52 -0
  18. data/lib/page_magic/drivers.rb +2 -2
  19. data/lib/page_magic/element.rb +19 -8
  20. data/lib/page_magic/element/locators.rb +4 -4
  21. data/lib/page_magic/element/not_found.rb +38 -0
  22. data/lib/page_magic/element/query.rb +19 -27
  23. data/lib/page_magic/element/query/multiple_results.rb +21 -0
  24. data/lib/page_magic/element/query/prefetched_result.rb +26 -0
  25. data/lib/page_magic/element/query/single_result.rb +20 -0
  26. data/lib/page_magic/element/selector.rb +38 -16
  27. data/lib/page_magic/element/selector/methods.rb +18 -0
  28. data/lib/page_magic/element/selector/model.rb +21 -0
  29. data/lib/page_magic/element_context.rb +5 -21
  30. data/lib/page_magic/element_definition_builder.rb +17 -24
  31. data/lib/page_magic/elements.rb +62 -102
  32. data/lib/page_magic/elements/config.rb +103 -0
  33. data/lib/page_magic/elements/inheritance_hooks.rb +15 -0
  34. data/lib/page_magic/elements/types.rb +25 -0
  35. data/lib/page_magic/exceptions.rb +3 -0
  36. data/lib/page_magic/instance_methods.rb +2 -2
  37. data/lib/page_magic/mapping.rb +79 -0
  38. data/lib/page_magic/session.rb +10 -32
  39. data/lib/page_magic/session_methods.rb +1 -1
  40. data/lib/page_magic/transitions.rb +49 -0
  41. data/lib/page_magic/utils/string.rb +4 -0
  42. data/lib/page_magic/utils/url.rb +20 -0
  43. data/lib/page_magic/watcher.rb +10 -17
  44. data/lib/page_magic/watchers.rb +28 -15
  45. data/spec/page_magic/class_methods_spec.rb +64 -37
  46. data/spec/page_magic/comparator/fuzzy_spec.rb +44 -0
  47. data/spec/page_magic/comparator/literal_spec.rb +41 -0
  48. data/spec/page_magic/comparator/null_spec.rb +35 -0
  49. data/spec/page_magic/comparator/parameter_map_spec.rb +75 -0
  50. data/spec/page_magic/driver_spec.rb +25 -29
  51. data/spec/page_magic/drivers/poltergeist_spec.rb +4 -7
  52. data/spec/page_magic/drivers/rack_test_spec.rb +4 -9
  53. data/spec/page_magic/drivers/selenium_spec.rb +9 -12
  54. data/spec/page_magic/drivers_spec.rb +36 -29
  55. data/spec/page_magic/element/locators_spec.rb +26 -25
  56. data/spec/page_magic/element/not_found_spec.rb +24 -0
  57. data/spec/page_magic/element/query/multiple_results_spec.rb +14 -0
  58. data/spec/page_magic/element/query/single_result_spec.rb +21 -0
  59. data/spec/page_magic/element/query_spec.rb +26 -47
  60. data/spec/page_magic/element/selector_spec.rb +118 -110
  61. data/spec/page_magic/element_context_spec.rb +46 -88
  62. data/spec/page_magic/element_definition_builder_spec.rb +12 -71
  63. data/spec/page_magic/element_spec.rb +256 -0
  64. data/spec/page_magic/elements/config_spec.rb +200 -0
  65. data/spec/page_magic/elements_spec.rb +87 -138
  66. data/spec/page_magic/instance_methods_spec.rb +63 -63
  67. data/spec/page_magic/mapping_spec.rb +181 -0
  68. data/spec/page_magic/session_methods_spec.rb +27 -25
  69. data/spec/page_magic/session_spec.rb +109 -198
  70. data/spec/page_magic/transitions_spec.rb +43 -0
  71. data/spec/page_magic/utils/string_spec.rb +20 -27
  72. data/spec/page_magic/utils/url_spec.rb +9 -0
  73. data/spec/page_magic/wait_methods_spec.rb +14 -22
  74. data/spec/page_magic/watcher_spec.rb +22 -0
  75. data/spec/page_magic/watchers_spec.rb +56 -62
  76. data/spec/page_magic_spec.rb +27 -24
  77. data/spec/spec_helper.rb +7 -3
  78. data/spec/support/shared_examples.rb +15 -17
  79. metadata +48 -15
  80. data/lib/page_magic/element/query_builder.rb +0 -61
  81. data/lib/page_magic/element/selector_methods.rb +0 -16
  82. data/lib/page_magic/matcher.rb +0 -130
  83. data/spec/element_spec.rb +0 -251
  84. data/spec/page_magic/element/query_builder_spec.rb +0 -110
  85. data/spec/page_magic/matcher_spec.rb +0 -338
  86. data/spec/support/shared_contexts/files_context.rb +0 -9
  87. data/spec/support/shared_contexts/nested_elements_html_context.rb +0 -18
  88. data/spec/support/shared_contexts/rack_application_context.rb +0 -11
  89. data/spec/support/shared_contexts/webapp_fixture_context.rb +0 -41
  90. data/spec/watcher_spec.rb +0 -64
@@ -1,38 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module PageMagic
4
- describe SessionMethods do
5
- include_context :webapp_fixture
6
- let(:session) { PageMagic.session(application: rack_app, url: '/page1') }
7
- subject do
8
- OpenStruct.new(session: session).tap do |o|
9
- o.extend(described_class)
10
- end
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)
11
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
12
15
 
13
- describe '#execute_script' do
14
- it 'returns the output of Session#execute_script' do
15
- expect(session.raw_session).to receive(:execute_script).with(:script).and_return(:result)
16
- expect(subject.execute_script(:script)).to eq(:result)
17
- 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)
18
20
  end
21
+ end
19
22
 
20
- describe '#page' do
21
- it 'returns the current page of the session' do
22
- expect(subject.page).to eq(session.current_page)
23
- 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)
24
26
  end
27
+ end
25
28
 
26
- describe '#path' do
27
- it 'returns the path of the session' do
28
- expect(subject.path).to eq(session.current_path)
29
- end
29
+ describe '#path' do
30
+ it 'returns the path of the session' do
31
+ expect(session_methods.path).to eq(session.current_path)
30
32
  end
33
+ end
31
34
 
32
- describe '#url' do
33
- it 'returns the url of the session' do
34
- expect(subject.url).to eq(session.current_url)
35
- end
35
+ describe '#url' do
36
+ it 'returns the url of the session' do
37
+ expect(session_methods.url).to eq(session.current_url)
36
38
  end
37
39
  end
38
40
  end
@@ -1,253 +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 = %i[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; end
159
- end
160
- 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)
161
124
  end
162
125
  end
126
+ end
163
127
 
164
- describe '#url' do
165
- let!(:base_url) { "http://example.com" }
166
- let!(:path) { 'home' }
167
- let!(:expected_url) { "#{base_url}/#{path}" }
168
-
169
- context 'base_url has a / on the end' do
170
- before do
171
- base_url << '/'
172
- end
173
-
174
- context 'path has / at the beginning' do
175
- it 'produces compound url' do
176
- expect(subject.send(:url, base_url, path)).to eq(expected_url)
177
- end
178
- end
179
-
180
- context 'path does not have / at the beginning' do
181
- it 'produces compound url' do
182
- expect(subject.send(:url, base_url, "/#{path}")).to eq(expected_url)
183
- end
184
- end
185
- end
186
-
187
- context 'current_url does not have a / on the end' do
188
- context 'path has / at the beginning' do
189
- it 'produces compound url' do
190
- expect(subject.send(:url, base_url, "/#{path}")).to eq(expected_url)
191
- end
192
- end
193
-
194
- context 'path does not have / at the beginning' do
195
- it 'produces compound url' do
196
- expect(subject.send(:url, base_url, path)).to eq(expected_url)
197
- end
198
- 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)
199
134
  end
200
- end
201
135
 
202
- describe '#visit' do
203
- let(:session) do
204
- allow(browser).to receive(:visit)
205
- 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')
206
141
  end
207
142
 
208
- context 'page supplied' do
209
- it 'sets the current page' do
210
- session.define_page_mappings '/page' => page
143
+ it 'raises an error when page no page mappings are found' do
144
+ expect do
211
145
  session.visit(page)
212
- expect(session.current_page).to be_a(page)
213
- end
214
-
215
- it 'uses the base url and the path in the page mappings' do
216
- session.define_page_mappings '/page' => page
217
- expect(browser).to receive(:visit).with("#{url}/page")
218
- session.visit(page)
219
- end
220
-
221
- context 'no mappings found' do
222
- it 'raises an error' do
223
- expect { session.visit(page) }.to raise_exception InvalidURLException, described_class::URL_MISSING_MSG
224
- end
225
- end
226
-
227
- context 'mapping is a regular expression' do
228
- it 'raises an error' do
229
- session.define_page_mappings(/mapping/ => page)
230
- expect { session.visit(page) }.to raise_exception InvalidURLException, described_class::REGEXP_MAPPING_MSG
231
- end
232
- end
146
+ end.to raise_exception PageMagic::InvalidURLException, described_class::URL_MISSING_MSG
147
+ end
233
148
 
234
- it 'calls the onload hook' do
235
- on_load_hook_called = false
236
- page.on_load do
237
- on_load_hook_called = true
238
- end
239
- session.define_page_mappings('/page' => page)
240
- session.visit(page)
241
- expect(on_load_hook_called).to eq(true)
242
- 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)
243
154
  end
155
+ end
244
156
 
245
- context 'url supplied' do
246
- it 'visits that url' do
247
- expected_url = 'http://url.com/page'
248
- expect(browser).to receive(:visit).with(expected_url)
249
- session.visit(url: expected_url)
250
- 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)
251
162
  end
252
163
  end
253
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