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
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe PageMagic::Utils::String do
4
+ describe '.classify' do
5
+ context 'when parameter is symbol' do
6
+ it 'returns a string' do
7
+ expect(described_class.classify(:Symbol)).to eq('Symbol')
8
+ end
9
+ end
10
+
11
+ context 'when parameter is string' do
12
+ it 'returns a string' do
13
+ expect(described_class.classify(:String)).to eq('String')
14
+ end
15
+ end
16
+
17
+ context 'when the first letter is lower case' do
18
+ it 'converts the first letter to uppercase' do
19
+ expect(described_class.classify(:symbol)).to eq('Symbol')
20
+ end
21
+ end
22
+
23
+ context 'when parameter is in snakecase' do
24
+ it 'removes underscores and capitalises each word' do
25
+ expect(described_class.classify(:snake_case)).to eq('SnakeCase')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe PageMagic::Utils::URL do
4
+ describe '#concat' do
5
+ it 'produces compound url' do
6
+ expect(described_class.concat('http://base.url/', '/home')).to eq('http://base.url/home')
7
+ end
8
+ end
9
+ end
@@ -1,40 +1,34 @@
1
- module PageMagic
2
- describe WaitMethods do
3
- subject do
4
- Object.new.tap do |o|
5
- o.extend(described_class)
6
- end
7
- end
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe PageMagic::WaitMethods do
4
+ include described_class
8
5
 
6
+ describe '#wait_until' do
9
7
  let(:default_options) { { timeout_after: 0.1, retry_every: 0.05 } }
8
+ let!(:start_time) { Time.now }
10
9
 
11
10
  it 'waits until the prescribed thing has happened' do
12
- expect { subject.wait_until(default_options) { true } }.to_not raise_exception
11
+ expect { wait_until(**default_options) { true } }.not_to raise_exception
13
12
  end
14
13
 
15
- it 'should keep trying for a specified period' do
16
- start_time = Time.now
17
-
18
- expect { subject.wait_until(default_options) { false } }.to raise_exception TimeoutException
19
-
14
+ it 'keeps trying for a specified period' do
15
+ wait_until(**default_options) { false }
16
+ rescue PageMagic::TimeoutException
20
17
  expect(Time.now - default_options[:timeout_after]).to be > start_time
21
18
  end
22
19
 
23
- context 'timeout_after specified' do
20
+ context 'when `timeout_after` specified' do
24
21
  it 'throws an exception if when the prescribed action does not happen in time' do
25
- expect { subject.wait_until(default_options) { false } }.to raise_error TimeoutException
22
+ expect { wait_until(**default_options) { false } }.to raise_error PageMagic::TimeoutException
26
23
  end
27
24
  end
28
25
 
29
- context 'retry time specified' do
26
+ context 'when retry time specified' do
30
27
  it 'retries at the given interval' do
31
28
  count = 0
32
- expect do
33
- subject.wait_until(timeout_after: default_options[:timeout_after] * 2, retry_every: 0.1) do
34
- count += 1
35
- end
36
- end.to raise_exception TimeoutException
37
- expect(count).to eq(2)
29
+ expect { wait_until(timeout_after: 0.2, retry_every: 0.1) { count += 1 } }
30
+ .to raise_exception(PageMagic::TimeoutException)
31
+ .and change { count }.by(2)
38
32
  end
39
33
  end
40
34
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe PageMagic::Watcher do
4
+ describe '#initialize' do
5
+ context 'when `check` has not been called yet' do
6
+ it 'sets `observed_value` to nil' do
7
+ instance = described_class.new(:custom_watcher, context: self)
8
+ expect(instance.observed_value).to be_nil
9
+ end
10
+ end
11
+ end
12
+
13
+ describe '#check' do
14
+ it 'assigns last to be the result of calling the block passed to the constructor' do
15
+ instance = described_class.new(:object_id, context: self) do
16
+ object_id
17
+ end
18
+ instance.check
19
+ expect(instance.observed_value).to eq(object_id)
20
+ end
21
+ end
22
+ end
@@ -1,84 +1,80 @@
1
- module PageMagic
2
- describe Watchers do
3
- let(:element) { OpenStruct.new(text: :current_value) }
1
+ # frozen_string_literal: true
4
2
 
