puppet7 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ == puppet7
data/README ADDED
@@ -0,0 +1,80 @@
1
+ == puppet7
2
+
3
+ === Introduction
4
+ Puppet7 is page object library for selenium web testing.
5
+ it's integrated RSpec2. You can write ui test code first, and then
6
+ define page objects later.
7
+
8
+ When undefined page object makes error, Puppet7 treats it pending result of rspec (yellow).
9
+
10
+
11
+ === Quick start
12
+
13
+ You can start to write web page action and validation codes first.
14
+
15
+ require "puppet7"
16
+ describe_domain :MySite do
17
+ describe :mainPage do
18
+ it "should have search input and button" do
19
+ # all it blocks are executed in domain context
20
+ mainPage.open do # page context block
21
+ searchInput.should be_visible
22
+ searchButton.should be_visible
23
+ end
24
+ end
25
+ it "should do that you can search something." do
26
+ mainPage.open do
27
+ searchInput.type "keyword"
28
+ searchButton.click
29
+ end
30
+ searchResultPage do
31
+ should be_current
32
+ searchInput.text.should == "keyword"
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ 'describe_domain' is kind of a magic keyword. It'll initialize selenium session and page objects
39
+ and it make you be able to write rspec test in page object context.
40
+
41
+ Then you run this test by rspec, you can get report like below.
42
+
43
+ PENDING: 'MySite' is undefined domain!
44
+
45
+ Now you define page objects.
46
+
47
+ def_domain :MySite do
48
+ baseurl "http://my_so_cool_site.com"
49
+ page :mainPage
50
+ page :searchResultPage
51
+ end
52
+
53
+ def_page :mainPage do
54
+ url "/"
55
+ end
56
+
57
+ def_page :searchResultPage do
58
+ url "/search"
59
+ end
60
+
61
+ Run again rspec test. Maybe you can get report like below.
62
+
63
+ PENDING: 'searchInput' is undefined page action or ui element!
64
+
65
+ Ok. It's not bad. Let's define ui element in page definition.
66
+
67
+ def_page :mainPage do
68
+ url "/"
69
+ element :searchInput, ""
70
+ element :searchButton, ""
71
+ end
72
+
73
+ Run again! You can get a report.
74
+
75
+ ERROR in UI['mainPage.searchInput'] ...
76
+
77
+ Like above, you can write ui testing code step by step.
78
+ And more importantly you can write ui testing scenarios without web page.
79
+ RSpec'll generate cool document as test result, this document is a good checklist as acceptance criteria.
80
+
@@ -0,0 +1,59 @@
1
+ # coding:utf-8
2
+
3
+ require "puppet7/puppet"
4
+ require "puppet7/exceptions"
5
+ module Puppet7
6
+ module KernelExtension
7
+ # pending method for rspec. (used internally)
8
+ def do_pending msg, ex_klass=PuppetException
9
+ if Puppet.puppet_testing
10
+ throw :pending_declared_in_example, msg
11
+ else
12
+ raise ex_klass.new msg
13
+ end
14
+ end
15
+
16
+ # load page definitions. (relative path based current file path)
17
+ #
18
+ # example
19
+ # load_pages "pages" # load all page definitions in "./pages"
20
+ #
21
+ # def_domain :naver do
22
+ # ...
23
+ # end
24
+ def load_pages rel_path="pages"
25
+ def __loading_rubyfiles dir
26
+ Dir.new(dir).each do |filename|
27
+ next if filename == '.' or filename == '..'
28
+ file = File.join(dir, filename)
29
+ if File.directory?(file)
30
+ __loading_rubyfiles file
31
+ elsif file.end_with?(".rb")
32
+ require file
33
+ end
34
+ end
35
+ end
36
+ caller_file = caller[0].gsub /:\d+:in.*$/, ""
37
+ __loading_rubyfiles File.join(File.dirname(caller_file), rel_path)
38
+ end
39
+
40
+
41
+ # return a class given name(e.g. 'MyModule::MyClass')
42
+ def class_by_name module_name, &block
43
+ klass = Kernel
44
+ module_name.split("::").each do |name|
45
+ if klass.const_defined?(name.to_sym)
46
+ klass = klass.const_get(name.to_sym)
47
+ else
48
+ klass = nil
49
+ break
50
+ end
51
+ end
52
+ if klass and block_given?
53
+ klass.class_eval &block
54
+ end
55
+ klass
56
+ end
57
+ end
58
+ end
59
+ include Puppet7::KernelExtension
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+
3
+ module Puppet7
4
+ class Configuration
5
+ attr_accessor :js_ext_files, :use_report, :report_file
6
+
7
+ @@selenium_default_configs = {
8
+ :host => 'localhost',
9
+ :port => 4444,
10
+ :browser => '*firefox',
11
+ :timeout_in_seconds => 15,
12
+ :highlight_located_element => true
13
+ }.freeze
14
+
15
+ def initialize
16
+ @js_ext_files = []
17
+ @use_report = false
18
+ @report_file = "report.html"
19
+ @selenium_configs = @@selenium_default_configs.clone
20
+ end
21
+
22
+ def selenium_configs
23
+ @selenium_configs
24
+ end
25
+
26
+ def selenium_configs= configs
27
+ @selenium_configs.merge!(configs)
28
+ end
29
+ end
30
+
31
+ def self.configuration
32
+ @configuration ||= Puppet7::Configuration.new
33
+ end
34
+
35
+ def self.configure
36
+ yield configuration if block_given?
37
+ end
38
+ end
@@ -0,0 +1,136 @@
1
+ # coding: utf-8
2
+
3
+ require "puppet7/puppet"
4
+ require "puppet7/page"
5
+ require "puppet7/page_part"
6
+ require "puppet7/utils"
7
+ require "puppet7/exceptions"
8
+ require "puppet7/puppet_report"
9
+
10
+ module Puppet7
11
+ class Domain < Puppet
12
+ attr_accessor :baseurl, :encoding, :pages, :selenium
13
+ include Puppet7::Utils
14
+
15
+ class << self
16
+ include Puppet7::Utils
17
+ def baseurl baseurl
18
+ self.class_eval do
19
+ @__baseurl = parse_url(baseurl).baseurl
20
+ end
21
+ end
22
+
23
+ def encoding encoding
24
+ self.class_eval do
25
+ @__encoding = encoding
26
+ end
27
+ end
28
+
29
+ def page page_name
30
+ self.class_eval do
31
+ @__pages ||= []
32
+ @__pages << page_name.to_sym
33
+ end
34
+ end
35
+
36
+ def new *args
37
+ # init instance
38
+ instance = super
39
+ instance.instance_eval do
40
+ @pages = {}
41
+ end
42
+ # init instance vars.
43
+ self.class_eval do
44
+ @__baseurl ||= nil
45
+ @__encoding ||= "UTF-8"
46
+ @__pages ||= []
47
+ instance.baseurl = @__baseurl
48
+ instance.encoding = @__encoding
49
+ @__pages.each do |page_name|
50
+ begin
51
+ instance.pages[page_name.to_sym] = Page.get_instance(page_name, instance)
52
+ make_page_accessor page_name
53
+ rescue NotDefinedException
54
+ make_pending_page_accessor page_name
55
+ end
56
+ end
57
+ end
58
+ # check vars
59
+ if instance.baseurl == nil
60
+ Puppet7.report.add_undefined_object :domain, "#{name}.baseurl"
61
+ do_pending "Domain[#{instance.puppet_name}] need to define 'baseurl'!"
62
+ end
63
+ instance
64
+ end
65
+
66
+ private
67
+ def make_page_accessor page_name
68
+ self.class_eval <<-END
69
+ def #{page_name} &block
70
+ page = pages[:'#{page_name}']
71
+ if block_given?
72
+ @block_binding = block.binding
73
+ page.instance_eval &block
74
+ @block_binding = nil
75
+ end
76
+ page
77
+ end
78
+ END
79
+ end
80
+
81
+ def make_pending_page_accessor page_name
82
+ self.class_eval <<-END
83
+ def #{page_name} &block
84
+ Puppet7.report.add_undefined_object :page, "#{page_name}"
85
+ do_pending "'#{page_name}' page is Not defined!"
86
+ end
87
+ END
88
+ end
89
+ end
90
+
91
+ def baseurl= url
92
+ if url
93
+ @baseurl = parse_url(url).baseurl
94
+ else
95
+ @baseurl = nil
96
+ end
97
+ end
98
+
99
+ def current_url_info
100
+ parse_url(selenium.location)
101
+ end
102
+
103
+ def page_title
104
+ selenium.title.force_encoding(encoding)
105
+ end
106
+
107
+ def selenium
108
+ if @selenium == nil
109
+ do_pending "Selenium is Not running!"
110
+ else
111
+ @selenium
112
+ end
113
+ end
114
+
115
+ def selenium_init?
116
+ @selenium != nil and @selenium.session_started?
117
+ end
118
+
119
+ def method_missing sym, *args, &b
120
+ Puppet7.report.add_undefined_object :page, sym
121
+ do_pending "'#{sym}' is Not defined Domain Action or it is Not defined Page!"
122
+ end
123
+
124
+ def image_ok? img_url
125
+ selenium.run_script <<-EOF
126
+ var __img = new Image();
127
+ var __img_check_result = "'CANNOT CHECKING'";
128
+ __img.onerror= function(){__img_check_result = false;};
129
+ __img.onload= function(){__img_check_result = true;};
130
+ __img.src = '#{img_url}';
131
+ EOF
132
+ sleep 1 # wait for check img
133
+ eval(selenium.get_eval('window.__img_check_result;'))
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,62 @@
1
+ # coding: utf-8
2
+
3
+ require "puppet7/selenium_locator_actions"
4
+
5
+ module Puppet7
6
+ class Element
7
+ include Puppet7::SeleniumLocatorActions
8
+
9
+ attr_reader :parent, :name, :nclick, :xpath
10
+ def initialize parent, name, xpath=:pending, nclick=:pending
11
+ @parent = parent
12
+ @name = name.to_sym
13
+ @xpath = xpath
14
+ end
15
+
16
+ def image_ok?
17
+ img_url = attr("src")
18
+ @parent.image_ok? img_url
19
+ end
20
+
21
+ def each
22
+ if count == 1
23
+ yield self
24
+ elsif count > 1
25
+ count.times do |idx|
26
+ yield Element.new(@parent, "#{@name}_#{idx}", _xpath_list_access(idx), @nclick)
27
+ end
28
+ end
29
+ end
30
+
31
+ def [] idx
32
+ Element.new(@parent, "#{@name}_#{idx}", _xpath_list_access(idx), @nclick)
33
+ end
34
+
35
+ def child rel_xpath
36
+ Element.new(@parent, "#{name}_#{rel_xpath}", "#{@xpath}#{rel_xpath}", @nclick)
37
+ end
38
+
39
+ def fullname
40
+ "#{@parent.fullname}.#{@name}"
41
+ end
42
+
43
+
44
+ private
45
+ def selenium
46
+ if @xpath == :pending
47
+ try_pending "#{fullname} has no xpath!"
48
+ end
49
+ begin
50
+ return yield(@parent.selenium)
51
+ rescue => err
52
+ raise "Error in UI[#{fullname}] : #{err}"
53
+ end if block_given?
54
+ @parent.selenium
55
+ end
56
+
57
+ def _xpath_list_access idx
58
+ xpath_list = @xpath.sub(/^xpath=/, "")
59
+ "xpath=(#{xpath_list})[#{idx +1}]"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,17 @@
1
+ # coding:utf-8
2
+
3
+ module Puppet7
4
+ # Exception for all puppet7 error
5
+ class PuppetException < Exception;end
6
+
7
+ # Exception for using not defined Puppets
8
+ class NotDefinedException < PuppetException ;end
9
+
10
+ # Exception for duplicated Elements.
11
+ class DuplicatedUIElementException < PuppetException ;end
12
+ class DuplicatedPagePartException < PuppetException ;end
13
+
14
+ # Exception for error while initializing selenium.
15
+ class SeleniumException < PuppetException ;end
16
+ class SeleniumInitError < SeleniumException ;end
17
+ end
@@ -0,0 +1,173 @@
1
+ # coding:utf-8
2
+
3
+ require "puppet7/common"
4
+ require "puppet7/page_part"
5
+ require "puppet7/page"
6
+ require "puppet7/domain"
7
+ require "puppet7/exceptions"
8
+
9
+ module Puppet7
10
+ module KernelExtension
11
+ # Define a PagePart.
12
+ #
13
+ # Create a subclass of PagePart given name, and then evaluation given block in the class context.
14
+ #
15
+ # - example.
16
+ # def_part :header do
17
+ # element :logo, by_xpath('//*[id="logo"]')
18
+ # part :gnb
19
+ # end
20
+ #
21
+ def def_part part_name, &block
22
+ PagePart.subclass(part_name).class_eval &block
23
+ end
24
+
25
+ # Define a Page
26
+ def def_page page_name, &block
27
+ Page.subclass(page_name.to_sym).class_eval &block if block
28
+ end
29
+
30
+ # Define a Domain
31
+ def def_domain domain_name, &block
32
+ Domain.subclass(domain_name.to_sym).class_eval &block if block
33
+ end
34
+
35
+ # initialze domain.
36
+ #
37
+ # - create domain object and pages...
38
+ # - initialize selenium session.
39
+ # - execute code block and finish domain if given block.
40
+ #
41
+ def begin_domain domain_name, &block
42
+ Puppet.begin_testing
43
+ if Domain.subclass?(domain_name)
44
+ # create domain
45
+ @domain = Domain.get_instance(domain_name)
46
+ # init selenium
47
+ begin
48
+ @domain.selenium = SeleniumSupport.init_selenium :url => @domain.baseurl
49
+ rescue SeleniumInitError => err # ignore exception
50
+ STDERR.puts "#{err}"
51
+ end
52
+ begin
53
+ yield @domain
54
+ rescue => err
55
+ STDERR.puts "#{err}"
56
+ end if block_given?
57
+ @domain
58
+ else
59
+ Puppet7.report.add_undefined_object :domain, domain_name
60
+ do_pending "Not Defined Domain : #{domain_name}"
61
+ end
62
+ end
63
+
64
+ # end selenium session of domain initialized.
65
+ def end_domain
66
+ Puppet.end_testing
67
+ return unless @domain and @domain.selenium_init?
68
+ sleep 1; # delay for image error checking
69
+
70
+ # get error images
71
+ image_errors = @domain.selenium.get_stored_var :imageErrors;
72
+ image_errors.each do |page, images|
73
+ Puppet7.report.add_image_errors(page, images)
74
+ end
75
+
76
+ # get js errors
77
+ js_errors = @domain.selenium.get_stored_var :jsErrors;
78
+ js_errors.each do |page, errs|
79
+ Puppet7.report.add_js_errors(page, errs)
80
+ end
81
+ @domain.selenium.stop
82
+ end
83
+
84
+
85
+
86
+ # get database accessor
87
+ def get_dbaccessor dbi_url, user, passwd, dir
88
+ sqlexec = Puppet7::SQLExec.new dbi_url, user, passwd
89
+ caller_file = caller[0].gsub /:\d+:in.*$/, ""
90
+ sqldir = File.join(File.dirname(caller_file), dir)
91
+ Puppet7::SQLAgent.new(sqlexec, sqldir)
92
+ end
93
+
94
+ # it is shortcut for begin_domain .. end_domain.
95
+ #
96
+ # It supply domain object and domain context when you write tests with rspec.
97
+ #
98
+ # example
99
+ # describe_domain :naver do
100
+ # describe "main_page" do
101
+ # it "should have search input and search button." do
102
+ # # you could write test in domain context.
103
+ # main_page.open do
104
+ # # you could write code in page context in code block of page
105
+ # search_input.should be_visible?
106
+ # search_button.should be_visible?
107
+ # end
108
+ # end
109
+ # it "should move login page, when click login button." do
110
+ # main_page.open.login_button.click
111
+ # login_page do
112
+ # # page block checks url automatically
113
+ # # if current location is not login page, error occurrs.
114
+ # user_input.should be_visible
115
+ # passwd_input.should be_visible
116
+ # end
117
+ # end
118
+ # end
119
+ # end
120
+ def describe_domain domain_name, *args, &block
121
+ describe "#{domain_name}", *args do
122
+ before(:each) do
123
+ begin_domain domain_name.to_sym
124
+ end
125
+ after(:each) do
126
+ end_domain
127
+ end
128
+ self.instance_exec &block
129
+ end
130
+ end
131
+ end
132
+ end
133
+ include Puppet7::KernelExtension
134
+
135
+ # hook method_missing of RSpec Example group.
136
+ #
137
+ # it is able to write test in domain context.
138
+ class_by_name "RSpec::Core::ExampleGroup" do
139
+ def method_missing sym, *args, &block
140
+ begin # first try super
141
+ super
142
+ rescue # hook later...
143
+ @domain.send sym, *args, &block
144
+ end
145
+ end
146
+ end
147
+
148
+ # hook example
149
+ class_by_name "RSpec::Core::Example" do
150
+ alias_method :__run_before_each, :run_before_each
151
+ def run_before_each
152
+ __run_before_each
153
+ begin
154
+ desc = self.description
155
+ @example_group_instance.instance_eval do
156
+ @domain.selenium.set_stored_var :testing_message, desc
157
+ end
158
+ rescue
159
+ end if @example_group_instance.instance_eval("@domain && @domain.selenium_init?")
160
+ end
161
+ end
162
+
163
+ class Array
164
+ def each_with *other_arrays
165
+ other_arrays.each do |arr|
166
+ raise "Error: not same array length." if arr.length != length
167
+ end
168
+
169
+ each_index do |i|
170
+ yield self[i], *(other_arrays.collect {|arr| arr[i]})
171
+ end
172
+ end
173
+ end