page_magic 0.8.8

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