actir 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +11 -0
- data/README.md +56 -0
- data/Rakefile +70 -0
- data/actir.gemspec +31 -0
- data/bin/actir +9 -0
- data/bin/ants +9 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/actir.rb +41 -0
- data/lib/actir/basic_page.rb +49 -0
- data/lib/actir/config.rb +192 -0
- data/lib/actir/initializer.rb +70 -0
- data/lib/actir/parallel_tests/cli.rb +323 -0
- data/lib/actir/parallel_tests/grouper.rb +44 -0
- data/lib/actir/parallel_tests/parallel_tests.rb +77 -0
- data/lib/actir/parallel_tests/report/html_formatter.rb +417 -0
- data/lib/actir/parallel_tests/report/html_reporter.rb +145 -0
- data/lib/actir/parallel_tests/test/logger.rb +74 -0
- data/lib/actir/parallel_tests/test/re_run.rb +153 -0
- data/lib/actir/parallel_tests/test/runner.rb +224 -0
- data/lib/actir/remote.rb +80 -0
- data/lib/actir/script/cookies_baidu.rb +143 -0
- data/lib/actir/version.rb +3 -0
- data/lib/actir/webdriver/browser.rb +201 -0
- data/lib/actir/webdriver/browser_options.rb +54 -0
- data/lib/actir/webdriver/config/devices.yaml +40 -0
- data/lib/actir/webdriver/devices.rb +31 -0
- data/lib/actir/webdriver/driver.rb +42 -0
- metadata +175 -0
data/lib/actir/remote.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'actir/config'
|
2
|
+
|
3
|
+
module Actir
|
4
|
+
|
5
|
+
#
|
6
|
+
# 远程测试环境相关的方法
|
7
|
+
#
|
8
|
+
# @author: Hub
|
9
|
+
#
|
10
|
+
# @Date: 2015-3-6
|
11
|
+
#
|
12
|
+
module Remote
|
13
|
+
|
14
|
+
#
|
15
|
+
# 获取远程selenium测试环境的docker镜像的IPAddress
|
16
|
+
#
|
17
|
+
# @example : get_remote_address 2
|
18
|
+
#
|
19
|
+
# @param : num [Fixnum] selenium-grid 的node镜像的节点数,对应多进程测试时的进程数量
|
20
|
+
#
|
21
|
+
# @return : [Array] IPAddress字符串的数组,形如["127.0.0.1:5555", "127.0.0.2:5555"]
|
22
|
+
#
|
23
|
+
def self.get_remote_address(num = 0)
|
24
|
+
@docker_cfg = Actir::Config.get("config.test_mode.docker") if @docker_cfg == nil
|
25
|
+
docker_ip = @docker_cfg["ip"]
|
26
|
+
node_sub_name = ( (@docker_cfg["name"] == nil || @docker_cfg["name"] == "") ? "-node" : @docker_cfg["name"])
|
27
|
+
docker_node_name = $env + node_sub_name
|
28
|
+
docker_inspect_str = "docker inspect -f='{{.NetworkSettings.IPAddress}}' \\`docker ps | grep #{docker_node_name} | grep 5900 | awk '{print \\$11}'\\`"
|
29
|
+
#需要判断执行脚本的环境是本地还是Linux服务器,本地需要ssh
|
30
|
+
puts docker_inspect_str if $debug
|
31
|
+
ip_str = if is_local?
|
32
|
+
`ssh root@#{docker_ip} "#{docker_inspect_str}"`
|
33
|
+
else
|
34
|
+
`#{docker_inspect_str}`
|
35
|
+
end
|
36
|
+
ip_array = ip_str.split("\n")
|
37
|
+
address = []
|
38
|
+
#如果入参num小于address.size,则返回num个address
|
39
|
+
#如果入参num大于address.size,则返回所有address
|
40
|
+
#如果入参num为0即默认的不传入参,则返回所有address
|
41
|
+
ip_array.each_with_index do |ip, i|
|
42
|
+
address[ip_array.size - 1 -i] = ip + ":" + @docker_cfg["port"]
|
43
|
+
end
|
44
|
+
if ip_array.size >= num && num != 0
|
45
|
+
return address.first(num)
|
46
|
+
else
|
47
|
+
return address
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# 获取远端docker-selenium的node镜像的个数
|
52
|
+
#
|
53
|
+
# @example : get_remote_num
|
54
|
+
#
|
55
|
+
# @return : [Fixnum] 远端docker-selenium的node镜像的个数
|
56
|
+
#
|
57
|
+
# def self.get_remote_num
|
58
|
+
# @docker_cfg = Actir::Config.get("config.test_mode.docker") if @docker_cfg == nil
|
59
|
+
# docker_ip = @docker_cfg["ip"]
|
60
|
+
# node_sub_name = ( (@docker_cfg["name"] == nil || @docker_cfg["name"] == "") ? "-node" : @docker_cfg["name"])
|
61
|
+
# docker_node_name = $env + node_sub_name
|
62
|
+
# #5900是node节点的端口号
|
63
|
+
# docker_inspect_str = "docker ps | grep #{docker_node_name} | grep -c 5900"
|
64
|
+
# num = if is_local?
|
65
|
+
# `ssh root@#{docker_ip} "#{docker_inspect_str}"`
|
66
|
+
# else
|
67
|
+
# `#{docker_inspect_str}`
|
68
|
+
# end
|
69
|
+
# num.to_i
|
70
|
+
# end
|
71
|
+
|
72
|
+
# 判断执行环境是否是本地环境(Mac)
|
73
|
+
def self.is_local?
|
74
|
+
hostname = `hostname`
|
75
|
+
hostname.include? "local"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'watir-webdriver'
|
2
|
+
|
3
|
+
module Actir
|
4
|
+
class CookiesBaidu
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# 更新百度账号所有的配置文件
|
9
|
+
def update_all(address = [])
|
10
|
+
baidu_card = Actir::Config.get("cookies.baidu")
|
11
|
+
baidu_card.each do |card, value|
|
12
|
+
update_cookies(card, address)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
#将所有百度支付卡的available状态恢复为true
|
17
|
+
def re_available
|
18
|
+
#每次登陆都判断一下cookies文件的上一次修改时间和当前时间
|
19
|
+
#如果日期不同,则刷新所有的cookies文件中baidu-card的状态
|
20
|
+
unless Actir::Config.is_same_day?("cookies")
|
21
|
+
Actir::Config.lock("cookies") do
|
22
|
+
str_array = IO.readlines(cookies_file)
|
23
|
+
str_array.each_with_index do |line, index|
|
24
|
+
if line =~ /available\:\s*false/
|
25
|
+
str_array[index] = line.gsub(/false/, 'true')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
cookiesfile = File.open(cookies_file, 'w')
|
29
|
+
str_array.each do |line|
|
30
|
+
cookiesfile.puts line
|
31
|
+
end
|
32
|
+
cookiesfile.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# 获取可用的百度账号的hash
|
38
|
+
# 返回值{card1 => {"username" => "xxx", "password"=>"iloveyouzan", xxx}}
|
39
|
+
def get_useful_card
|
40
|
+
# old_config = Actir::Config.config_dir
|
41
|
+
# Actir::Config.config_dir = script_config_path
|
42
|
+
#通过配置文件判断取出可用的卡的参数
|
43
|
+
baidu_card = Actir::Config.get("cookies.baidu")
|
44
|
+
card = {}
|
45
|
+
baidu_card.each do |key, value|
|
46
|
+
if value["available"] == true
|
47
|
+
#有可用的卡,取出cookies等参数
|
48
|
+
card.store(key, value)
|
49
|
+
break
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# Actir::Config.config_dir = old_config
|
53
|
+
card
|
54
|
+
end
|
55
|
+
|
56
|
+
# 设置不可用的卡
|
57
|
+
# 入参传入卡的key
|
58
|
+
def set_useless_card(card)
|
59
|
+
Actir::Config.set("cookies.baidu." + card + "." + "available", "false")
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# 更新配置文件中的baidu_cookies
|
65
|
+
def update_cookies(card, address = [])
|
66
|
+
#打开百付宝
|
67
|
+
open_baifubao(address)
|
68
|
+
#获取对应卡的账号密码
|
69
|
+
args = Actir::Config.get("cookies.baidu." + card)
|
70
|
+
#登录百付宝
|
71
|
+
login_baifubao(args["username"], args["password"])
|
72
|
+
#获取cookies
|
73
|
+
cookies = get_baifubao_cookies
|
74
|
+
#清除之前的cookies
|
75
|
+
modify_cookies(card, cookies)
|
76
|
+
puts "Already update baifubao's cookies"
|
77
|
+
end
|
78
|
+
|
79
|
+
# 访问百付宝主页
|
80
|
+
def open_baifubao(address = [])
|
81
|
+
if address.size == 0
|
82
|
+
if $mode == :remote
|
83
|
+
address = Actir::Remote.get_remote_address(1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
@browser = Browser.new(:www, :url => address[0], :mode => $mode)
|
87
|
+
@browser.goto "baifubao.com"
|
88
|
+
@browser
|
89
|
+
end
|
90
|
+
|
91
|
+
# 登录百付宝主页
|
92
|
+
def login_baifubao(username, password)
|
93
|
+
text_baifubao_username.set username
|
94
|
+
text_baifubao_password.set password
|
95
|
+
button_baifubao_login.click
|
96
|
+
end
|
97
|
+
|
98
|
+
# 获取cookies
|
99
|
+
def get_baifubao_cookies
|
100
|
+
id = @browser.cookies[:BAIDUID][:value]
|
101
|
+
ss = @browser.cookies[:BDUSS][:value]
|
102
|
+
@browser.close
|
103
|
+
#cookies = " BAIDUID:\s\s\s\s\s\s\"" + id + "\"\n BDUSS:\s\s\s\s\s\s\s\s\"" + ss + "\"\n"
|
104
|
+
#以hash形式返回
|
105
|
+
{:BAIDUID => id, :BDUSS => ss }
|
106
|
+
end
|
107
|
+
|
108
|
+
def modify_cookies(card, cookies)
|
109
|
+
cookies.each do |key, value|
|
110
|
+
Actir::Config.set("cookies.baidu."+card+"."+key.to_s , "\""+value.to_s+"\"")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# 配置文件所在文件夹的路径
|
115
|
+
def script_config_path
|
116
|
+
File.join(File.dirname(__FILE__), "config")
|
117
|
+
end
|
118
|
+
|
119
|
+
# 配置文件相对路径
|
120
|
+
def cookies_file
|
121
|
+
File.join(Actir::Config.config_dir, "/cookies.yaml")
|
122
|
+
end
|
123
|
+
|
124
|
+
def text_baifubao_username
|
125
|
+
@browser.text_field(:id => 'TANGRAM__PSP_4__userName')
|
126
|
+
end
|
127
|
+
|
128
|
+
def text_baifubao_password
|
129
|
+
@browser.text_field(:id => 'TANGRAM__PSP_4__password')
|
130
|
+
end
|
131
|
+
|
132
|
+
def button_baifubao_login
|
133
|
+
#@browser.input(:xpath, "//input[@id='TANGRAM__PSP_4__submit']")
|
134
|
+
@browser.input(:id => 'TANGRAM__PSP_4__submit')
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
#Actir::CookiesBaidu.re_available
|
143
|
+
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'actir/webdriver/driver'
|
2
|
+
|
3
|
+
#
|
4
|
+
# 初始化测试用浏览器
|
5
|
+
#
|
6
|
+
# @author: Hub
|
7
|
+
#
|
8
|
+
# @Date: 2015-1-5
|
9
|
+
#
|
10
|
+
class Browser
|
11
|
+
|
12
|
+
attr_reader :browser
|
13
|
+
|
14
|
+
PHANTOMJS_SIZE = {'width' => 1334, 'height' => 750}
|
15
|
+
REMOTE_SIZE = {'width' => 1050, 'height' => 611}
|
16
|
+
|
17
|
+
#
|
18
|
+
# 初始化函数
|
19
|
+
#
|
20
|
+
# 可以配置测试的mode:
|
21
|
+
# 1.:env
|
22
|
+
# :local 本地环境上测试
|
23
|
+
# :remote 远程测试环境上测试
|
24
|
+
# 2.:browser
|
25
|
+
# :chrome
|
26
|
+
# :firefox
|
27
|
+
# 3.:agent wap页面的useragent类型
|
28
|
+
# :iphone
|
29
|
+
# :android_phone
|
30
|
+
# 4.:address 当env为:remote时,url指定远程执行脚本的地址
|
31
|
+
#
|
32
|
+
def initialize(type = :www, *args)
|
33
|
+
args = init_args(*args)
|
34
|
+
@browser_type = args[:browser]
|
35
|
+
@agent = args[:agent]
|
36
|
+
@env = args[:mode]
|
37
|
+
if @env == :remote
|
38
|
+
@url = if args[:url]
|
39
|
+
"http://#{args[:url]}/wd/hub"
|
40
|
+
else
|
41
|
+
#TO-DO ,远程模式没有传入IP,改成local模式
|
42
|
+
@env = :local
|
43
|
+
puts "selenium-node's IPAddress is null. switch to local mode"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
if type == :www
|
47
|
+
@watir_browser = browser_www
|
48
|
+
elsif type == :wap
|
49
|
+
@watir_browser = browser_wap
|
50
|
+
else
|
51
|
+
raise "error browser type , please send args with :www or :wap!"
|
52
|
+
end
|
53
|
+
define_page_method
|
54
|
+
end
|
55
|
+
|
56
|
+
# 初始化入参
|
57
|
+
def init_args(args = {})
|
58
|
+
unless args.has_key?(:mode)
|
59
|
+
#若通过actir执行测试用例,则会配置ENV的模式
|
60
|
+
if ENV["mode"]
|
61
|
+
args[:mode] = ENV["mode"].to_sym
|
62
|
+
else
|
63
|
+
#若ENV为空,则读取配置文件,判断有无配置文件
|
64
|
+
if File.exist?(config_file)
|
65
|
+
args[:mode] = $config["config"]["test_mode"]["env"]
|
66
|
+
else
|
67
|
+
args[:mode] = :local
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
unless args.has_key?(:browser)
|
72
|
+
if File.exist?(config_file)
|
73
|
+
args[:browser] = $config["config"]["test_mode"]["browser"]
|
74
|
+
else
|
75
|
+
args[:browser] = :chrome
|
76
|
+
end
|
77
|
+
end
|
78
|
+
args[:agent] = :iphone unless args.has_key?(:agent)
|
79
|
+
args[:url] = $address unless args.has_key?(:url)
|
80
|
+
args
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
#
|
85
|
+
# 打开普通www浏览器
|
86
|
+
#
|
87
|
+
def browser_www
|
88
|
+
case @env
|
89
|
+
when :local
|
90
|
+
#本地chrome浏览器
|
91
|
+
browser = Watir::Browser.new @browser_type
|
92
|
+
when :remote
|
93
|
+
#远程服务器的chrome浏览器
|
94
|
+
browser = Watir::Browser.new(
|
95
|
+
:remote,
|
96
|
+
:desired_capabilities => @browser_type,
|
97
|
+
:url => @url
|
98
|
+
)
|
99
|
+
end
|
100
|
+
#重新设置窗口大小,不然phantomjs的ghost driver各种问题
|
101
|
+
if @browser_type == :phantomjs
|
102
|
+
browser.window.resize_to(PHANTOMJS_SIZE["width"], PHANTOMJS_SIZE["height"])
|
103
|
+
elsif @env == :remote
|
104
|
+
browser.window.resize_to(REMOTE_SIZE["width"], REMOTE_SIZE["height"])
|
105
|
+
end
|
106
|
+
browser
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# 通过useragent打开WAP页面
|
111
|
+
#
|
112
|
+
# local模式下直接打开本地chrome浏览器,或者使用phantomjs无界面执行
|
113
|
+
#
|
114
|
+
# remote模式下通过设置chrome的switches参数远程打开测试环境上的chrome浏览器
|
115
|
+
#
|
116
|
+
# TO-DO: remote模式的phantomjs
|
117
|
+
#
|
118
|
+
def browser_wap
|
119
|
+
case @env
|
120
|
+
when :local
|
121
|
+
driver = Actir::Webdriver.driver(:browser => @browser_type, :agent => @agent)
|
122
|
+
when :remote
|
123
|
+
driver = Actir::Webdriver.driver(:browser => @browser_type, :agent => @agent, :url => @url)
|
124
|
+
end
|
125
|
+
browser = Watir::Browser.new driver
|
126
|
+
if @browser_type == :phantomjs
|
127
|
+
#重新设置窗口大小,不然phantomjs的ghost driver各种问题 = =
|
128
|
+
browser.window.resize_to(PHANTOMJS_SIZE["width"], PHANTOMJS_SIZE["height"])
|
129
|
+
end
|
130
|
+
browser
|
131
|
+
end
|
132
|
+
|
133
|
+
# 自动定义驱动需要调用的所有元素相关的方法
|
134
|
+
# 需要所有的页面元素相关的类名以XXXPage方式命名
|
135
|
+
def define_page_method
|
136
|
+
p Module.constants.grep(/(Page|Wap)$/) if $debug
|
137
|
+
Module.constants.grep(/(Page|Wap)$/).each do |page_klass|
|
138
|
+
if basic_page?(page_klass)
|
139
|
+
method = ""
|
140
|
+
if page_klass.to_s =~ /Page$/
|
141
|
+
method_name = page_klass.to_s.gsub!(/Page$/, "_page")
|
142
|
+
elsif page_klass.to_s =~ /Wap$/
|
143
|
+
method_name = page_klass.to_s.gsub!(/Wap$/, "_wap")
|
144
|
+
end
|
145
|
+
self.class.send :define_method, "#{method_name.downcase}" do
|
146
|
+
page = Module.const_get(page_klass).new(@watir_browser)
|
147
|
+
page
|
148
|
+
end #define_method
|
149
|
+
puts "defined #{method_name.downcase}" if $debug
|
150
|
+
end #if
|
151
|
+
end #each
|
152
|
+
end
|
153
|
+
|
154
|
+
def valid_page_klass? klass
|
155
|
+
return false if klass.eql?(:Page)
|
156
|
+
return false if klass.eql?(:BasicPage)
|
157
|
+
Module.const_get(klass) < Actir::BasicPage
|
158
|
+
end
|
159
|
+
alias_method :basic_page?, :valid_page_klass?
|
160
|
+
|
161
|
+
def method_missing(m, *args, &blk)
|
162
|
+
if @watir_browser.respond_to? m
|
163
|
+
@watir_browser.send(m, *args, &blk)
|
164
|
+
else
|
165
|
+
super
|
166
|
+
end #if
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# 在同一个浏览器的新tab页上打开指定url的页面,并自动将焦点放到新开窗口中
|
171
|
+
#
|
172
|
+
# @example :
|
173
|
+
# browser.new_window('youzan.com') do
|
174
|
+
# action
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# @param url : [String] url字符串
|
178
|
+
#
|
179
|
+
def new_window url
|
180
|
+
@watir_browser.execute_script("window.open(\"#{url}\")")
|
181
|
+
#获取浏览器中的窗口数目
|
182
|
+
window_num = @watir_browser.windows.length
|
183
|
+
#默认要操作的新窗口都是最后一个窗口
|
184
|
+
@watir_browser.windows[window_num - 1].use do
|
185
|
+
#重新设置窗口大小,不然phantomjs的ghost driver各种问题 = =
|
186
|
+
if @browser_type == :phantomjs
|
187
|
+
@watir_browser.window.resize_to(PHANTOMJS_SIZE["width"],PHANTOMJS_SIZE["height"])
|
188
|
+
end
|
189
|
+
#使用执行块,由调用者决定在新开的订单详情页面内做些啥
|
190
|
+
yield
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def config_file
|
197
|
+
File.join($project_path, "config", "config.yaml")
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'facets/hash/except'
|
2
|
+
|
3
|
+
module Actir
|
4
|
+
module Webdriver
|
5
|
+
class BrowserOptions
|
6
|
+
|
7
|
+
def initialize(opts, user_agent_string)
|
8
|
+
@options = opts
|
9
|
+
options[:browser] ||= :chrome
|
10
|
+
options[:agent] ||= :iphone
|
11
|
+
#options[:orientation] ||= :portrait
|
12
|
+
initialize_for_browser(user_agent_string)
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(*args, &block)
|
16
|
+
m = args.first
|
17
|
+
value = options[m]
|
18
|
+
super unless value
|
19
|
+
value.downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def browser_options
|
23
|
+
#options.except(:browser, :agent, :orientation)
|
24
|
+
options.except(:browser, :agent)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def options
|
30
|
+
@options ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_for_browser(user_agent_string)
|
34
|
+
case options[:browser]
|
35
|
+
when :firefox
|
36
|
+
options[:profile] ||= Selenium::WebDriver::Firefox::Profile.new
|
37
|
+
options[:profile]['general.useragent.override'] = user_agent_string
|
38
|
+
when :chrome
|
39
|
+
options[:switches] ||= []
|
40
|
+
options[:switches] << "--user-agent=#{user_agent_string}"
|
41
|
+
# add bu Hub
|
42
|
+
# support phantomjs
|
43
|
+
when :phantomjs
|
44
|
+
options[:desired_capabilities] ||= Selenium::WebDriver::Remote::Capabilities.phantomjs(
|
45
|
+
"phantomjs.page.settings.userAgent" => user_agent_string
|
46
|
+
)
|
47
|
+
else
|
48
|
+
raise "WebDriver currently only supports :chrome, :firefox and :phantomjs"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|