page_magic 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 +135 -144
  8. data/Makefile +17 -0
  9. data/README.md +48 -4
  10. data/Rakefile +12 -2
  11. data/VERSION +1 -1
  12. data/circle.yml +4 -2
  13. data/lib/active_support/core_ext/object/to_query.rb +84 -0
  14. data/lib/page_magic.rb +22 -20
  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 -68
  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 +17 -13
  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 +200 -0
  75. data/spec/page_magic/elements_spec.rb +90 -127
  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 +31 -30
  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,41 @@ 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)
82
- end
83
-
84
- context 'specifying a rack application' do
85
+ context 'when `:rack_application` is specified' do
85
86
  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)
87
+ session = described_class.session(application: rack_application, url: url)
88
+ expect(session.raw_session.app).to be(rack_application)
88
89
  end
89
90
  end
90
91
 
91
- context 'specifying options' do
92
+ context 'when `:options` is specified' do
92
93
  it 'passes the options to the browser driver' do
93
94
  options = { option: :config }
94
95
  session = described_class.session(options: options, browser: :chrome, url: url)
@@ -97,9 +98,9 @@ describe PageMagic do
97
98
  end
98
99
  end
99
100
 
100
- context 'driver for browser not found' do
101
+ context 'when the driver assigned to `:browser` is not found' do
101
102
  it 'raises an error' do
102
- expected_exception = described_class::UnspportedBrowserException
103
+ expected_exception = described_class::UnsupportedBrowserException
103
104
  expect { described_class.session(browser: :invalid, url: url) }.to raise_exception expected_exception
104
105
  end
105
106
  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