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
data/Makefile ADDED
@@ -0,0 +1,17 @@
1
+ .PHONY: help
2
+ DOCKER_IMAGE = lvl-up/page-magic
3
+ MOUNT_DIR = /page_magic
4
+
5
+ help:
6
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
7
+
8
+ docker: ## build project docker image
9
+ docker build -t $(DOCKER_IMAGE) .
10
+
11
+ test: ## Run tests
12
+ docker run -v $(PWD):$(MOUNT_DIR) -w $(MOUNT_DIR) -t $(DOCKER_IMAGE) bundle exec rspec
13
+
14
+ build: docker ## build gem
15
+ rake build
16
+
17
+ all: docker test build ## run all targets before building gem
data/README.md CHANGED
@@ -56,10 +56,12 @@ Check it out :)
56
56
  - [Helper Methods](#helper-methods)
57
57
  - [Dynamic Selectors](#dynamic-selectors)
58
58
  - [Starting a session](#starting-a-session)
59
+ - [Using an existing session](#using-an-existing-session)
60
+ - [Rack applications and Rack::Test](#rack-applications-and-racktest)
59
61
  - [Page mapping](#page-mapping)
60
62
  - [Mapping against query string parameters](#mapping-against-query-string-parameters)
61
63
  - [Mapping against fragment identifiers](#mapping-against-fragment-identifiers)
62
- - [Loading pages from source](#loading-pages-from-source)
64
+ - [Loading pages/elements from source](#loading-pages/elements-from-source)
63
65
  - [Watchers](#watchers)
64
66
  - [Method watchers](#method-watchers)
65
67
  - [Simple watchers](#simple-watchers)
@@ -325,9 +327,16 @@ session = PageMagic.session(browser: :chrome, url: 'https://www.github.com')
325
327
  ```
326
328
 
327
329
  Your session won't do much besides navigating to the given url until you have [mapped pages](#page-mapping) to it, so
328
- take a look at this next!
330
+ take a look at this next!
329
331
 
330
332
  **Note** PageMagic supports having multiple sessions using different browsers at the same time :)
333
+
334
+ ## Using an existing session
335
+ If you are introducing PageMagic in to a test suite that already makes use of Capybara, PageMagic can be configured to
336
+ make use of the session that is already configure like this:
337
+ ```ruby
338
+ session = PageMagic.session(session: Capybara.current_session)
339
+ ```
331
340
 
332
341
  ## Rack applications and Rack::Test
333
342
  To run a session against a rack application instead of a live site, simply supply the rack application when creating
@@ -381,16 +390,27 @@ against URL fragments.
381
390
  browser.define_page_mappings PageMagic.mapping(fragment: string_or_regex) => ResultsPage
382
391
  ```
383
392
 
384
- # Loading pages from source
393
+ # Loading pages/elements from source
385
394
  PageMagic supports loading page objects using html source. This technique can be useful for getting quick feedback that
386
- your templates correctly render based on your view objects. I.e you can test your templates in isolation.
395
+ your templates correctly render based on your view objects. I.e you can test your templates and partials/fragments in isolation.
387
396
  ```ruby
388
397
  class MyPage
389
398
  include PageMagic
399
+ element(:link, id: 'link_id')
390
400
  #element definitions
391
401
  end
392
402
 
393
403
  page_instance = Page.load(html_string)
404
+ page_instance.link.text # returns the link text
405
+
406
+
407
+ class CustomElement < PageMagic::Element
408
+ element(:link, id: 'link_id')
409
+ #element definitions
410
+ end
411
+
412
+ page_element = CustomElement.load(html_string)
413
+ page_element.link.text # returns the link text
394
414
  ```
395
415
 
396
416
  # Watchers
@@ -453,7 +473,8 @@ You can register any Capybara compliant driver as follows
453
473
  Webkit = PageMagic::Driver.new(:webkit) do |app, options, browser_alias_chosen|
454
474
  # Write the code necessary to initialise the driver you have chosen
455
475
  require 'capybara/webkit'
456
- Capybara::Webkit::Driver.new(app, options)
476
+ Capybara::Webkit::Driver.new(app,
477
+ )
457
478
  end
458
479
 
459
480
  #2. Register driver
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Bundler.require :test, :development
2
4
 
3
5
  RuboCop::RakeTask.new
@@ -12,11 +14,19 @@ Jeweler::Tasks.new do |gem|
12
14
  gem.license = 'ruby'
13
15
  gem.summary = 'Framework for modeling and interacting with webpages'
14
16
  gem.description = 'Framework for modeling and interacting with webpages which wraps capybara'
15
- gem.email = 'info@lad-tech.com'
17
+ gem.email = 'info@lvl-up.uk'
16
18
  gem.authors = ['Leon Davis']
17
19
  gem.required_ruby_version = '>= 2.1'
18
20
  end
19
21
 
20
22
  Jeweler::RubygemsDotOrgTasks.new
21
23
 
22
- task default: [:spec, 'rubocop:auto_correct']
24
+ require 'rake/testtask'
25
+ Rake::TestTask.new do |t|
26
+ t.libs << 'spec'
27
+ t.pattern = 'spec/**/*_test.rb'
28
+ t.warning = true
29
+ t.verbose = true
30
+ end
31
+
32
+ task default: [:spec, :test, 'rubocop:auto_correct']
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.8
1
+ 2.0.2
data/circle.yml CHANGED
@@ -3,4 +3,6 @@ machine:
3
3
  version: '2.2'
4
4
  test:
5
5
  override:
6
- - bundle exec rake
6
+ - bundle exec rake
7
+ post:
8
+ - CODECLIMATE_REPO_TOKEN=$CODECLIMATE_REPO_TOKEN bundle exec codeclimate-test-reporter
@@ -0,0 +1,84 @@
1
+ require 'cgi'
2
+
3
+ class Object
4
+ # Alias of <tt>to_s</tt>.
5
+ def to_param
6
+ to_s
7
+ end
8
+
9
+ # Converts an object into a string suitable for use as a URL query string,
10
+ # using the given <tt>key</tt> as the param name.
11
+ def to_query(key)
12
+ "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
13
+ end
14
+ end
15
+
16
+ class NilClass
17
+ # Returns +self+.
18
+ def to_param
19
+ self
20
+ end
21
+ end
22
+
23
+ class TrueClass
24
+ # Returns +self+.
25
+ def to_param
26
+ self
27
+ end
28
+ end
29
+
30
+ class FalseClass
31
+ # Returns +self+.
32
+ def to_param
33
+ self
34
+ end
35
+ end
36
+
37
+ class Array
38
+ # Calls <tt>to_param</tt> on all its elements and joins the result with
39
+ # slashes. This is used by <tt>url_for</tt> in Action Pack.
40
+ def to_param
41
+ collect(&:to_param).join '/'
42
+ end
43
+
44
+ # Converts an array into a string suitable for use as a URL query string,
45
+ # using the given +key+ as the param name.
46
+ #
47
+ # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
48
+ def to_query(key)
49
+ prefix = "#{key}[]"
50
+
51
+ if empty?
52
+ nil.to_query(prefix)
53
+ else
54
+ collect { |value| value.to_query(prefix) }.join '&'
55
+ end
56
+ end
57
+ end
58
+
59
+ class Hash
60
+ # Returns a string representation of the receiver suitable for use as a URL
61
+ # query string:
62
+ # @example
63
+ # {name: 'David', nationality: 'Danish'}.to_query
64
+ # # => "name=David&nationality=Danish"
65
+ #
66
+ # An optional namespace can be passed to enclose key names:
67
+ # @example
68
+ # {name: 'David', nationality: 'Danish'}.to_query('user')
69
+ # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
70
+ #
71
+ # The string pairs "key=value" that conform the query string
72
+ # are sorted lexicographically in ascending order.
73
+ #
74
+ # This method is also aliased as +to_param+.
75
+ def to_query(namespace = nil)
76
+ collect do |key, value|
77
+ unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
78
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
79
+ end
80
+ end.compact.sort! * '&'
81
+ end
82
+
83
+ alias to_param to_query
84
+ end
data/lib/page_magic.rb CHANGED
@@ -1,16 +1,17 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__).to_s)
1
+ # frozen_string_literal: true
2
+
2
3
  require 'capybara'
3
- require 'page_magic/exceptions'
4
- require 'page_magic/wait_methods'
5
- require 'page_magic/watchers'
6
- require 'page_magic/session'
7
- require 'page_magic/session_methods'
8
- require 'page_magic/elements'
9
- require 'page_magic/element_context'
10
- require 'page_magic/element'
11
- require 'page_magic/class_methods'
12
- require 'page_magic/instance_methods'
13
- require 'page_magic/drivers'
4
+ require_relative 'page_magic/exceptions'
5
+ require_relative 'page_magic/wait_methods'
6
+ require_relative 'page_magic/watchers'
7
+ require_relative 'page_magic/session'
8
+ require_relative 'page_magic/session_methods'
9
+ require_relative 'page_magic/elements'
10
+ require_relative 'page_magic/element_context'
11
+ require_relative 'page_magic/element'
12
+ require_relative 'page_magic/class_methods'
13
+ require_relative 'page_magic/instance_methods'
14
+ require_relative 'page_magic/drivers'
14
15
 
15
16
  # module PageMagic - PageMagic is an api for modelling pages in a website.
16
17
  module PageMagic
@@ -18,9 +19,9 @@ module PageMagic
18
19
 
19
20
  # @!method matcher
20
21
  # define match critera for loading a page object class
21
- # @see Matcher#initialize
22
- # @return [Matcher]
23
- def_delegator Matcher, :new, :matcher
22
+ # @see Mapping#initialize
23
+ # @return [Mapping]
24
+ def_delegator Mapping, :new, :matcher
24
25
 
25
26
  class << self
26
27
  # @return [Drivers] registered drivers
@@ -31,7 +32,8 @@ module PageMagic
31
32
  def included(clazz)
32
33
  clazz.class_eval do
33
34
  include(InstanceMethods)
34
- extend(Elements, ClassMethods)
35
+ extend ClassMethods
36
+ extend Elements
35
37
  end
36
38
  end
37
39
 
@@ -40,7 +42,7 @@ module PageMagic
40
42
  # PageMagic.mapping '/', parameters: {project: 'page_magic'}, fragment: 'display'
41
43
  # @see Matchers#initialize
42
44
  def mapping(path = nil, parameters: nil, fragment: nil)
43
- Matcher.new(path, parameters: parameters, fragment: fragment)
45
+ Mapping.new(path, parameters: parameters, fragment: fragment)
44
46
  end
45
47
 
46
48
  # Visit this page based on the class level registered url
@@ -48,16 +50,21 @@ module PageMagic
48
50
  # @param [Symbol] browser name of browser
49
51
  # @param [String] url url to start the session on
50
52
  # @param [Hash] options browser driver specific options
51
- # @return [Session] configured sessoin
52
- def session(application: nil, browser: :rack_test, url:, options: {})
53
- driver = drivers.find(browser)
54
- raise UnspportedBrowserException unless driver
53
+ # @param [Capybara::Session] session - use you own already configure capybara session e.g. Capybara.current_session
54
+ # @return [Session] configured session
55
+ def session(session: nil, url: nil, application: nil, browser: :rack_test, options: {})
56
+ session ||= begin
57
+ driver = drivers.find(browser)
58
+ raise UnsupportedBrowserException unless driver
59
+
60
+ Capybara.register_driver browser do |app|
61
+ driver.build(app, browser: browser, options: options)
62
+ end
55
63
 
56
- Capybara.register_driver browser do |app|
57
- driver.build(app, browser: browser, options: options)
64
+ Capybara::Session.new(browser, application)
58
65
  end
59
66
 
60
- Session.new(Capybara::Session.new(browser, application), url)
67
+ Session.new(session, url)
61
68
  end
62
69
  end
63
70
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # module ClassMethods - contains class level methods for PageObjects
3
5
  module ClassMethods
@@ -15,6 +17,7 @@ module PageMagic
15
17
  # if one has not been set on the page object class it will return a default block that does nothing
16
18
  def on_load(&block)
17
19
  return @on_load || DEFAULT_ON_LOAD unless block
20
+
18
21
  @on_load = block
19
22
  end
20
23
 
@@ -27,14 +30,14 @@ module PageMagic
27
30
 
28
31
  # Visit this page based on the class level registered url
29
32
  # @param [Object] application rack application (optional)
30
- # @param [Symbol] browser name of browser
33
+ # @param [Symbol] browser name of browser driver to use
31
34
  # @param [Hash] options browser driver specific options
32
35
  # @return [Session] active session configured to be using an instance of the page object modeled by this class
33
36
  def visit(application: nil, browser: :rack_test, options: {})
34
37
  session_options = { browser: browser, options: options, url: url }
35
38
  session_options[:application] = application if application
36
39
 
37
- PageMagic.session(session_options).tap do |session|
40
+ PageMagic.session(**session_options).tap do |session|
38
41
  session.visit(self, url: url)
39
42
  end
40
43
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'comparator/fuzzy'
4
+ require_relative 'comparator/literal'
5
+ require_relative 'comparator/parameter_map'
6
+ require_relative 'comparator/null'
7
+
8
+ module PageMagic
9
+ # class Comparator - used for comparing components used for mapping pages
10
+ class Comparator
11
+ class << self
12
+ def for(comparator)
13
+ klass = { Regexp => Fuzzy, Hash => ParameterMap, NilClass => Null }.fetch(comparator.class, Literal)
14
+ klass.new(comparator)
15
+ end
16
+ end
17
+
18
+ attr_reader :comparator, :fuzzy
19
+
20
+ def initialize(comparator, fuzzy)
21
+ @comparator = comparator
22
+ @fuzzy = fuzzy
23
+ end
24
+
25
+ def fuzzy?
26
+ fuzzy
27
+ end
28
+
29
+ def to_s
30
+ comparator.to_s
31
+ end
32
+
33
+ def ==(other)
34
+ comparator == other.comparator
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PageMagic
4
+ class Comparator
5
+ # class Fuzzy - used for modeling and comparing components that are 'fuzzy' i.e. respond to `=~` e.g. a Regexp
6
+ class Fuzzy < Comparator
7
+ def initialize(comparator)
8
+ super(comparator, true)
9
+ end
10
+
11
+ def match?(value)
12
+ comparator =~ value ? true : false
13
+ end
14
+
15
+ def <=>(other)
16
+ return -1 if other.is_a?(Null)
17
+ return 1 unless other.fuzzy?
18
+
19
+ 0
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PageMagic
4
+ class Comparator
5
+ # class Literal - used for modeling and comparing thing directly. E.g. strings
6
+ class Literal < Comparator
7
+ def initialize(comparator)
8
+ super(comparator, false)
9
+ end
10
+
11
+ def match?(value)
12
+ comparator == value
13
+ end
14
+
15
+ def <=>(other)
16
+ return 1 if other.fuzzy? || other.is_a?(Null)
17
+
18
+ 0
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PageMagic
4
+ class Comparator
5
+ # models mapping used to relate pages to uris
6
+ class Null < Comparator
7
+ def initialize(_comparator = nil)
8
+ super(nil, false)
9
+ end
10
+
11
+ def match?(_value)
12
+ true
13
+ end
14
+
15
+ def <=>(other)
16
+ return 0 if other.is_a?(Null)
17
+
18
+ 1
19
+ end
20
+
21
+ def present?
22
+ false
23
+ end
24
+ end
25
+ end
26
+ end