itms_automation 2.4 → 2.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/bin/generate.rb +3 -37
  3. data/bin/helper.rb +23 -10
  4. data/bin/itms_automation +18 -23
  5. data/doc/installation.md +16 -0
  6. data/doc/itms-automation-API.md +103 -0
  7. data/doc/itms-automation-help.md +18 -0
  8. data/example/android/android_app/android_app_calculator.zip +0 -0
  9. data/example/android/android_app/android_app_calculator/AndroidCalculator.apk +0 -0
  10. data/example/android/android_app/android_app_calculator/features/calculator.feature +36 -0
  11. data/example/android/android_app/android_app_calculator/features/my_first.feature +13 -0
  12. data/example/android/android_app/android_app_calculator/features/screenshots/test.png +0 -0
  13. data/example/android/android_app/android_app_calculator/features/step_definitions/custom_steps.rb +5 -0
  14. data/example/android/android_app/android_app_calculator/features/support/env.rb +52 -0
  15. data/example/android/android_app/android_app_calculator/features/support/hooks.rb +26 -0
  16. data/example/android/android_web/android_web_gmail_login.zip +0 -0
  17. data/example/android/android_web/android_web_gmail_login/features/gmail_login.feature +12 -0
  18. data/example/android/android_web/android_web_gmail_login/features/my_first.feature +1 -0
  19. data/example/android/android_web/android_web_gmail_login/features/screenshots/test.png +0 -0
  20. data/example/android/android_web/android_web_gmail_login/features/step_definitions/custom_steps.rb +5 -0
  21. data/example/android/android_web/android_web_gmail_login/features/support/env.rb +51 -0
  22. data/example/android/android_web/android_web_gmail_login/features/support/hooks.rb +27 -0
  23. data/example/desktop web/desktop_web_gmail_login.zip +0 -0
  24. data/example/desktop web/desktop_web_gmail_login/features/gmail_login.feature +9 -0
  25. data/example/desktop web/desktop_web_gmail_login/features/gmail_multi_login.feature +21 -0
  26. data/example/desktop web/desktop_web_gmail_login/features/my_first.feature +1 -0
  27. data/example/desktop web/desktop_web_gmail_login/features/screenshots/test.png +0 -0
  28. data/example/desktop web/desktop_web_gmail_login/features/step_definitions/custom_steps.rb +5 -0
  29. data/example/desktop web/desktop_web_gmail_login/features/support/env.rb +106 -0
  30. data/example/desktop web/desktop_web_gmail_login/features/support/hooks.rb +38 -0
  31. data/features-skeleton/my_first.feature +5 -0
  32. data/features-skeleton/screenshots/test.png +0 -0
  33. data/features-skeleton/step_definitions/custom_steps.rb +5 -0
  34. data/features-skeleton/step_definitions/repositories/project_object.yml +1 -0
  35. data/features-skeleton/support/env.rb +105 -0
  36. data/features-skeleton/support/hooks.rb +38 -0
  37. data/lib/itms_automation.rb +1 -3
  38. data/lib/itms_automation/assertion_steps.rb +68 -0
  39. data/lib/itms_automation/click_elements_steps.rb +24 -0
  40. data/lib/itms_automation/configuration_steps.rb +7 -0
  41. data/lib/itms_automation/input_steps.rb +59 -0
  42. data/lib/itms_automation/javascript_handling_steps.rb +10 -0
  43. data/lib/itms_automation/methods/assertion_methods.rb +333 -0
  44. data/lib/itms_automation/methods/click_elements_methods.rb +23 -0
  45. data/lib/itms_automation/methods/configuration_methods.rb +35 -0
  46. data/lib/itms_automation/methods/error_handling_methods.rb +93 -0
  47. data/lib/itms_automation/methods/input_methods.rb +69 -0
  48. data/lib/itms_automation/methods/javascript_handling_methods.rb +9 -0
  49. data/lib/itms_automation/methods/misc_methods.rb +63 -0
  50. data/lib/itms_automation/methods/navigate_methods.rb +123 -0
  51. data/lib/itms_automation/methods/progress_methods.rb +15 -0
  52. data/lib/itms_automation/methods/required_files.rb +9 -0
  53. data/lib/itms_automation/methods/screenshot_methods.rb +6 -0
  54. data/lib/itms_automation/navigation_steps.rb +79 -0
  55. data/lib/itms_automation/progress_steps.rb +17 -0
  56. data/lib/itms_automation/screenshot_steps.rb +6 -0
  57. data/lib/itms_automation/version.rb +5 -3
  58. metadata +85 -75
  59. data/bin/console +0 -14
  60. data/bin/documentation_generator.rb +0 -119
  61. data/bin/setup +0 -8
  62. data/lib/itms_automation/all_steps.rb +0 -8
  63. data/lib/itms_automation/assertion_helper.rb +0 -29
  64. data/lib/itms_automation/auto_util.rb +0 -707
  65. data/lib/itms_automation/database_steps_helper.rb +0 -125
  66. data/lib/itms_automation/web_steps_helper.rb +0 -867
  67. data/project/Gemfile +0 -3
  68. data/project/Gemfile.lock +0 -92
  69. data/project/README.md +0 -34
  70. data/project/Rakefile +0 -24
  71. data/project/cucumber.yml +0 -4
  72. data/project/features/TestSuite/WebGUI.feature +0 -5
  73. data/project/features/step_definitions/lib_steps/steps_definition.rb +0 -46
  74. data/project/features/step_definitions/repositories/project_object.yml +0 -26
  75. data/project/features/support/env.rb +0 -9
  76. data/project/features/support/hooks.rb +0 -122
