page_magic 0.8.8

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.
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ page_magic
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - '1.9.3'
3
+ - '2.0.0'
4
+
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "watir-webdriver"
4
+ gem 'capybara'
5
+ gem 'wait'
6
+
7
+ group :test do
8
+ gem 'sinatra'
9
+ gem "rspec", require: 'rspec/core/rake_task'
10
+ gem "simplecov", require: false
11
+ end
12
+
13
+ group :development do
14
+ gem "jeweler", "~> 1.8.4"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ capybara (2.1.0)
5
+ mime-types (>= 1.16)
6
+ nokogiri (>= 1.3.3)
7
+ rack (>= 1.0.0)
8
+ rack-test (>= 0.5.4)
9
+ xpath (~> 2.0)
10
+ childprocess (0.3.9)
11
+ ffi (~> 1.0, >= 1.0.11)
12
+ diff-lcs (1.2.4)
13
+ ffi (1.8.1)
14
+ git (1.2.5)
15
+ jeweler (1.8.4)
16
+ bundler (~> 1.0)
17
+ git (>= 1.2.5)
18
+ rake
19
+ rdoc
20
+ json (1.7.7)
21
+ mime-types (1.25)
22
+ mini_portile (0.5.1)
23
+ multi_json (1.7.3)
24
+ nokogiri (1.6.0)
25
+ mini_portile (~> 0.5.0)
26
+ rack (1.5.2)
27
+ rack-protection (1.5.0)
28
+ rack
29
+ rack-test (0.6.2)
30
+ rack (>= 1.0)
31
+ rake (10.0.4)
32
+ rdoc (4.0.1)
33
+ json (~> 1.4)
34
+ rspec (2.14.1)
35
+ rspec-core (~> 2.14.0)
36
+ rspec-expectations (~> 2.14.0)
37
+ rspec-mocks (~> 2.14.0)
38
+ rspec-core (2.14.6)
39
+ rspec-expectations (2.14.3)
40
+ diff-lcs (>= 1.1.3, < 2.0)
41
+ rspec-mocks (2.14.4)
42
+ rubyzip (0.9.9)
43
+ selenium-webdriver (2.32.1)
44
+ childprocess (>= 0.2.5)
45
+ multi_json (~> 1.0)
46
+ rubyzip
47
+ websocket (~> 1.0.4)
48
+ simplecov (0.7.1)
49
+ multi_json (~> 1.0)
50
+ simplecov-html (~> 0.7.1)
51
+ simplecov-html (0.7.1)
52
+ sinatra (1.4.3)
53
+ rack (~> 1.4)
54
+ rack-protection (~> 1.4)
55
+ tilt (~> 1.3, >= 1.3.4)
56
+ tilt (1.4.1)
57
+ wait (0.5.1)
58
+ watir-webdriver (0.6.4)
59
+ selenium-webdriver (>= 2.18.0)
60
+ websocket (1.0.7)
61
+ xpath (2.0.0)
62
+ nokogiri (~> 1.3)
63
+
64
+ PLATFORMS
65
+ ruby
66
+
67
+ DEPENDENCIES
68
+ capybara
69
+ jeweler (~> 1.8.4)
70
+ rspec
71
+ simplecov
72
+ sinatra
73
+ wait
74
+ watir-webdriver
data/README.md ADDED
@@ -0,0 +1 @@
1
+ [![Build Status](https://travis-ci.org/Ladtech/page_magic.png)](https://travis-ci.org/Ladtech/page_magic)
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ Bundler.require :test,:development
2
+
3
+ RSpec::Core::RakeTask.new(:spec) do
4
+ ENV['coverage'] = "true"
5
+ end
6
+
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "page_magic"
9
+ gem.homepage = "https://github.com/ladtech/page_magic"
10
+ gem.license = "ruby"
11
+ gem.summary = "Framework for modeling and interacting with webpages"
12
+ gem.description = "Framework for modeling and interacting with webpages which wraps capybara"
13
+ gem.email = "info@lad-tech.com"
14
+ gem.authors = ["Leon Davis"]
15
+ gem.add_dependency "watir-webdriver"
16
+ end
17
+
18
+ Jeweler::RubygemsDotOrgTasks.new
19
+
20
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.8
@@ -0,0 +1,21 @@
1
+ module PageMagic
2
+ module AjaxSupport
3
+ def on_element_with_trigger css_selector, event, browser, &block
4
+ set_variable_on_event(browser, css_selector, event)
5
+ yield
6
+ wait_until(:timeout_after => 20.seconds, :retry_every => 1.second) do
7
+ event_triggered(browser, event)
8
+ end
9
+ end
10
+
11
+ def event_triggered(browser, variable)
12
+ browser.execute_script("return #{variable}_triggered")
13
+ end
14
+
15
+ def set_variable_on_event(browser, element, event)
16
+ variable_name="#{event}_triggered"
17
+ browser.execute_script("#{variable_name}=false")
18
+ browser.execute_script("$('#{element}').on('#{event}', function(){#{variable_name}=true})")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module PageMagic
2
+ module Browser
3
+ class << self
4
+ attr_reader :browser
5
+ attr_accessor :session
6
+
7
+ def use browser
8
+ @browser = browser
9
+ end
10
+ end
11
+
12
+ def browser
13
+ Browser.session ||= PageMagic.session(Browser.browser || :chrome)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,81 @@
1
+ module PageMagic
2
+
3
+ class ElementMissingException < Exception
4
+
5
+ end
6
+
7
+ class ElementContext
8
+
9
+ attr_reader :caller, :page_element
10
+
11
+ def initialize page_element, browser, caller, *args
12
+ @page_element = page_element
13
+ @browser = browser
14
+ @caller = caller
15
+ end
16
+
17
+ def method_missing method, *args
18
+ return @caller.send(method, *args) if @executing_hooks
19
+ return @page_element.send(method, *args) if @page_element.methods.include?(method)
20
+
21
+
22
+ element_locator_factory = @page_element.element_definitions[method]
23
+
24
+ action = nil
25
+ unless element_locator_factory
26
+ action = resolve_action(method)
27
+ element_locator_factory = @page_element.element_definitions[method.to_s.gsub("#{action}_", '').to_sym]
28
+ end
29
+
30
+ raise ElementMissingException, "Could not find: #{method}" unless element_locator_factory
31
+
32
+ if args.empty?
33
+ element_locator = element_locator_factory.call(@page_element, nil)
34
+ else
35
+ element_locator = element_locator_factory.call(@page_element, *args)
36
+ end
37
+
38
+ result = element_locator.locate
39
+
40
+ return element_locator if element_locator.class.is_a? PageSection
41
+
42
+ [:set, :select_option, :unselect_option, :click].each do |action_method|
43
+ apply_hooks(page_element: result,
44
+ capybara_method: action_method,
45
+ before_hook: element_locator.before_hook,
46
+ after_hook: element_locator.after_hook,
47
+ )
48
+ end
49
+
50
+ action ? result.send(action) : result
51
+ end
52
+
53
+ def resolve_action(field)
54
+ {/^click_/ => :click}.each do |prefix, action|
55
+ return action if field.to_s =~ prefix
56
+ end
57
+ end
58
+
59
+ def apply_hooks(options)
60
+ _self = self
61
+ page_element, capybara_method = options[:page_element], options[:capybara_method]
62
+
63
+ original_method = page_element.method(capybara_method)
64
+
65
+ page_element.define_singleton_method capybara_method do |*arguments, &block|
66
+ _self.call_hook &options[:before_hook]
67
+ original_method.call *arguments, &block
68
+ _self.call_hook &options[:after_hook]
69
+ end
70
+ end
71
+
72
+
73
+ def call_hook &block
74
+ @executing_hooks = true
75
+ result = self.instance_exec @browser, &block
76
+ @executing_hooks = false
77
+ result
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,99 @@
1
+ require 'watir-webdriver'
2
+ module PageMagic
3
+
4
+ class UnsupportedSelectorException < Exception
5
+
6
+ end
7
+
8
+ class PageElement
9
+
10
+ class MissingLocatorOrSelector < Exception
11
+ end
12
+
13
+ include PageElements
14
+ include AjaxSupport
15
+
16
+ module ::Watir
17
+ SelectList = Select
18
+ end
19
+
20
+ attr_reader :type, :name, :selector, :before_hook, :after_hook, :browser_element, :locator
21
+
22
+ class << self
23
+ def default_before_hook
24
+ @default_before_hook ||= Proc.new {}
25
+ end
26
+
27
+ def default_after_hook
28
+ @default_after_hook ||= Proc.new {}
29
+ end
30
+ end
31
+
32
+ def initialize name, parent_page_element, type=nil, selector=nil, &block
33
+ @parent_page_element = parent_page_element
34
+ if selector
35
+ @browser_element = parent_page_element.browser_element
36
+ else
37
+ @browser_element = parent_page_element
38
+ end
39
+
40
+ @type = type
41
+ @name = name.downcase.to_sym
42
+ @selector = selector
43
+
44
+ @before_hook, @after_hook = self.class.default_before_hook, self.class.default_after_hook
45
+ instance_eval &block if block_given?
46
+ end
47
+
48
+ def session
49
+ @parent_page_element.session
50
+ end
51
+
52
+ def before &block
53
+ @before_hook = block
54
+ end
55
+
56
+ def after &block
57
+ @after_hook = block
58
+ end
59
+
60
+ def locate *args
61
+ if @selector && @selector.is_a?(Hash)
62
+ method, selector = @selector.to_a.flatten
63
+ case method
64
+ when :id
65
+ @browser_element.find("##{selector}")
66
+ when :name
67
+ @browser_element.find("*[name='#{selector}']")
68
+ when :label
69
+ @browser_element.find_field(selector)
70
+ when :text
71
+ if @type == :link
72
+ @browser_element.find_link(selector)
73
+ elsif @type == :button
74
+ @browser_element.find_button(selector)
75
+ else
76
+ raise UnsupportedSelectorException
77
+ end
78
+ when :css
79
+ @browser_element.find(:css, selector)
80
+ else
81
+ raise UnsupportedSelectorException
82
+
83
+ end
84
+ else
85
+ @browser_element
86
+ end
87
+ end
88
+
89
+
90
+ def == page_element
91
+ page_element.is_a?(PageElement) &&
92
+ @type == page_element.type &&
93
+ @name == page_element.name &&
94
+ @selector == page_element.selector
95
+ @before_hook == page_element.before_hook &&
96
+ @after_hook == page_element.after_hook
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,91 @@
1
+ module PageMagic
2
+ module PageElements
3
+ class InvalidElementNameException < Exception
4
+ end
5
+
6
+ class InvalidMethodNameException < Exception
7
+ end
8
+
9
+ def self.extended clazz
10
+ clazz.class_eval do
11
+ attr_reader :browser_element
12
+
13
+ def elements browser_element, *args
14
+ self.class.elements browser_element, *args
15
+ end
16
+
17
+ def element_definitions
18
+ self.class.element_definitions
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ def method_added method
25
+ raise InvalidMethodNameException, "method name matches element name" if element_definitions[method]
26
+ end
27
+
28
+
29
+
30
+ def elements(browser_element, *args)
31
+ element_definitions.values.collect { |definition| definition.call(browser_element, *args) }
32
+ end
33
+
34
+ def elements?
35
+ !element_definitions.empty?
36
+ end
37
+
38
+ ELEMENT_TYPES = [:element, :text_field, :button, :link, :checkbox, :select_list, :radios, :textarea]
39
+ ELEMENT_TYPES.each do |field|
40
+ define_method field do |*args, &block|
41
+ name, selector = args
42
+ add_element_definition(name) do |browser_element|
43
+ case selector
44
+ when Hash, NilClass
45
+ PageElement.new(name, browser_element, field, selector, &block)
46
+ else
47
+ PageElement.new(name, selector, field, nil, &block)
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+ def section *args, &block
55
+ case args.first
56
+ when Symbol
57
+ name, selector = args
58
+
59
+ add_element_definition(name) do |parent_browser_element, *args_for_section|
60
+ page_section = Class.new do
61
+ extend PageMagic::PageSection
62
+ end
63
+
64
+ page_section.parent_browser_element = parent_browser_element.browser_element
65
+ page_section.selector selector if selector
66
+
67
+ page_section.class_exec *args_for_section, &block
68
+ page_section.new(parent_browser_element, name, selector)
69
+ end
70
+ else
71
+ section_class, name, selector = args
72
+ add_element_definition(name) do |parent_browser_element|
73
+ section_class.new(parent_browser_element, name, selector)
74
+ end
75
+ end
76
+
77
+
78
+ end
79
+
80
+ def add_element_definition name, &block
81
+ raise InvalidElementNameException, "duplicate page element defined" if element_definitions[name]
82
+ raise InvalidElementNameException, "a method already exists with this method name" if instance_methods.find { |method| method == name }
83
+
84
+ element_definitions[name] = block
85
+ end
86
+
87
+ def element_definitions
88
+ @element_definitions ||= {}
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,70 @@
1
+ module PageMagic
2
+ attr_reader :browser, :session
3
+
4
+ include AjaxSupport
5
+
6
+ def initialize session=Capybara.current_session, options={}, &block
7
+ if session.is_a? Capybara::Session
8
+ @browser = session
9
+ else
10
+ @browser = session.browser
11
+ @session = session
12
+ end
13
+ @browser_element = @browser
14
+ navigate if options[:navigate_to_page]
15
+ block.call @browser if block
16
+ end
17
+
18
+ def title
19
+ @browser.title
20
+ end
21
+
22
+ def refresh
23
+ @browser.refresh
24
+ end
25
+
26
+ def current_path
27
+ @browser.current_path
28
+ end
29
+
30
+ def text_on_page? text
31
+ text().downcase.include?(text.downcase)
32
+ end
33
+
34
+ def window_exists? title
35
+ raise "implement me"
36
+ end
37
+
38
+ def accept_popup
39
+ raise "implement me"
40
+ end
41
+
42
+ def alert_present?
43
+ raise "implement me"
44
+ end
45
+
46
+ def text_in_popup? text
47
+ raise "implement me"
48
+ end
49
+
50
+ def visit
51
+ @browser.visit self.class.url
52
+ self
53
+ end
54
+
55
+ def click element
56
+ self.send(element.downcase.gsub(" ", "_").to_sym)
57
+ end
58
+
59
+ def text
60
+ @browser.text
61
+ end
62
+
63
+ def method_missing method, *args
64
+ ElementContext.new(self, @browser, self).send(method, *args)
65
+ end
66
+ end
67
+
68
+
69
+
70
+
@@ -0,0 +1,101 @@
1
+ module PageMagic
2
+ module PageSection
3
+
4
+ module Location
5
+ def locate_in browser_element, selector
6
+ method, selector = selector.to_a.flatten
7
+ case method
8
+ when :id
9
+ browser_element.find("##{selector}")
10
+ when :css
11
+ browser_element.find(:css, selector)
12
+ else
13
+ raise UnsupportedSelectorException
14
+ end
15
+ end
16
+ end
17
+ class UndefinedSelectorException < Exception
18
+
19
+ end
20
+
21
+ class << self
22
+ def extended clazz
23
+ clazz.extend(PageElements)
24
+ clazz.class_eval do
25
+ attr_reader :name, :selector
26
+ class << self
27
+ def selector selector=nil
28
+ return @selector unless selector
29
+
30
+ if @parent_browser_element
31
+ @browser_element = locate_in @parent_browser_element, selector
32
+ end
33
+
34
+ @selector = selector
35
+ end
36
+
37
+ attr_accessor :parent_browser_element, :browser_element
38
+ end
39
+
40
+ include Location
41
+ extend Location
42
+
43
+ def initialize parent_page_element, name=nil, selector=self.class.selector
44
+
45
+ @selector = selector
46
+ @parent_page_element = parent_page_element
47
+
48
+ @selector = selector ? selector : self.class.selector
49
+
50
+ raise UndefinedSelectorException, "Pass a selector to the constructor/define one the class" unless @selector
51
+ @browser_element = locate_in(@parent_page_element.browser_element, @selector)
52
+ if name
53
+ @name = name
54
+ else
55
+ @name = underscore(self.class.name).to_sym
56
+ end
57
+
58
+ end
59
+
60
+
61
+ def session
62
+ @parent_page_element.session
63
+ end
64
+
65
+ # TODO test this
66
+ def locate *args
67
+ @browser_element
68
+ end
69
+
70
+ #TODO - consolidate this
71
+ def method_missing method, *args
72
+ begin
73
+ ElementContext.new(self, @browser_element, self, *args).send(method, args.first)
74
+ rescue ElementMissingException
75
+ begin
76
+ @browser_element.send(method, *args)
77
+ rescue
78
+ super
79
+ end
80
+
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def underscore string
87
+ string.gsub(/::/, '/').
88
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
89
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
90
+ tr("-", "_").
91
+ downcase
92
+ end
93
+
94
+
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+ end
101
+ end
@@ -0,0 +1,28 @@
1
+ module PageMagic
2
+ class Session
3
+ attr_reader :browser
4
+ attr_accessor :current_page
5
+
6
+ include WaitUntil
7
+
8
+ def initialize browser
9
+ @browser = browser
10
+ end
11
+
12
+ def visit page
13
+ @browser.visit page.url
14
+ @current_page = page.new self
15
+ self
16
+ end
17
+
18
+ def move_to page_class
19
+ page_class = eval(page_class) if page_class.is_a?(String)
20
+ @current_page = page_class.new self
21
+ wait.until { browser.current_url == page_class.url }
22
+ end
23
+
24
+ def method_missing name, *args, &block
25
+ @current_page.send(name, *args, &block)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module PageMagic
2
+ module WaitUntil
3
+ def wait
4
+ @wait ||= Wait.new
5
+ end
6
+ end
7
+ end