tabbyx 0.1.4

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/bin/console +14 -0
  3. data/bin/setup +8 -0
  4. data/lib/tabbyx.rb +29 -0
  5. data/lib/tabbyx/calabash_android.rb +11 -0
  6. data/lib/tabbyx/calabash_ios.rb +4 -0
  7. data/lib/tabbyx/core/base.rb +54 -0
  8. data/lib/tabbyx/core/config.rb +36 -0
  9. data/lib/tabbyx/core/initialize.rb +35 -0
  10. data/lib/tabbyx/fixtures/constants.rb +21 -0
  11. data/lib/tabbyx/fixtures/global.rb +39 -0
  12. data/lib/tabbyx/helpers/csv_helper.rb +65 -0
  13. data/lib/tabbyx/helpers/debug_helper.rb +37 -0
  14. data/lib/tabbyx/helpers/excel_helper.rb +79 -0
  15. data/lib/tabbyx/helpers/http_batch_handler.rb +50 -0
  16. data/lib/tabbyx/helpers/http_helper.rb +46 -0
  17. data/lib/tabbyx/helpers/minitest_helper.rb +48 -0
  18. data/lib/tabbyx/helpers/screenshot_helpers.rb +128 -0
  19. data/lib/tabbyx/helpers/txt_helper.rb +35 -0
  20. data/lib/tabbyx/steps_android/assert_steps.rb +31 -0
  21. data/lib/tabbyx/steps_android/check_box_steps.rb +3 -0
  22. data/lib/tabbyx/steps_android/context_menu_steps.rb +17 -0
  23. data/lib/tabbyx/steps_android/date_picker_steps.rb +8 -0
  24. data/lib/tabbyx/steps_android/enter_text_steps.rb +23 -0
  25. data/lib/tabbyx/steps_android/location_steps.rb +7 -0
  26. data/lib/tabbyx/steps_android/navigation_steps.rb +47 -0
  27. data/lib/tabbyx/steps_android/press_button_steps.rb +39 -0
  28. data/lib/tabbyx/steps_android/progress_steps.rb +51 -0
  29. data/lib/tabbyx/steps_android/search_steps.rb +7 -0
  30. data/lib/tabbyx/steps_android/spinner_steps.rb +11 -0
  31. data/lib/tabbyx/steps_ios/assertions.rb +79 -0
  32. data/lib/tabbyx/steps_ios/date_picker.rb +39 -0
  33. data/lib/tabbyx/steps_ios/operations.rb +187 -0
  34. data/lib/tabbyx/steps_ios/wait.rb +48 -0
  35. data/lib/tabbyx/utils/adb_devices.rb +248 -0
  36. data/lib/tabbyx/utils/adb_screenshot.rb +22 -0
  37. data/lib/tabbyx/utils/code_coverage.rb +6 -0
  38. data/lib/tabbyx/utils/shell.rb +32 -0
  39. data/lib/tabbyx/version.rb +3 -0
  40. metadata +286 -0