5
- subject do
6
- Object.new.tap do |o|
7
- o.extend(described_class)
8
- allow(o).to receive(:my_watcher).and_return(element)
9
- end
10
- end
11
-
12
- describe '#changed?' do
13
- before do
14
- subject.watch(:my_watcher, :text)
15
- end
3
+ require 'ostruct'
4
+ RSpec.describe PageMagic::Watchers do
5
+ include described_class
6
+ let(:element) { OpenStruct.new(text: :current_value) }
16
7
 
17
- context 'watched element has changed' do
18
- it 'returns true' do
19
- element.text = :new_value
20
- expect(subject.changed?(:my_watcher)).to eq(true)
21
- end
8
+ describe '#changed?' do
9
+ context 'when the watched element has changed' do
10
+ it 'returns true' do
11
+ watch(:element_watcher, context: element, method: :text)
12
+ element.text = :new_value
13
+ expect(changed?(:element_watcher)).to eq(true)
22
14
  end
15
+ end
23
16
 
24
- context 'watched element has not changed' do
25
- it 'returns false' do
26
- expect(subject.changed?(:my_watcher)).to eq(false)
27
- end
17
+ context 'when the watched element has not changed' do
18
+ it 'returns false' do
19
+ watch(:element_watcher, context: element, method: :text)
20
+ expect(changed?(:element_watcher)).to eq(false)
28
21
  end
29
22
  end
23
+ end
30
24
 
31
- describe '#watch' do
32
- it 'stores the initial value of the watched element' do
33
- subject.watch(:my_watcher, :text)
34
- expect(subject.watcher(:my_watcher).last).to eq(:current_value)
35
- end
25
+ describe '#watch' do
26
+ it 'stores the initial value of the watched element' do
27
+ watch(:element_watcher, context: element, method: :text)
28
+ expect(watcher(:element_watcher).observed_value).to eq(:current_value)
29
+ end
36
30
 
37
- context 'element name/ method name supplied' do
38
- it 'stores the watch instruction' do
39
- subject.watch(:object_id)
40
- expect(subject.watcher(:object_id)).to eq(Watcher.new(:object_id))
41
- end
31
+ context 'when a method name is supplied' do
32
+ it 'stores the watch instruction' do
33
+ watch(:object_id)
34
+ watcher = watcher(:object_id)
35
+ expect(watcher.check.observed_value).to eq(object_id)
42
36
  end
37
+ end
43
38
 
44
- context 'element name and attribute supplied' do
45
- it 'stores the watch instruction' do
46
- subject.watch(:my_watcher, :text)
47
- expect(subject.watcher(:my_watcher)).to eq(Watcher.new(:my_watcher, :text))
48
- end
39
+ context 'when a watcher name and a method are supplied' do
40
+ it 'stores the watch instruction' do
41
+ watch(:my_watcher, method: :object_id)
42
+ watcher = watcher(:my_watcher)
43
+ expect(watcher.check.observed_value).to eq(object_id)
49
44
  end
45
+ end
50
46
 
51
- context 'block supplied' do
52
- it 'stores the watch instruction' do
53
- block = proc {}
54
- subject.watch(:my_watcher, &block)
55
- expect(subject.watcher(:my_watcher)).to eq(Watcher.new(:my_watcher, &block))
56
- end
47
+ context 'when a block is supplied' do
48
+ it 'stores the watch instruction' do
49
+ block = proc { :value }
50
+ watch(:my_watcher, &block)
51
+ watcher = watcher(:my_watcher)
52
+ expect(watcher.check.observed_value).to eq(:value)
57
53
  end
54
+ end
58
55
 
59
- context 'watcher with the same name added' do
60
- it 'replaces the watcher' do
61
- subject.watch(:object_id)
62
- original_watcher = subject.watchers.first
63
- subject.watch(:object_id)
64
- expect(subject.watchers.size).to eq(1)
65
- expect(subject.watchers.first).to_not be(original_watcher)
66
- end
56
+ context 'when a watcher with the same name is added' do
57
+ it 'replaces the watcher' do
58
+ original_watcher = watch(:object_id)
59
+ expect(watcher(:object_id)).not_to be(original_watcher)
67
60
  end
61
+ end
68
62
 
