puppet7 0.2.0.beta1

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/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