@@ -0,0 +1,50 @@
1
+ require 'json'
2
+ require 'excel_helper'
3
+ require 'http_helper'
4
+
5
+ =begin
6
+ 使用方法:
7
+ 1. 将API的url,参数,action等按照模板 inputs/api_testcases.xlsx 填写
8
+ TESTCASE ACTION URL PARAMS
9
+ 2. 运行测试,接口请求返回的结果会填回表格
10
+ example:
11
+ RunTestCases.new("api_testcases.xlsx",0).run_test_cases
12
+ 3. 运行完毕后打开excel文件查看返回数据
13
+ =end
14
+ class BatchGetResponse
15
+ attr_accessor :testcases
16
+
17
+ def initialize(filename,worksheet=0)
18
+ @testcases = ExcelHelper.read_from_excel(filename,worksheet)
19
+ @filename = filename
20
+ @worksheet = worksheet
21
+ end
22
+
23
+ def run_test_cases
24
+ testresults = []
25
+ @testcases.each do |testcase|
26
+ testresult = []
27
+ unless testcase[1].nil?
28
+ (0..3).each { |i| testresult[i] = testcase[i] }
29
+ action = testcase[1].strip
30
+ url = testcase[2].strip
31
+ params = testcase[3]
32
+ end
33
+
34
+ case action
35
+ when 'GET'
36
+ res = HTTPHelper.new.get_response(HOST+url,params)
37
+ testresult << res << res.code << res["Data"]
38
+ when 'POST'
39
+ res = HTTPHelper.new.post_response(HOST+url,params)
40
+ testresult << res << res.code << res["Data"]
41
+ when 'ACTION'
42
+ testresult << "RESPONSE" << "RESPONSE CODE" << "DATA"
43
+ end
44
+
45
+ testresults.push testresult
46
+ end
47
+ ExcelHelper.write_dictionary_to_excel(testresults,"testresult1.xlsx",@worksheet) unless testresults.nil?
48
+ end
49
+
50
+ end
@@ -0,0 +1,46 @@
1
+ require 'httparty'
2
+ require 'tabbyx/fixtures/global'
3
+
4
+ module HTTPHelper
5
+ @header =
6
+ {
7
+ 'Content-Type' => 'application/json',
8
+ 'Accept' => 'application/json',
9
+ 'User-Agent' => USER_AGENT,
10
+ 'Cookie' => COOKIE
11
+ }.to_hash
12
+
13
+ def self.post_response(requestUrl,params=nil,serviceRequest=nil,query=nil)
14
+ @body =
15
+ {
16
+ :serviceRequest => serviceRequest
17
+ }
18
+
19
+ params ? request = requestUrl+'?'+params : request = requestUrl
20
+ puts "请求: " + request
21
+
22
+ if serviceRequest.nil?
23
+ response = HTTParty.post(request, :query => query, :headers => @header)
24
+ else
25
+ response = HTTParty.post(request, :body => @body, :headers => @header)
26
+ end
27
+
28
+ response
29
+ end
30
+
31
+ def self.get_response(requestUrl, params=nil, query=nil)
32
+ params ? request = requestUrl+'?'+params : request = requestUrl
33
+ query ? q = query.to_hash : q = query
34
+ response = HTTParty.get(request, :query => q, :headers => @header)
35
+ response
36
+ end
37
+
38
+ def self.response_code(response)
39
+ response.code
40
+ end
41
+
42
+ def self.response_data(response)
43
+ response["Data"] || response["data"]
44
+ end
45
+
46
+ end
@@ -0,0 +1,48 @@
1
+ # require this file if you want to use minitest-reporter
2
+
3
+ require 'minitest/reporters'
4
+
5
+ # test report for rake testing(API & unit testing)
6
+ module Minitest
7
+ module Reporters
8
+ class AwesomeReporter < HtmlReporter
9
+ GRAY = '0;36'
10
+ GREEN = '1;32'
11
+ RED = '1;31'
12
+
13
+ def initialize(options = {})
14
+ super
15
+ @slow_threshold = options.fetch(:slow_threshold, nil)
16
+ end
17
+
18
+ def record_pass(test)
19
+ if @slow_threshold.nil? || test.time <= @slow_threshold
20
+ super
21
+ else
22
+ gray('O')
23
+ end
24
+ end
25
+
26
+ def color_up(string, color)
27
+ color? ? "\e\[#{ color }m#{ string }#{ ANSI::Code::ENDCODE }" : string
28
+ end
29
+
30
+ def red(string)
31
+ color_up(string, RED)
32
+ end
33
+
34
+ def green(string)
35
+ color_up(string, GREEN)
36
+ end
37
+
38
+ def gray(string)
39
+ color_up(string, GRAY)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ reporter_options = { color: true, slow_count: 5, reports_dir:'reports'}
47
+ report = Minitest::Reporters::AwesomeReporter.new(reporter_options)
48
+ Minitest::Reporters.use! [report]
@@ -0,0 +1,128 @@
1
+ # This requires ImageMagick to be installed
2
+
3
+ def screenshot_dirs
4
+ base_dir = screenshot_base_dir
5
+ orig_dir = base_dir + 'orig'
6
+ check_dir = base_dir + 'check'
7
+ diff_dir = base_dir + 'diff'
8
+
9
+ # Create directories if they don't exist
10
+ FileUtils.mkdir_p(orig_dir)
11
+ FileUtils.mkdir_p(check_dir)
12
+ FileUtils.mkdir_p(diff_dir)
13
+
14
+ {:orig_dir => orig_dir, :check_dir => check_dir, :diff_dir => diff_dir}
15
+ end
16
+
17
+ def remember_screen_as(screen)
18
+ screen.tr!(" ", "_")
19
+ dirs = screenshot_dirs
20
+ orig_filename = "#{dirs[:orig_dir]}/" + "#{screen}.png"
21
+ capture_filename = screenshot({:prefix => "#{dirs[:orig_dir]}/", :name => "#{screen}.png"})
22
+ puts orig_filename
23
+ puts capture_filename
24
+ if (orig_filename != capture_filename)
25
+ FileUtils.mv(capture_filename,orig_filename) # calabash might add a counter to the file
26
+ end
27
+ end
28
+
29
+ def compare_screen(screen)
30
+ screen.tr!(" ", "_")
31
+ dirs = screenshot_dirs
32
+
33
+ screenshot_filename = screenshot({:prefix => "#{dirs[:check_dir]}/", :name => "#{screen}.png"})
34
+ check_filename = "#{dirs[:check_dir]}/#{screen}.png"
35
+ check_rotated_filename = "#{dirs[:check_dir]}/#{screen}_rotated.png"
36
+ orig_filename = "#{dirs[:orig_dir]}/#{screen}.png"
37
+ diff_filename = "#{dirs[:diff_dir]}/#{screen}.png"
38
+ diff_rotated_filename = "#{dirs[:diff_dir]}/#{screen}_rotated.png"
39
+
40
+ # Moving files is instant, sleep 1 sec is needed to ensure that the movement is done before compaing two images.
41
+ FileUtils.mv(screenshot_filename,check_filename) # discard number, only keep one
42
+ sleep 1
43
+
44
+ if (File.exists? orig_filename)
45
+ # If the two images don't have the same width and height, ImageMagic's compare method will throw an exception
46
+ # get the original image's dimension
47
+ orig_image_size = (%x[identify -format "%[fx:w]x%[fx:h]" "#{orig_filename}"]).chomp # might be 1536 × 2048, 768x1024 or return error
48
+
49
+ # get the dimension of the image that is to be checked
50
+ check_image_size = (%x[identify -format "%[fx:w]x%[fx:h]" "#{check_filename}"]).chomp
51
+
52
+ # occasionally identify method isn't reliable, you will get 0x0 above, the image's width and height should have 3-4 digits
53
+ if /^(\d{3,4})x(\d{3,4})$/.match(orig_image_size).nil?
54
+ fail(msg="Cannot get image size, please replace the original image")
55
+ end
56
+
57
+ if /^(\d{3,4})x(\d{3,4})$/.match(check_image_size).nil?
58
+ fail(msg="the original screenshot's info isn't readable to ImageMagic, please check.")
59
+ end
60
+
61
+ # convert the check_image to the same size of the orig_image if necessary, so they are comparable
62
+ %x[convert "#{check_filename}" -resize #{orig_image_size} "#{check_filename}"] unless orig_image_size == check_image_size
63
+ sleep 2
64
+ # Create a rotated version of the screenshot
65
+ # shell_res = %x[convert "#{check_filename}" -rotate 180 #{check_rotated_filename}]
66
+
67
+ # Compare pixel-by-pixel
68
+ # -fuzz %1 is too strict, change it to 5%
69
+ diff = (%x[compare -metric AE -fuzz 5% "#{orig_filename}" "#{check_filename}" "#{diff_filename}" 2>&1]).chomp
70
+ diff_rotated = (%x[compare -metric AE -fuzz 5% "#{orig_filename}" "#{check_rotated_filename}" "#{diff_rotated_filename}" 2>&1]).chomp
71
+ if (!diff.match(/^\d.*$/))
72
+ fail(msg="Error comparing pictures. '#{diff}'")
73
+ end
74
+
75
+ # Use rotated file if it has a better match
76
+ if (diff.to_f > diff_rotated.to_f)
77
+ log "Using rotated screenshot instead, since it provides a better match"
78
+ FileUtils.cp(check_rotated_filename,check_filename)
79
+ FileUtils.cp(diff_rotated_filename,diff_filename)
80
+ diff = diff_rotated
81
+ end
82
+ FileUtils.rm check_rotated_filename
83
+ FileUtils.rm diff_rotated_filename
84
+
85
+ pixel_count = (%x[convert "#{orig_filename}" -format "%[fx:w*h]" info:]).chomp # occasionally unreliable
86
+ percent_diff = 100*diff.to_f/pixel_count.to_f
87
+ puts "Screenshots diff: #{percent_diff}"
88
+
89
+ # Remove diff if no difference
90
+ FileUtils.rm diff_filename if (percent_diff == 0)
91
+
92
+ else
93
+ # No original file. Use the new screenshot. Keep the one in "check".
94
+ FileUtils.cp(check_filename,orig_filename)
95
+ log "No original screenshot. New has been created at #{orig_filename}. Please check."
96
+ percent_diff = 0
97
+ end
98
+
99
+ {
100
+ :check_filename => check_filename,
101
+ :orig_filename => orig_filename,
102
+ :diff_filename => diff_filename,
103
+ :percent_diff => percent_diff
104
+ }
105
+ end
106
+
107
+ def screen_match?(screen, x = 100, invert = false, always_clean = false)
108
+ res = compare_screen(screen)
109
+
110
+ FileUtils.rm res[:check_filename] if always_clean
111
+
112
+ return false if (!invert && (100-res[:percent_diff]) < x.to_f)
113
+ return false if (invert && (100-res[:percent_diff]) >= x.to_f)
114
+
115
+ # Remove check file if everything is ok
116
+ FileUtils.rm res[:check_filename] if !always_clean
117
+
118
+ true
119
+ end
120
+
121
+ def screen_not_match?(screen, x = 100) ; screen_match?(screen, x, true) end
122
+
123
+ def assert_screen_match(screen,x = 100,invert = false)
124
+ match = screen_match?(screen,x,invert)
125
+ fail(msg="Screenshots differ too much.") if !match && !invert
126
+ fail(msg="Screenshots differ too little.") if !match && invert
127
+ true
128
+ end
@@ -0,0 +1,35 @@
1
+ require 'tabbyx/core/base'
2
+
3
+ module TXThelper
4
+
5
+ # example:
6
+ # TXThelper.read_from_text('api_testcases.csv')
7
+
8
+ def self.read_from_text(filename)
9
+ file = Base.file_exists?(filename)
10
+ text = []
11
+ lines = IO.readlines(file)
12
+ lines.each do |line|
13
+ text.push line
14
+ end
15
+ text
16
+ end
17
+
18
+ def self.read_file_by_path(file_path)
19
+ file = File.absolute_path(file_path)
20
+ data = []
21
+ lines = IO.readlines(file)
22
+ lines.each do |line|
23
+ data.push line
24
+ end
25
+ data
26
+ end
27
+
28
+ def self.write_array_to_text(array,filename)
29
+ Base.file_exists?(filename) ? file = Base.file_exists?(filename) : file = Base.create_file(filename)
30
+ File.open(file,"w") do |line|
31
+ array.each_with_index { |item,index | line.print(index,":",item);line.puts "\n" }
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,31 @@
1
+
2
+ Then /^我看见文本"([^\"]*)"$/ do |text|
3
+ wait_for_text(text, timeout: 10)
4
+ end
5
+
6
+ Then /^我看见"([^\"]*)"$/ do |text|
7
+ wait_for_text(text, timeout: 10)
8
+ end
9
+
10
+ Then /^我必须看见"([^\"]*)"$/ do |text|
11
+ wait_for_text(text, timeout: 10)
12
+ end
13
+
14
+ Then /^我必须看见文本包含"([^\"]*)"$/ do |text|
15
+ wait_for_text(text, timeout: 10)
16
+ end
17
+
18
+
19
+
20
+ Then /^我看不到文本"([^\"]*)"$/ do |text|
21
+ wait_for_text_to_disappear(text, timeout: 10)
22
+ end
23
+
24
+ Then /^我看不见文本"([^\"]*)"$/ do |text|
25
+ wait_for_text_to_disappear(text, timeout: 10)
26
+ end
27
+
28
+ Then /^我看不见"([^\"]*)"$/ do |text|
29
+ wait_for_text_to_disappear(text, timeout: 10)
30
+ end
31
+
@@ -0,0 +1,3 @@
1
+ Then /^点击第(\d+)个复选框$/ do |index|
2
+ tap_when_element_exists("android.widget.CheckBox index:#{index.to_i-1}")
3
+ end
@@ -0,0 +1,17 @@
1
+ Then /^长按"([^\"]*)"并选择第(\d+)个文本$/ do |text, index|
2
+ step_deprecated
3
+
4
+ long_press_when_element_exists("* {text CONTAINS[c] '#{text}'}")
5
+ tap_when_element_exists("com.android.internal.view.menu.ListMenuItemView android.widget.TextView index:#{index.to_i - 1}")
6
+ end
7
+
8
+ Then /^长按"([^\"]*)"并选择文本"([^\"]*)"$/ do |text, identifier|
9
+ step_deprecated
10
+
11
+ long_press_when_element_exists("* {text CONTAINS[c] '#{text}'}")
12
+ tap_when_element_exists("com.android.internal.view.menu.ListMenuItemView android.widget.TextView marked:'#{identifier}'")
13
+ end
14
+
15
+ Then /^长按"([^\"]*)"$/ do |text|
16
+ long_press_when_element_exists("* {text CONTAINS[c] '#{text}'}")
17
+ end
@@ -0,0 +1,8 @@
1
+
2
+ Given /^我在第([^\"]*)个日期选择器上选择日期"(\d\d-\d\d-\d\d\d\d)"$/ do |index,date|
3
+ set_date("android.widget.DatePicker index:#{index.to_i-1}", date)
4
+ end
5
+
6
+ Given /^我将日期选择器的"([^\"]*)"日期设为"(\d\d-\d\d-\d\d\d\d)"$/ do |content_description, date|
7
+ set_date("android.widget.DatePicker {contentDescription LIKE[c] '#{content_description}'}", date)
8
+ end
@@ -0,0 +1,23 @@
1
+ Then /^我在文本框"([^\"]*)"输入"([^\"]*)"$/ do |text, content_description|
2
+ enter_text("android.widget.EditText {contentDescription LIKE[c] '#{content_description}'}", text)
3
+ end
4
+
5
+ Then /^我输入"([^\"]*)"到第(\d+)个输入框$/ do |text, index|
6
+ enter_text("android.widget.EditText index:#{index.to_i-1}", text)
7
+ end
8
+
9
+ Then /^我输入"([^\"]*)"到输入框其id为"([^\"]*)"$/ do |text, id|
10
+ enter_text("android.widget.EditText id:'#{id}'", text)
11
+ end
12
+
13
+ Then /^我清空编辑框"([^\"]*)"$/ do |identifier|
14
+ clear_text_in("android.widget.EditText marked:'#{identifier}'}")
15
+ end
16
+
17
+ Then /^我清空第(\d+)个编辑框$/ do |index|
18
+ clear_text_in("android.widget.EditText index:#{index.to_i-1}")
19
+ end
20
+
21
+ Then /^我清空编辑框其id为"([^\"]*)"$/ do |id|
22
+ clear_text_in("android.widget.EditText id:'#{id}'")
23
+ end
@@ -0,0 +1,7 @@
1
+ Then /^我到达地理位置"([^\"]*)"$/ do |location|
2
+ set_gps_coordinates_from_location(location)
3
+ end
4
+
5
+ Then /^我在纬度([-+]?[0-9]*\.?[0-9]+),经度([-+]?[0-9]*\.?[0-9]+)$/ do |latitude, longitude|
6
+ set_gps_coordinates(latitude, longitude)
7
+ end
@@ -0,0 +1,47 @@
1
+ Then /^我返回$/ do
2
+ press_back_button
3
+ end
4
+
5
+ Then /^我点击菜单$/ do
6
+ press_menu_button
7
+ end
8
+
9
+ Then /^我点击回车键$/ do
10
+ press_user_action_button
11
+ # Or, possibly, press_enter_button
12
+ end
13
+
14
+
15
+ Then /^向左滑动$/ do
16
+ perform_action('swipe', 'left')
17
+ end
18
+
19
+ Then /^向右滑动$/ do
20
+ perform_action('swipe', 'right')
21
+ end
22
+
23
+ Then /^我从菜单中选择"([^\"]*)"$/ do |identifier|
24
+ select_options_menu_item(identifier)
25
+ end
26
+
27
+ Then /^我选择第(\d+)个tab$/ do | tab |
28
+ touch("android.widget.TabWidget descendant TextView index:#{tab.to_i-1}")
29
+ end
30
+
31
+ # @param - the "tag" associated with the tab, or the text within the tab label
32
+ Then /^我选择"([^\"]*)"tab$/ do | tab |
33
+ touch("android.widget.TabWidget descendant TextView {text LIKE[c] '#{tab}'}")
34
+ end
35
+
36
+ Then /^向下滚动$/ do
37
+ scroll_down
38
+ end
39
+
40
+ Then /^向上滚动$/ do
41
+ scroll_up
42
+ end
43
+
44
+ Then /^从位置(\d+):(\d+)拖拽到位置(\d+):(\d+)共拖拽(\d+)步$/ do |from_x, from_y, to_x, to_y, steps|
45
+ perform_action('drag', from_x, to_x, from_y, to_y, steps)
46
+ end
47
+