web4cucumber 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,250 @@
1
+ The main filosophy behind this project is:
2
+ Separate the data from the logic as much as you possibly can. Have you had to
3
+ go through a tremendous ruby module with hundreds of methods which click
4
+ buttons, input text in form fields, randomly wait (probably, till the page is
5
+ loaded) and randomly take screenshots? Have you dreamed to have a way to store
6
+ test data in a separate simple text files and feed them to the cucumber?
7
+ Or maybe you have hundreds of scenarios looking like this:
8
+ ```
9
+ When I click the element ":id=>'click_me'"
10
+ And I write "hello" in the form with ":id=>'topic'"
11
+ And I write "Blahblahblah" in the form with ":id=>'body'"
12
+ And I click the element ":id=>'submit'"
13
+ Then the page shuold contain "Success"
14
+ ```
15
+ And you sometimes ask yourselves "Is it a cucumber way to write scenarios like
16
+ this?" Well the answer is no. Probably, all you need is to write a couple of
17
+ steps instead:
18
+ ```
19
+ When I create a blogpost with:
20
+ |title|body|
21
+ |hello|Blahblahblah|
22
+ Then the step should succeed
23
+ ```
24
+ And have a simple yaml file describing the corresponding webpage, that would
25
+ look like this:
26
+ ```
27
+ blogpost_create:
28
+ pages:
29
+ - 'blogpost_create_page'
30
+ blogpost_create_page:
31
+ url: '/blogposts/new'
32
+ expected_fields:
33
+ title:
34
+ type: 'textfield'
35
+ selector:
36
+ id: 'topic'
37
+ body:
38
+ type: 'textfield'
39
+ selector:
40
+ id: 'body'
41
+ commit:
42
+ selector:
43
+ id: 'commit'
44
+ ```
45
+ This library implements an abstraction layer between cucumber logic and Watir
46
+ webdriver. Using this library you no longer need to write the Watir-aware Ruby
47
+ code for low-level browser interaction. Instead you describe your pages and
48
+ actions to be performed in Web UI of your product in simple yaml-formatted
49
+ files. This approach allows you to separate the application data (html
50
+ properties of page elements) from test logic.
51
+
52
+ How to use this?
53
+
54
+ - Add `gem 'Web4Cucumber'` to you Gemfile and run `bundle install`
55
+ - In your project in one of your lib/*.rb files add `require "Web4Cucumber"` and inherit your own class from Web4Cucumber one, like this:
56
+ ```
57
+ class Web < Web4Cucumber
58
+ def initialize(options)
59
+ super(options)
60
+ # Add here some code specific to your project, like
61
+ # @@logged_in = false
62
+ end
63
+ ```
64
+
65
+ - In your features/support/env.rb in Before hook instatiate your beautiful class:
66
+ ```
67
+ options = {
68
+ :base_url => "http://base_url_of_your_project",
69
+ :browser => :firefox, # or :chrome. Other browsers are not supported yet
70
+ :rules_path => "path_to_the_folder_with_your_yaml_files",
71
+ :logger => @logger
72
+ # You need to pass an object that will do the logging for you. I believe you have it implemented.
73
+ # If not, please take a look in the examples/testproject folder.
74
+ }
75
+ @web = Web.new(options)
76
+
77
+ ```
78
+ From now on you will have @web, an instance of Web4Cucumber class with a bunch of convenient methods for high-level interactions with the browser, and, a browser running by default in the headless mode. For development and debugging purposes, however I recommend setting DEBUG_WEB environmental variable to *true* so that you will be presented with a visible browser window.
79
+
80
+ - Once this preparation is done, let's try to understand how to write yaml files. The key concept Web4Cucumber is built around is an *action*.
81
+ Action is any set of user actions you want to automate, it could be web search, form submissions, data upload etc. An action is described in a yaml file with the following structure:
82
+
83
+ ```
84
+ our_test_action:
85
+ pages:
86
+ - 'first_page'
87
+ - 'second_page'
88
+ final_checkpoints:
89
+ alert_success:
90
+ selector:
91
+ text: 'You have successfully performed whatever you intended'
92
+
93
+ first_page:
94
+ url: '/some_relative_path/testme'
95
+ expected_fields:
96
+ field_one_on_page_one:
97
+ type: textfield
98
+ selector:
99
+ id: 'i-am-field-1'
100
+ field_two_on_page_one:
101
+ type: textfield
102
+ selector:
103
+ class: 'generic-field-2-class'
104
+ checkpoints:
105
+ sometext:
106
+ selector:
107
+ text: 'I am text one on page one'
108
+ someothertext:
109
+ selector:
110
+ text: 'I am text two on page one'
111
+ commit:
112
+ selector:
113
+ text: 'Click me'
114
+
115
+ second_page:
116
+ sleep 2 # wait 2 seconds till the page is loaded
117
+ expected_fields:
118
+ field_one_on_page_two:
119
+ type: filefield
120
+ selector:
121
+ id: 'upload-something'
122
+ field_two_on_page_two:
123
+ type: textfield
124
+ selector:
125
+ xpath '//*[@id="fancy_something"]/span'
126
+ def_value: 'If you dont pass :field_two_on_page_two value, this text will go there'
127
+ checkpoints:
128
+ sometext:
129
+ selector:
130
+ text: 'I am text one on page two'
131
+ someothertext:
132
+ selector:
133
+ text: 'I am text two on page two'
134
+ negative_checkpoints:
135
+ error_message:
136
+ selector:
137
+ text: 'I am a critical error message! Wish you never see me on this page!'
138
+ commit:
139
+ scroll: true
140
+ # sometimes the element you need is outside the area of the virtual viewport
141
+ # so webdriver is unable to interact with it. Use this keyword to execute a simple
142
+ # javascript *scroll_into_view* function
143
+ selector:
144
+ text: 'Click me too'
145
+ ```
146
+ If you take a closer look at this yaml structure, you'll notice that it describes describes two web pages pages. The first one is accessed through relative url "/some_relative_path/testme" that is being appended to your project's base_url (used during Web initialization).
147
+ The second page is accessed by clicking the "Click me" button on the first page. The yaml file describes 2 textfields on first page and one textfield and one filefield on the second page. It also describes checkpoints - elements whose presence will be asserted during the action execution. We can provide default values for textfields right inside the yaml with the *def_value* keyword. Now the most interesting question: how do we use that?
148
+
149
+ - The whole workflow would look like this. You would create your step:
150
+
151
+ ```
152
+ When I run my beautiful action with:
153
+ |option |value |
154
+ |field_one_on_page_one |some text |
155
+ |field_two_on_page_one | some other text|
156
+ |field_one_on_page_two | lib/files/myfile|
157
+ # We remember that field_one_on_page_two is a filefield
158
+ # And we leave default value for second field on the page two.
159
+ ```
160
+ Then you would write your step definition:
161
+ ```
162
+ When /^I run my beautiful action with:$/ do |table|
163
+ options = {}
164
+ table.rows.each do |row|
165
+ options[row[0].to_sym] = row[1]
166
+ end
167
+ @result = @web.run_action(:our_test_action, options)
168
+ end
169
+ ```
170
+ As you can see, field_names declared in the yaml files, like *field_one_on_page_one* are used as keys in the hash, passed to the *@web.run_action* method as the second parameter. First parameter being the name of the action also declared in the yaml file.
171
+ If you then inspect the @result object then ideally you would get something like this:
172
+ ```
173
+ pp @result
174
+ {:result=>true,
175
+ :failed_positive_checkpoints=>[],
176
+ :failed_negative_checkpoints=>[],
177
+ :errors=>[]}
178
+
179
+ ```
180
+ If something went wrong and the webdriver was unable to find some checkpoints or other fields described in the yaml file, then the @result[:result] would be changed to *false* and @result[:failed_positive_checkpoints] array will be populated with the elements that were not found, @result[:failed_negative_checkpoints] - with elements you described in *negative_checkpoints* and @result[:errors] - with exception messages. Also, a *screenshots* folder will be created in your working directory, containing browser screenshot of the page that caught an error.
181
+
182
+ Now we have to master yaml file creation in details.
183
+ The structure of yaml files supported by Web4Cucumber library is quite flexible. You could store page descriptions separately from action descriptions, or you could store all your actions in one files, only make sure each action has a precide list of corresponding pages in exactly the same order that they appear during action execution. Besides list of pages only one keyword is supported under an action:
184
+ *final_checkpoints*. This is a list of elements whose presence we expect once the action is finished.
185
+
186
+ The key part of an *action* is a *page*. The following keywords can be used under the page description:
187
+ 1. expected_fields
188
+ 2. checkpoints
189
+ 3. negative_checkpoints
190
+ 4. links
191
+ 5. base_url
192
+ 6. url
193
+ 7. sleep
194
+ 8. commit
195
+
196
+ - *expected_fields* is a section where field descriptions go. Field structure will be discussed in more details later.
197
+ - *checkpoints*, as was mentioned above, is a list of named web elements you expect to be present on a web page.
198
+ - *negative_checkpoints* similarly a list of named elements you do not expect on a page.
199
+ - *links* Is a list of links on a page that need to be checked in a web crawler kind of way. For each link you specify the selector by which the link can be accessed and a number of checkpoints (web elements) you wish to check on a page accesible via that particular link. A typycal link section of a page would look like this:
200
+ ```
201
+ :links:
202
+ :getstarted:
203
+ :selector:
204
+ :text: "Get Started"
205
+ :checkpoints:
206
+ :getstarted:
207
+ :selector:
208
+ :text: "Getting Started"
209
+ :securitypolicy:
210
+ :selector:
211
+ :text: "Security Policy"
212
+ :checkpoints:
213
+ :securityinformation:
214
+ :selector:
215
+ :text: "Security Information"
216
+ ```
217
+ In this example webdriver would click first on a link with test "Get Started", check that the page has an element with text "Get Started", then get back, click on the link with text "Security Policy", check that the page has an element with text "Security Information", then again get back.
218
+ - *base_url* - needed when you need to perform some action on a third-party software (for integration cases for example)
219
+ - *url* - a relative url path of the page (base_url for your project is passed to the Web4Cucumber class during instance initialization)
220
+ You can have a variable part of relative url, embraced between angle brackets. For example, if you have "/blog/<blogtype>/new" url in your yaml file and then pass {:blogtype=>'public'} in your options to the @web.run_action method, the webdriver will access the /blog/public/new relative url.
221
+ - *sleep* - a sleep interval in seconds, how long to wait before doing anything on this page: useful for slow loading pages
222
+ - *commit* - well this is a commit button - the one you normally click to submit a form. The only tricky part about commit comes when the page presents you with a javascript popup. In this case the commit section would look like this:
223
+ ```
224
+ commit:
225
+ type: alert
226
+ ```
227
+ that's it!
228
+
229
+ The last thing needed to be mentioned is the structure of the *field* itself. The *field* is mapped to a key in *options* passed to the @web.run_action method through it's name. That means, you pass a text to the particular textfield in the following way:
230
+ 1. Describe the field in the yaml file
231
+ ```
232
+ expected_fields:
233
+ login:
234
+ type: 'textfield'
235
+ selector:
236
+ id: 'user-login'
237
+ ```
238
+ 2. pass the value:
239
+
240
+ ```@web.run_action(:some_action_name, {:login=>'username@example.com', <...other_options_go_here...>})```
241
+ That's it.
242
+ Different elemet types get treated differently by webdriver, textfields can not be clicked the way buttons do, so you need to provide element type. The following types are supported:
243
+ - select - a dropdown list. You need to pass the element value
244
+ - checkbox - self-explanatory
245
+ - radio - same here
246
+ - textfield - you can provide a text that will be inputted in this textfield
247
+ - textarea - self-explanatory
248
+ - filefield - provide a full path to the file you would upload here
249
+ - a -link
250
+ - element any element that can be simply clicked. No need to provide the type in this case, it will be implied by default.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+ gem 'json'
3
+ gem 'cucumber'
4
+ gem 'watir-webdriver'
5
+ gem 'web4cucumber'
6
+ gem 'rspec-expectations'
7
+ gem 'debugger'
8
+ gem 'byebug'
9
+ gem 'headless'
10
+ gem 'pry'
@@ -0,0 +1,59 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ builder (3.2.2)
5
+ childprocess (0.5.5)
6
+ ffi (~> 1.0, >= 1.0.11)
7
+ coderay (1.1.0)
8
+ columnize (0.8.9)
9
+ cucumber (1.3.19)
10
+ builder (>= 2.1.2)
11
+ diff-lcs (>= 1.1.3)
12
+ gherkin (~> 2.12)
13
+ multi_json (>= 1.7.5, < 2.0)
14
+ multi_test (>= 0.1.2)
15
+ debugger (1.6.8)
16
+ columnize (>= 0.3.1)
17
+ debugger-linecache (~> 1.2.0)
18
+ debugger-ruby_core_source (~> 1.3.5)
19
+ debugger-linecache (1.2.0)
20
+ debugger-ruby_core_source (1.3.5)
21
+ diff-lcs (1.2.5)
22
+ ffi (1.9.6)
23
+ gherkin (2.12.2)
24
+ multi_json (~> 1.3)
25
+ headless (1.0.2)
26
+ json (1.8.2)
27
+ method_source (0.8.2)
28
+ multi_json (1.10.1)
29
+ multi_test (0.1.2)
30
+ pry (0.9.12.6)
31
+ coderay (~> 1.0)
32
+ method_source (~> 0.8)
33
+ slop (~> 3.4)
34
+ rspec-expectations (2.14.4)
35
+ diff-lcs (>= 1.1.3, < 2.0)
36
+ rubyzip (1.1.7)
37
+ selenium-webdriver (2.44.0)
38
+ childprocess (~> 0.5)
39
+ multi_json (~> 1.0)
40
+ rubyzip (~> 1.0)
41
+ websocket (~> 1.0)
42
+ slop (3.5.0)
43
+ watir-webdriver (0.6.11)
44
+ selenium-webdriver (>= 2.18.0)
45
+ web4cuke (0.0.1)
46
+ websocket (1.2.1)
47
+
48
+ PLATFORMS
49
+ ruby
50
+
51
+ DEPENDENCIES
52
+ cucumber
53
+ debugger
54
+ headless
55
+ json
56
+ pry
57
+ rspec-expectations
58
+ watir-webdriver
59
+ web4cuke
@@ -0,0 +1,12 @@
1
+ Feature: example.feature
2
+ # We expect that you have an account at https://www.openshift.com/
3
+ # And that you have two environmental variables containing your
4
+ # Openshift login and password: $OPENSHIFT_LOGIN and $OPENSHIFT_PASSWORD
5
+ Scenario: Login to Openshift web console
6
+ Given I am logged in to OpenShift web console
7
+ Then the url should contain "app/account"
8
+ When I run my beautiful action with:
9
+ |option |value |
10
+ |field_one_on_page_one |some text |
11
+ |field_two_on_page_one | some other text|
12
+ |field_one_on_page_two| lib/files/myfile |
@@ -0,0 +1,23 @@
1
+ Given /^I am logged in to OpenShift web console$/ do
2
+ options = {}
3
+ options[:login] = ENV["OPENSHIFT_USER"]
4
+ options[:password] = ENV["OPENSHIFT_PASSWORD"]
5
+ @result = @web.login(options)
6
+ expect(@result[:result]).to be_true, "Failed to log in"
7
+ end
8
+
9
+ Then /^the url should contain "(.*?)"$/ do |urlpart|
10
+ url = @web.get_url
11
+ expect(url.include?(urlpart)).to be_true, "Failed to find #{urlpart} in page url"
12
+
13
+ end
14
+
15
+ When /^I run my beautiful action with:$/ do |table|
16
+ options = {}
17
+ table.rows.each do |row|
18
+ options[row[0].to_sym] = row[1]
19
+ end
20
+ @result = @web.run_action(:our_test_action, options)
21
+ end
22
+
23
+
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rspec/expectations'
3
+ LIB_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
4
+ $LOAD_PATH.unshift(LIB_PATH)
5
+ require "web"
6
+ require "log"
7
+
8
+ Before do |scenario|
9
+ options = {
10
+ :base_url => "https://openshift.com",
11
+ :browser => :firefox,
12
+ :rules_path => File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'web')),
13
+ :logger => Log.new
14
+ }
15
+ @web = Web.new(options)
16
+ end
17
+
18
+ After do |scenario|
19
+ @web.finalize
20
+ end
@@ -0,0 +1,17 @@
1
+ class Log
2
+ def initialize
3
+ end
4
+
5
+ def info(text)
6
+ puts text
7
+ end
8
+
9
+ def warn(text)
10
+ puts text
11
+ end
12
+
13
+ def error(text)
14
+ puts text
15
+ end
16
+
17
+ end
@@ -0,0 +1,19 @@
1
+ require "web4cucumber"
2
+
3
+ class Web < Web4Cucumber
4
+ def initialize(options)
5
+ super(options)
6
+ @@logged_in = false
7
+ end
8
+
9
+ def login(options)
10
+ unless options.keys == [:login, :password]
11
+ raise "Please povide both :login and :password options"
12
+ end
13
+ result = run_action(:login, options)
14
+ if result[:result] == true
15
+ @@logged_in = true
16
+ end
17
+ return result
18
+ end
19
+ end
@@ -0,0 +1,117 @@
1
+ :check_community_header:
2
+ :pages:
3
+ - "check_community_header_page"
4
+ :check_community_header_page:
5
+ :url: '/community'
6
+
7
+ :checkpoints:
8
+ :products:
9
+ :selector:
10
+ :text: "Products"
11
+ :getinvolved:
12
+ :selector:
13
+ :text: "Get Involved"
14
+ :devcenter:
15
+ :selector:
16
+ :text: "Developer Center"
17
+ :support:
18
+ :selector:
19
+ :text: "Support"
20
+ :partners:
21
+ :selector:
22
+ :text: "Partners"
23
+ :community:
24
+ :selector:
25
+ :text: "Community"
26
+ :connect:
27
+ :selector:
28
+ :text: "Connect"
29
+ :about:
30
+ :selector:
31
+ :text: "About"
32
+ :getstarted:
33
+ :selector:
34
+ :text: "Get Started"
35
+ :status:
36
+ :selector:
37
+ :text: "Status"
38
+ :stackoverflow:
39
+ :selector:
40
+ :text: "Stack Overflow"
41
+ :premiumplans:
42
+ :selector:
43
+ :text: "Premium Plans"
44
+ :events:
45
+ :selector:
46
+ :text: "Events"
47
+ :blog:
48
+ :selector:
49
+ :text: "Blog"
50
+ :securitypolicy:
51
+ :selector:
52
+ :text: "Security Policy"
53
+ :openshiftonirc:
54
+ :selector:
55
+ :text: "#openshift on IRC"
56
+ :newslettersignup:
57
+ :selector:
58
+ :text: "Join our Newsletter"
59
+ :careers:
60
+ :selector:
61
+ :text: "Careers"
62
+ :aboutredhat:
63
+ :selector:
64
+ :text: "About Red Hat"
65
+ :partnerprograms:
66
+ :selector:
67
+ :text: "Partner Programs"
68
+ :contactus:
69
+ :selector:
70
+ :text: "Contact Us"
71
+ :enterprisesalesinfo:
72
+ :selector:
73
+ :text: "Enterprise Sales Info"
74
+ :links:
75
+ :getstarted:
76
+ :selector:
77
+ :text: "Get Started"
78
+ :checkpoints:
79
+ :getstartedwithopenshift:
80
+ :selector:
81
+ :text: "Getting Started with OpenShift"
82
+ :securitypolicy:
83
+ :selector:
84
+ :text: "Security Policy"
85
+ :checkpoints:
86
+ :securityinformation:
87
+ :selector:
88
+ :text: "Security Information"
89
+ :blog:
90
+ :selector:
91
+ :text: "Blog"
92
+ :checkpoints:
93
+ :blog:
94
+ :selector:
95
+ :text: "Blog"
96
+ :newslettersignup:
97
+ :selector:
98
+ :text: "Join our Newsletter"
99
+ :checkpoints:
100
+ :blogupdates:
101
+ :selector:
102
+ :text: "Blog Updates"
103
+ :enterprisesalesinfo:
104
+ :selector:
105
+ :text: "Enterprise Sales Info"
106
+ :checkpoints:
107
+ :tryopenshiftenterprise:
108
+ :selector:
109
+ :text: "Try OpenShift Enterprise"
110
+ :partnerprograms:
111
+ :selector:
112
+ :text: "Partner Programs"
113
+ :checkpoints:
114
+ :findapartner:
115
+ :selector:
116
+ :text: "Find a Partner"
117
+