69
- context 'watcher defined on element that does not exist' do
70
- it 'raises an error' do
71
- expected_message = described_class::ELEMENT_MISSING_MSG % :missing
72
- expect { subject.watch(:missing, :text) }.to raise_exception(ElementMissingException, expected_message)
73
- end
63
+ context 'when a watcher defined on a method that does not exist' do
64
+ it 'raises an error' do
65
+ expected_message = described_class::ELEMENT_MISSING_MSG % :text
66
+ expect do
67
+ watch(:missing, method: :text)
68
+ end.to raise_exception(PageMagic::ElementMissingException, expected_message)
74
69
  end
75
70
  end
71
+ end
76
72
 
77
- describe '#watcher' do
78
- it 'returns the watcher with the given name' do
79
- subject.watch(:my_watcher, :text)
80
- expect(subject.watcher(:my_watcher)).to eq(Watcher.new(:my_watcher, :text))
81
- end
73
+ describe '#watcher' do
74
+ it 'returns the watcher with the given name' do
75
+ watch(:my_watcher, method: :object_id)
76
+ watcher = watcher(:my_watcher)
77
+ expect(watcher.check.observed_value).to eq(object_id)
82
78
  end
83
79
  end
84
80
  end
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'page_magic'
2
4
 
3
- describe PageMagic do
4
- subject do
5
- Class.new { include PageMagic }
5
+ RSpec.describe PageMagic do
6
+ subject(:page_class) do
7
+ Class.new.tap do |klass|
8
+ klass.include(described_class)
9
+ end
6
10
  end
7
11
 
8
12
  describe '.drivers' do
@@ -15,12 +19,12 @@ describe PageMagic do
15
19
 
16
20
  describe '.included' do
17
21
  it 'gives a method for defining the url' do
18
- subject.url :url
19
- expect(subject.url).to eq(:url)
22
+ page_class.url :url
23
+ expect(page_class.url).to eq(:url)
20
24
  end
21
25
 
22
26
  it 'lets you define elements' do
23
- expect(subject).to be_a(PageMagic::Elements)
27
+ expect(page_class).to be_a(PageMagic::Elements)
24
28
  end
25
29
  end
26
30
 
@@ -36,12 +40,12 @@ describe PageMagic do
36
40
  Class.new(parent_page)
37
41
  end
38
42
 
39
- context 'children' do
40
- it 'should inherit elements defined on the parent class' do
43
+ describe 'children' do
44
+ it 'inherits elements defined on the parent class' do
41
45
  expect(child_page.element_definitions).to include(:next)
42
46
  end
43
47
 
44
- it 'should pass on element definitions to their children' do
48
+ it 'passes on element definitions to their children' do
45
49
  grand_child_class = Class.new(child_page)
46
50
  expect(grand_child_class.element_definitions).to include(:next)
47
51
  end
@@ -51,44 +55,48 @@ describe PageMagic do
51
55
  describe '#mapping' do
52
56
  it 'returns a matcher' do
53
57
  mapping = described_class.mapping('/', parameters: {}, fragment: '')
54
- expect(mapping).to eq(PageMagic::Matcher.new('/', parameters: {}, fragment: ''))
58
+ expect(mapping).to eq(PageMagic::Mapping.new('/', parameters: {}, fragment: ''))
55
59
  end
56
60
  end
57
61
 
58
62
  describe '.session' do
59
- let(:url) { 'http://url.com/' }
60
- let(:application) { rack_application.new }
61
-
62
- before do
63
- allow_any_instance_of(Capybara::Selenium::Driver).to receive(:visit)
63
+ let(:rack_application) do
64
+ Class.new do
65
+ def self.call(_env)
66
+ [200, {}, ['hello world!!']]
67
+ end
68
+ end
64
69
  end
65
70
 
71
+ let(:url) { 'http://url.com/' }
72
+
66
73
  it "defaults to capybara's default session " do
67
74
  Capybara.default_driver = :rack_test
68
- expect(subject.new.browser.mode).to eq(:rack_test)
75
+ expect(page_class.new.browser.mode).to eq(:rack_test)
69
76
  end
70
77
 
71
- context 'specifying the browser' do
78
+ context 'when `:browser` is specified' do
72
79
  it 'loads the correct driver' do
