actir 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|