page_magic 2.0.0.alpha1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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