73
- session = described_class.session(application: rack_application.new, browser: :firefox, url: :url)
80
+ session = described_class.session(application: rack_application, browser: :firefox, url: :url)
74
81
  expect(session.raw_session.driver).to be_a(Capybara::Selenium::Driver)
75
82
  end
76
83
  end
77
84
 
78
- include_context :rack_application
79
- it 'sets the base url for the session' do
80
- session = described_class.session(application: application, url: url)
81
- expect(session.current_url).to eq(url)
85
+ context 'when `:rack_application` is specified' do
86
+ it 'configures capybara to run against the app' do
87
+ session = described_class.session(application: rack_application, url: url)
88
+ expect(session.raw_session.app).to be(rack_application)
89
+ end
82
90
  end
83
91
 
84
- context 'specifying a rack application' do
85
- it 'configures capybara to run against the app' do
86
- session = described_class.session(application: application, url: url)
87
- expect(session.raw_session.app).to be(application)
92
+ context 'when `session` is specified' do
93
+ it 'uses it' do
94
+ session = described_class.session(session: :custom_session)
95
+ expect(session.raw_session).to be(:custom_session)
88
96
  end
89
97
  end
90
98
 
91
- context 'specifying options' do
99
+ context 'when `:options` is specified' do
92
100
  it 'passes the options to the browser driver' do
93
101
  options = { option: :config }
94
102
  session = described_class.session(options: options, browser: :chrome, url: url)
@@ -97,9 +105,9 @@ describe PageMagic do
97
105
  end
98
106
  end
99
107
 
100
- context 'driver for browser not found' do
108
+ context 'when the driver assigned to `:browser` is not found' do
101
109
  it 'raises an error' do
102
- expected_exception = described_class::UnspportedBrowserException
110
+ expected_exception = described_class::UnsupportedBrowserException
103
111
  expect { described_class.session(browser: :invalid, url: url) }.to raise_exception expected_exception
104
112
  end
105
113
  end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,15 @@
1
- Bundler.require
2
- $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
1
+ # frozen_string_literal: true
2
+
3
+ this_dir = File.dirname(__FILE__)
4
+ $LOAD_PATH.unshift("#{this_dir}/lib")
3
5
  require 'support/shared_contexts'
4
6
  require 'support/shared_examples'
5
7
 
6
8
  require 'simplecov' if ENV['coverage']
7
9
 
8
10
  require 'page_magic'
11
+
12
+ Dir["#{this_dir}/../lib/page_magic/drivers/*.rb"].sort.each do |driver|
13
+ require driver
14
+ end
15
+ Capybara.server = :webrick
@@ -1,3 +1,5 @@
1
- Dir["#{__dir__}/shared_contexts/*.rb"].each do |file|
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{__dir__}/shared_contexts/*.rb"].sort.each do |file|
2
4
  require file
3
5
  end
@@ -1,25 +1,25 @@
1
- module PageMagic
2
- shared_examples 'session accessor' do
3
- it 'includes session methods' do
4
- expect(described_class.included_modules).to include(SessionMethods)
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'session accessor' do
4
+ it 'includes session methods' do
5
+ expect(described_class.included_modules).to include(PageMagic::SessionMethods)
6
6
  end
7
+ end
7
8
 
8
- shared_examples 'element watcher' do
9
- it 'includes watchers' do
10
- expect(described_class.included_modules).to include(Watchers)
11
- end
9
+ RSpec.shared_examples 'element watcher' do
10
+ it 'includes watchers' do
11
+ expect(described_class.included_modules).to include(PageMagic::Watchers)
12
12
  end
13
+ end
13
14
 
14
- shared_examples 'waiter' do
15
- it 'includes waiters' do
16
- expect(described_class.included_modules).to include(WaitMethods)
17
- end
15
+ RSpec.shared_examples 'waiter' do
16
+ it 'includes waiters' do
17
+ expect(described_class.included_modules).to include(PageMagic::WaitMethods)
18
18
  end
19
+ end
19
20
 
20
- shared_examples 'element locator' do
21
- it 'includes Locators' do
22
- expect(described_class.included_modules).to include(Element::Locators)
23
- end
21
+ RSpec.shared_examples 'element locator' do
22
+ it 'includes Locators' do
23
+ expect(described_class.included_modules).to include(PageMagic::Element::Locators)
24
24
  end
25
25
  end