@@ -1,3 +1 @@
1
- require 'cucumber_priority'
2
- require "itms_automation/version"
3
- Dir[File.dirname(__FILE__) + '/itms_automation/*.rb'].each { |file| require file }
1
+ Dir[File.dirname(__FILE__) + '/itms_automation/methods/*.rb'].each { |file| require file }
@@ -0,0 +1,68 @@
1
+ # require 'cucumber'
2
+ # require_relative 'methods/assertion_methods'
3
+
4
+ # # page title checking
5
+ # Then(/^I should\s*((?:not)?)\s+see page title as "(.*?)"$/) do |present, title|
6
+ # check_title(title, present.empty?)
7
+ # end
8
+
9
+ # Then(/^I should\s*((?:not)?)\s+see page title having partial text as "(.*?)"$/) do |present, partial_text_title|
10
+ # check_partial_title(partial_text_title, present.empty?)
11
+ # end
12
+
13
+ # # step to check element text
14
+ # Then(/^element "([^\"]*)" should\s*((?:not)?)\s+have text as "(.*?)"$/) do |element, present, value |
15
+ # check_element_text(element, value, present.empty?)
16
+ # end
17
+
18
+ # # step to check element partial text
19
+ # Then(/^element "([^\"]*)" should\s*((?:not)?)\s+have partial text as "(.*?)"$/) do |element, present, value |
20
+ # check_element_partial_text(element, value, present.empty?)
21
+ # end
22
+
23
+ # # step to check attribute value
24
+ # Then(/^element "([^\"]*)" should\s*((?:not)?)\s+have attribute "(.*?)" with value "(.*?)"$/) do |element, present, attrb, value|
25
+ # check_element_attribute(element, attrb, value, present.empty?)
26
+ # end
27
+
28
+ # # step to check element enabled or not
29
+ # Then(/^element "([^\"]*)" should\s*((?:not)?)\s+be (enabled|disabled)$/) do |element, present, state|
30
+ # flag = state == 'enabled'
31
+ # flag = !flag unless present.empty?
32
+ # check_element_enable(element, flag)
33
+ # end
34
+
35
+ # # step to check element present or not
36
+ # Then(/^element "(.*?)" should\s*((?:not)?)\s+be present$/) do |element, present|
37
+ # check_element_presence(element, present.empty?)
38
+ # end
39
+
40
+ # # step to assert checkbox is checked or unchecked
41
+ # Then(/^checkbox "(.*?)" should be (checked|unchecked)$/) do |element, state|
42
+ # flag = state == 'checked'
43
+ # is_checkbox_checked(element, flag)
44
+ # end
45
+
46
+ # # steps to assert radio button checked or unchecked
47
+ # Then(/^radio button"(.*?)" should be (selected|unselected)$/) do |element, state|
48
+ # flag = state == 'selected'
49
+ # is_radio_button_selected(element, flag)
50
+ # end
51
+
52
+ # # steps to assert option by text from radio button group selected/unselected
53
+ # Then(/^option "(.*?)" by (.+) from radio button group "(.*?)" should be (selected|unselected)$/) do |option, attrb, element, state|
54
+ # flag = state == 'selected'
55
+ # is_option_from_radio_button_group_selected(element, attrb, option, flag)
56
+ # end
57
+
58
+ # # step to assert javascript pop-up alert text
59
+ # Then(/^I should see alert text as "(.*?)"$/) do |actual_value|
60
+ # check_alert_text(actual_value)
61
+ # end
62
+
63
+ # # step to assert dropdown list
64
+ # Then(/^option "(.*?)" by (.+) from dropdown "(.*?)" should be (selected|unselected)$/) do |option, by, element, state|
65
+ # flag = state == 'selected'
66
+ # is_option_from_dropdown_selected(element, by, option, state)
67
+ # end
68
+
@@ -0,0 +1,24 @@
1
+ # require 'cucumber'
2
+ # require_relative 'methods/click_elements_methods'
3
+
4
+ # # click on web element
5
+ # When(/^I click on element"(.*?)"$/) do |element|
6
+ # click(element)
7
+ # end
8
+
9
+ # Then(/^I forcefully click on element "(.*?)"$/) do |element|
10
+ # click_forcefully(element)
11
+ # end
12
+
13
+ # # double click on web element
14
+ # Then(/^I double click on element "(.*?)"$/) do |element|
15
+ # double_click(element)
16
+ # end
17
+
18
+ # When(/^I tap on element"(.*?)"$/) do |element|
19
+ # click(element)
20
+ # end
21
+
22
+ # Then(/^I long press on element "(.*?)"$/) do |element|
23
+ # long_press(element, duration)
24
+ # end
@@ -0,0 +1,7 @@
1
+ # require 'cucumber'
2
+ # require_relative 'methods/configuration_methods'
3
+
4
+ # # step to print configuration
5
+ # Then(/^I print configuration$/) do
6
+ # print_congifugartion
7
+ # end
@@ -0,0 +1,59 @@
1
+ # require 'cucumber'
2
+ # require_relative 'methods/input_methods'
3
+
4
+ # # enter text into input field steps
5
+ # Then(/^I enter "([^\"]*)" into element "(.*?)"$/) do |text, element|
6
+ # enter_text(type, element)
7
+ # end
8
+
9
+ # # clear input field steps
10
+ # Then(/^I clear input element "(.*?)"$/) do |type, element|
11
+ # clear_text(type, element)
12
+ # end
13
+
14
+ # # select option by text/value from dropdown/multiselect
15
+ # Then(/^I select "(.*?)" option by (.+) from\s*((?:multiselect)?)\sdropdown "(.*?)"$/) do |option, option_by, present, element|
16
+ # validate_option_by option_by
17
+ # select_option_from_dropdown(element, option_by, option)
18
+ # end
19
+
20
+ # # select option by index from dropdown/multiselect
21
+ # Then(/^I select (\d+) option by index from\s*((?:multiselect)?)\sdropdown "(.*?)"$/) do |option, present, element|
22
+ # select_option_from_dropdown(element, 'index', (option.to_i) -1)
23
+ # end
24
+
25
+ # # step to select option from mutliselect dropdown list
26
+ # Then(/^I select all options from multiselect dropdown "(.*?)"$/) do |element|
27
+ # select_all_option_from_multiselect_dropdown(element)
28
+ # end
29
+
30
+ # # step to unselect option from mutliselect dropdown list
31
+ # Then(/^I unselect all options from multiselect dropdown "(.*?)"$/) do |element|
32
+ # unselect_all_option_from_multiselect_dropdown(element)
33
+ # end
34
+
35
+ # # check checkbox steps
36
+ # Then(/^I check the checkbox "(.*?)"$/) do |element|
37
+ # check_checkbox(element)
38
+ # end
39
+
40
+ # # uncheck checkbox steps
41
+ # Then(/^I uncheck the checkbox "(.*?)"$/) do |element|
42
+ # uncheck_checkbox(element)
43
+ # end
44
+
45
+ # # steps to toggle checkbox
46
+ # Then(/^I toggle checkbox "(.*?)"$/) do |element|
47
+ # toggle_checkbox(element)
48
+ # end
49
+
50
+ # # step to select radio button
51
+ # Then(/^I select radio button "(.*?)"$/) do |element|
52
+ # select_radio_button(element)
53
+ # end
54
+
55
+ # # steps to select option by text from radio button group
56
+ # Then(/^I select "(.*?)" option by (.+) from radio button group "(.*?)"$/) do |option, option_by, element|
57
+ # validate_option_by option_by
58
+ # select_option_from_radio_button_group(element, option_by, option)
59
+ # end
@@ -0,0 +1,10 @@
1
+ # require 'cucumber'
2
+ # require_relative 'methods/javascript_handling_methods'
3
+
4
+ # Then(/^I accept alert$/) do
5
+ # handle_alert('accept')
6
+ # end
7
+
8
+ # Then(/^I dismiss alert$/) do
9
+ # handle_alert('dismiss')
10
+ # end
@@ -0,0 +1,333 @@
1
+ require 'net/https'
2
+ require_relative 'required_files'
3
+
4
+ # This file contains assertion methods which are called from assertion_steps.rb
5
+
6
+ # Method to return page title
7
+ def get_page_title
8
+ $driver.title
9
+ end
10
+
11
+ # Method to verify title
12
+ # param 1 : String : expected title
13
+ # param 2 : Boolean : test case [true or flase]
14
+ def check_title(title, test_case)
15
+ page_title = get_page_title
16
+ if test_case
17
+ if page_title != "#{title}"
18
+ raise TestCaseFailed, "Page Title Not Matched, Actual Page Title : #{page_title}"
19
+ end
20
+ else
21
+ if page_title == "#{title}"
22
+ raise TestCaseFailed, "Page Title Matched, Actual Page Title:#{page_title}"
23
+ end
24
+ end
25
+ end
26
+
27
+ # Method to verify partial title
28
+ # param 1 : String : partial title string
29
+ # param 2 : Boolean : test case [true or flase]
30
+ def check_partial_title(partial_text_title, test_case)
31
+ page_title = get_page_title
32
+ if test_case
33
+ if not page_title.include? "#{partial_text_title}"
34
+ raise TestCaseFailed, 'Partial Page Title Not Present'
35
+ end
36
+ else
37
+ if page_title.include? "#{partial_text_title}"
38
+ raise TestCaseFailed, 'Page Title Matched'
39
+ end
40
+ end
41
+ end
42
+
43
+ # Method to get element text
44
+ # param : String
45
+ def get_element_text(element)
46
+ WAIT.until { find_object(element) }.text
47
+ end
48
+
49
+ # Method to check element text
50
+ # param 1 : String : Element
51
+ # param 2 : String : Expected element text
52
+ # param 3 : Boolean : test case [true or flase]
53
+ def check_element_text(element, actual_value, test_case)
54
+ element_text = get_element_text(element)
55
+
56
+ if test_case
57
+ if element_text != actual_value
58
+ raise TestCaseFailed, "Text Not Matched. Actual Value :#{actual_value}"
59
+ end
60
+ else
61
+ if element_text == actual_value
62
+ raise TestCaseFailed, "Text Matched. Actual Value :#{actual_value}"
63
+ end
64
+ end
65
+ end
66
+
67
+ # Method to check partial element text
68
+ # param 1 : String : Element
69
+ # param 2 : String : Expected element partial text
70
+ # param 3 : Boolean : test case [true or flase]
71
+ def check_element_partial_text(element, actual_value, test_case)
72
+ element_text = get_element_text(element)
73
+
74
+ if test_case
75
+ if not element_text.include? "#{actual_value}"
76
+ raise TestCaseFailed, "Text Not Matched. Actual Value :#{actual_value}"
77
+ end
78
+ else
79
+ if element_text.include? "#{actual_value}"
80
+ raise TestCaseFailed, "Text Matched. Actual Value :#{actual_value}"
81
+ end
82
+ end
83
+ end
84
+
85
+ # Method to return element status - enabled?
86
+ # param : String : Element
87
+ def is_element_enabled(element)
88
+ WAIT.until{ find_object(element) }.enabled?
89
+ end
90
+
91
+ # Element enabled checking
92
+ # param 1 : String : Element
93
+ # param 2 : Boolean : test case [true or flase]
94
+ def check_element_enable(element, test_case)
95
+ result = is_element_enabled(element)
96
+
97
+ if test_case
98
+ raise TestCaseFailed, 'Element Not Enabled' unless result
99
+ else
100
+ raise TestCaseFailed, 'Element Enabled' unless !result
101
+ end
102
+ end
103
+
104
+ # method to get attribute value
105
+ # param 1 : String : Element
106
+ # param 2 : String : atrribute name
107
+ def get_element_attribute(element, attribute_name)
108
+ WAIT.until{ find_object(element) }.attribute("#{attribute_name}")
109
+ end
110
+
111
+ # method to check attribute value
112
+ # param 1 : String : Element
113
+ # param 2 : String : atrribute name
114
+ # param 3 : String : atrribute value
115
+ # param 4 : Boolean : test case [true or flase]
116
+ def check_element_attribute(element, attribute_name, attribute_value, test_case)
117
+
118
+ attr_val = get_element_attribute(element, attribute_name)
119
+
120
+ if test_case
121
+ if attr_val != attribute_value
122
+ raise TestCaseFailed, "Attribute Value Not Matched. Actual Value :#{attr_val}"
123
+ end
124
+ else
125
+ if attr_val == attribute_value
126
+ raise TestCaseFailed, "Attribute Value Matched. Actual Value :#{attr_val}"
127
+ end
128
+ end
129
+ end
130
+
131
+ # method to get element status - displayed?
132
+ # param : String : Element
133
+ def is_element_displayed(element)
134
+ WAIT.until{ find_object(element) }.displayed?
135
+ end
136
+
137
+ # method to check element presence
138
+ # param 1 : String : Element
139
+ # param 2 : Boolean : test case [true or flase]
140
+ def check_element_presence(element, test_case)
141
+ if test_case
142
+ if !is_element_displayed(element)
143
+ raise TestCaseFailed, 'Element Not Present'
144
+ end
145
+ else
146
+ begin
147
+ if is_element_displayed(element)
148
+ raise 'Present' # since it is negative test and we found element
149
+ end
150
+ rescue Exception => e
151
+ if e.message == 'Present' # only raise if it present
152
+ raise TestCaseFailed, 'Element Present'
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ # method to assert checkbox check/uncheck
159
+ # param 1 : String : Element
160
+ # param 2 : Boolean : test case [true or flase]
161
+ def is_checkbox_checked(element, should_be_checked = true)
162
+ checkbox = WAIT.until{ find_object(element) }
163
+
164
+ if !checkbox.selected? && should_be_checked
165
+ raise TestCaseFailed, 'Checkbox is not checked'
166
+ elsif checkbox.selected? && !should_be_checked
167
+ raise TestCaseFailed, 'Checkbox is checked'
168
+ end
169
+ end
170
+
171
+ # method to assert radio button selected/unselected
172
+ # param 1 : String : Element
173
+ # param 2 : Boolean : test case [true or flase]
174
+ def is_radio_button_selected(element, should_be_selected = true)
175
+ radio_button = WAIT.until{ find_object(element) }
176
+
177
+ if !radio_button.selected? && should_be_selected
178
+ raise TestCaseFailed, 'Radio Button not selected'
179
+ elsif radio_button.selected? && !should_be_selected
180
+ raise TestCaseFailed, 'Radio Button is selected'
181
+ end
182
+ end
183
+
184
+ # method to assert option from radio button group is selected/unselected
185
+ def is_option_from_radio_button_group_selected(element, by, option, should_be_selected = true)
186
+ radio_button_group = WAIT.until{ find_object(element) }
187
+
188
+ getter = ->(rb, by) { by == 'value' ? rb.attribute('value') : rb.text }
189
+
190
+ ele = radio_button_group.find { |rb| getter.call(rb, by) == option }
191
+
192
+ if !ele.selected? && should_be_selected
193
+ raise TestCaseFailed, 'Radio button is not selected'
194
+ elsif ele.selected? && !should_be_selected
195
+ raise TestCaseFailed, 'Radio button is selected'
196
+ end
197
+ end
198
+
199
+ # method to get javascript pop-up alert text
200
+ def get_alert_text
201
+ $driver.switch_to.alert.text
202
+ end
203
+
204
+ # method to check javascript pop-up alert text
205
+ def check_alert_text(text)
206
+ if get_alert_text != text
207
+ raise TestCaseFailed, "Text on alert pop up not matched. Actual Value :#{get_alert_text}"
208
+ end
209
+ end
210
+
211
+ def is_option_from_dropdown_selected(element, by, option, should_be_selected=true)
212
+ dropdown = WAIT.until { find_object(element) }
213
+ select_list = Selenium::WebDriver::Support::Select.new(dropdown)
214
+
215
+ if by == 'text'
216
+ actual_value = select_list.first_selected_option.text
217
+ else
218
+ actual_value = select_list.first_selected_option.attribute('value')
219
+ end
220
+
221
+ if !actual_value == option && should_be_selected
222
+ raise TestCaseFailed, 'Option Not Selected From Dropwdown'
223
+ elsif actual_value == option && !should_be_selected
224
+ raise TestCaseFailed, 'Option Selected From Dropwdown'
225
+ end
226
+ end
227
+
228
+ # Method to find difference between images
229
+ def does_images_similar?(actual_img_access_type, actual_img_access_name, excp_img_access_type, excp_img_access_name)
230
+ if !compare_image(actual_img_access_type, actual_img_access_name, excp_img_access_type, excp_img_access_name)
231
+ raise TestCaseFailed, 'Actual image is different from expected image'
232
+ end
233
+ end
234
+
235
+ # Method to compare two images
236
+ # param 1 : String : Locator type (id, name, class, xpath, css, url)
237
+ # param 2 : String : Locator value
238
+ # param 3 : String : Locator type (id, name, class, xpath, css, url, image_name)
239
+ # param 4 : String : Locator value
240
+ def compare_image(actual_img_access_type, actual_img_access_name, excp_img_access_type, excp_img_access_name)
241
+ if actual_img_access_type == 'url'
242
+ actual_img_url = actual_img_access_name
243
+ else
244
+ actual_img_url = get_element_attribute(actual_img_access_type, actual_img_access_name, 'src')
245
+ end
246
+
247
+ if excp_img_access_type == 'url'
248
+ expected_img_url = excp_img_access_name
249
+ elsif excp_img_access_type == 'image_name'
250
+ expected_img_url = './features/expected_images/' + excp_img_access_name
251
+ else
252
+ expected_img_url = get_element_attribute(excp_img_access_type, excp_img_access_name, 'src')
253
+ end
254
+
255
+ # replace 'https' with 'http' from actual image url
256
+ if actual_img_url.include? 'https'
257
+ actual_img_url['https'] = 'http'
258
+ end
259
+
260
+ # replace 'https' with 'http' from expected image url
261
+ if expected_img_url.include? 'https'
262
+ expected_img_url['https'] = 'http'
263
+ end
264
+
265
+ if expected_img_url.include? '.png'
266
+ image_type = 'png'
267
+ else
268
+ image_type = 'jpg'
269
+ end
270
+
271
+ # Storing actual image locally
272
+ open('./features/actual_images/actual_image.' + image_type, 'wb') do |file|
273
+ file << open(actual_img_url).read
274
+ end
275
+
276
+ actual_img_url = './features/actual_images/actual_image.' + image_type
277
+
278
+ # Storing Expected image locally
279
+ if excp_img_access_type != 'image_name'
280
+ open('./features/expected_images/expected_image.' + image_type, 'wb') do |file|
281
+ file << open(expected_img_url).read
282
+ end
283
+ expected_img_url = './features/expected_images/expected_image.' + image_type
284
+ end
285
+
286
+ # Verify image extension and call respective compare function
287
+ if image_type == 'png'
288
+ return compare_png_images(expected_img_url, actual_img_url)
289
+ end
290
+
291
+ compare_jpeg_images(expected_img_url, actual_img_url)
292
+ end
293
+
294
+ # Comparing jpg images
295
+ def compare_jpeg_images(expected_img_url, actual_img_url)
296
+ if open(expected_img_url).read == open(actual_img_url).read
297
+ return true
298
+ else
299
+ puts 'Difference in images'
300
+ return false
301
+ end
302
+ end
303
+
304
+ # Comparing png images
305
+ def compare_png_images(expected_img_url, actual_img_url)
306
+ images = [
307
+ ChunkyPNG::Image.from_file(expected_img_url),
308
+ ChunkyPNG::Image.from_file(actual_img_url)
309
+ ]
310
+
311
+ diff = []
312
+
313
+ images.first.height.times do |y|
314
+ images.first.row(y).each_with_index do |pixel, x|
315
+ diff << [x, y] unless pixel == images.last[x, y]
316
+ end
317
+ end
318
+
319
+ if diff.length != 0
320
+ puts "\npixels (total): #{images.first.pixels.length}"
321
+ puts "pixels changed: #{diff.length}"
322
+ puts "pixels changed (%): #{(diff.length.to_f / images.first.pixels.length) * 100}%"
323
+
324
+ x, y = diff.map { |xy| xy[0] }, diff.map { |xy| xy[1] }
325
+ images.last.rect(x.min, y.min, x.max, y.max, ChunkyPNG::Color.rgb(0, 255, 0))
326
+ cur_time = Time.now.strftime('%Y%m%d%H%M%S%L')
327
+ images.last.save("./features/image_difference/difference_#{cur_time}.png")
328
+
329
+ puts "\nDifference between images saved as : difference_#{cur_time}.png\n"
330
+ return false
331
+ end
332
+ true
333
+ end