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