watchdoge 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/watchdoge/configuration.rb +13 -9
- data/lib/watchdoge/cookie_pool.rb +4 -13
- data/lib/watchdoge/image_diff.rb +16 -6
- data/lib/watchdoge/notification/matter_most.rb +11 -11
- data/lib/watchdoge/notification/{slack.rb → slack_webhook.rb} +3 -3
- data/lib/watchdoge/notification.rb +29 -6
- data/lib/watchdoge/rails/railtie.rb +6 -2
- data/lib/watchdoge/regression/dsl.rb +30 -12
- data/lib/watchdoge/regression/utils.rb +26 -18
- data/lib/watchdoge/regression.rb +22 -40
- data/lib/watchdoge/version.rb +2 -1
- data/lib/watchdoge/{install.rb → webdriver_manager.rb} +10 -2
- data/lib/watchdoge/worker.rb +58 -42
- data/lib/watchdoge.rb +5 -5
- data/watchdoge.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3e163dcbfe0adf1bdd0cad7294c47475d7007792820a79a80d150cc86d58227
|
4
|
+
data.tar.gz: 8c03e0dff98141f87e822ac561a47b3267754e258425424d968aa5aef05c2c87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f6f3564b357805882226d960aa97b69ab4f11d3a1d8f82d5a176995f5b15d9e42fbfadf9c38c9fed09b43cf2c33dda79abe7c316b277e599c0ae4def9d853e6
|
7
|
+
data.tar.gz: 8a7af25b9f84fe6475471dd5bd98207f51d3253f2197b37749126712125e052ed1732081eb479d582d3bb07e9cd98fe35006924d8beab30c73dacc8fa42cff5b
|
@@ -1,22 +1,23 @@
|
|
1
|
+
# All configurable variables
|
2
|
+
|
3
|
+
# spec:
|
4
|
+
#
|
5
|
+
|
1
6
|
require 'watir'
|
2
7
|
|
3
8
|
module WatchDoge
|
4
9
|
class Configuration
|
5
10
|
class Hooks
|
6
|
-
attr_accessor :sign_in
|
7
|
-
attr_accessor :sign_out
|
8
11
|
attr_accessor :before_regression
|
9
12
|
attr_accessor :after_regression
|
10
13
|
attr_accessor :before_scenario
|
11
14
|
attr_accessor :after_scenario
|
12
15
|
|
13
16
|
def initialize
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@before_scenario = -> {}
|
19
|
-
@after_scenario = -> {}
|
17
|
+
@before_regression = []
|
18
|
+
@after_regression = []
|
19
|
+
@before_scenario = []
|
20
|
+
@after_scenario = []
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
@@ -34,6 +35,8 @@ module WatchDoge
|
|
34
35
|
attr_accessor :regression_dir
|
35
36
|
|
36
37
|
attr_accessor :hooks
|
38
|
+
attr_accessor :sign_in_proc
|
39
|
+
attr_accessor :sign_out_proc
|
37
40
|
|
38
41
|
attr_accessor :chrome_version
|
39
42
|
attr_accessor :firefox_version
|
@@ -50,7 +53,6 @@ module WatchDoge
|
|
50
53
|
@engine = Watir::Browser
|
51
54
|
@browser = :firefox
|
52
55
|
|
53
|
-
# cuurently using local fs, probably support NoSQL also maybe?
|
54
56
|
@cookie_pool = @base_dir + '/.doge_cookie'
|
55
57
|
|
56
58
|
@http_timeout = 120
|
@@ -81,6 +83,8 @@ module WatchDoge
|
|
81
83
|
end
|
82
84
|
|
83
85
|
@hooks = WatchDoge::Configuration::Hooks.new
|
86
|
+
@sign_in_proc = -> (worker, resource) {}
|
87
|
+
@sign_out_proc = -> (worker, resource) {}
|
84
88
|
|
85
89
|
@chrome_version = nil
|
86
90
|
@firefox_version = nil
|
@@ -1,25 +1,16 @@
|
|
1
|
+
# TODO: Improve or should just remove it.
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
require 'digest'
|
3
5
|
|
4
6
|
module WatchDoge
|
5
7
|
module CookiePool
|
6
8
|
class << self
|
7
|
-
def source
|
9
|
+
def source uid, uri = nil
|
8
10
|
suffix = "_#{Digest::MD5.hexdigest(uri.to_s)[0..5]}" if uri
|
9
11
|
|
10
|
-
"#{WatchDoge.configuration.cookie_pool}/#{
|
12
|
+
"#{WatchDoge.configuration.cookie_pool}/#{uid}#{suffix}"
|
11
13
|
end
|
12
|
-
|
13
|
-
# TODO: implement these two methods return Cookie object in future.
|
14
|
-
# def save uuid, json
|
15
|
-
# File.write source(uuid), json
|
16
|
-
# end
|
17
|
-
|
18
|
-
# def load uuid
|
19
|
-
# return "" unless File.exist? source(uuid)
|
20
|
-
|
21
|
-
# JSON.parse File.read(source(uuid))
|
22
|
-
# end
|
23
14
|
end
|
24
15
|
end
|
25
16
|
end
|
data/lib/watchdoge/image_diff.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
|
+
# generating diff image of two input image
|
2
|
+
|
3
|
+
# usage:
|
4
|
+
# diff_image = WatchDoge::ImageDiff.diff(chunky_png_image, another_chunky_png_image)
|
5
|
+
|
6
|
+
# spec:
|
7
|
+
# 1. input only accept ChunkyPNG::Image, output image is also ChunkyPNG::Image
|
8
|
+
# 2. Testing RGBA pixel by pixel. blending mask color if diff detected
|
9
|
+
|
10
|
+
# todo: should improve testing boundary for more accurate diff imagenot.
|
11
|
+
|
1
12
|
module WatchDoge
|
2
13
|
module ImageDiff
|
3
|
-
OPACITY = 0.7
|
4
|
-
HIGHLIGHT_COLOR = "#F163FF#{(OPACITY * 256).to_i.to_s(16).upcase}"
|
5
|
-
|
6
14
|
class << self
|
7
|
-
def diff input_image, reference_image
|
15
|
+
def diff input_image, reference_image, opacity: 0.7, mask_base_color: "#F163FF"
|
8
16
|
return nil if input_image.pixels.hash == reference_image.pixels.hash
|
9
17
|
|
18
|
+
mask_color = mask_base_color.concat (opacity * 256).to_i.to_s(16).upcase
|
19
|
+
|
10
20
|
dup_input_image = input_image.dup
|
11
21
|
|
12
22
|
input_image.height.times do |y|
|
@@ -14,7 +24,7 @@ module WatchDoge
|
|
14
24
|
|
15
25
|
if y >= reference_image.height
|
16
26
|
pixels.each_with_index do |pixel, x|
|
17
|
-
dup_input_image.compose_pixel(x, y,
|
27
|
+
dup_input_image.compose_pixel(x, y, mask_color)
|
18
28
|
end
|
19
29
|
else
|
20
30
|
reference_pixels = reference_image.row(y)
|
@@ -24,7 +34,7 @@ module WatchDoge
|
|
24
34
|
pixels.each_with_index do |pixel, x|
|
25
35
|
if pixels[x] != reference_pixels[x]
|
26
36
|
dup_input_image.compose_pixel(x, y, reference_pixels[x])
|
27
|
-
dup_input_image.compose_pixel(x, y,
|
37
|
+
dup_input_image.compose_pixel(x, y, mask_color)
|
28
38
|
end
|
29
39
|
end
|
30
40
|
end
|
@@ -16,23 +16,23 @@ module WatchDoge
|
|
16
16
|
def push message
|
17
17
|
case message
|
18
18
|
when String
|
19
|
-
|
19
|
+
post matter_most_meta(message)
|
20
20
|
when ChunkyPNG::Image
|
21
|
-
|
21
|
+
post_file matter_most_meta(message)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def
|
28
|
-
Net::HTTP.post
|
27
|
+
def post meta
|
28
|
+
Net::HTTP.post api_uri('/posts'),
|
29
29
|
meta.to_json,
|
30
30
|
"Content-Type" => "application/json",
|
31
31
|
"Authorization" => "Bearer #{@auth_token}"
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
uri =
|
34
|
+
def post_file meta
|
35
|
+
uri = api_uri("/files")
|
36
36
|
|
37
37
|
req = Net::HTTP::Post::Multipart.new uri.path, meta
|
38
38
|
req.add_field("Authorization", "Bearer #{@auth_token}")
|
@@ -45,22 +45,22 @@ module WatchDoge
|
|
45
45
|
|
46
46
|
file_id = JSON.parse(res)['file_infos'].first['id']
|
47
47
|
|
48
|
-
|
48
|
+
post_file_to_channel file_id
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
51
|
+
def post_file_to_channel file_id
|
52
|
+
post({
|
53
53
|
channel_id: @channel_id,
|
54
54
|
message: 'file uploaded from WatchDoge',
|
55
55
|
file_ids: [file_id]
|
56
56
|
})
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
59
|
+
def api_uri path
|
60
60
|
URI("#{@host}/api/v4#{path}")
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
63
|
+
def matter_most_meta message
|
64
64
|
case message
|
65
65
|
when String
|
66
66
|
{
|
@@ -5,13 +5,13 @@ require 'uri'
|
|
5
5
|
|
6
6
|
module WatchDoge
|
7
7
|
module Notification
|
8
|
-
class
|
8
|
+
class SlackWebhook
|
9
9
|
def initialize opt
|
10
10
|
@incoming_url = opt[:incoming_url]
|
11
11
|
end
|
12
12
|
|
13
13
|
def push message
|
14
|
-
body =
|
14
|
+
body = slack_body(message).to_json
|
15
15
|
Net::HTTP.post URI(@incoming_url),
|
16
16
|
body,
|
17
17
|
"Content-Type" => "application/json"
|
@@ -19,7 +19,7 @@ module WatchDoge
|
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
-
def
|
22
|
+
def slack_body message
|
23
23
|
case message
|
24
24
|
when String
|
25
25
|
{
|
@@ -1,15 +1,39 @@
|
|
1
|
+
# Notification send message to sources setting in configuration
|
2
|
+
|
3
|
+
# usage:
|
4
|
+
# WatchDoge::Notification.push(message)
|
5
|
+
|
6
|
+
# spec:
|
7
|
+
# 1. could adding custom sources
|
8
|
+
# 2. custom source MUST under WatchDoge::Notification namespace and having #push method
|
9
|
+
|
10
|
+
# for example:
|
11
|
+
# class WatchDoge::Notification::CustomSource
|
12
|
+
# # args passing from value of WatchDoge::Configuration.notifications['CustomSource']
|
13
|
+
# def initialize **args
|
14
|
+
# # implement your source setup
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # called by WatchDoge::Notification.push(msg)
|
18
|
+
# def push msg
|
19
|
+
# # do your source push here
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
|
23
|
+
# built-in sources
|
24
|
+
require 'watchdoge/notification/slack_webhook'
|
25
|
+
require 'watchdoge/notification/matter_most'
|
26
|
+
|
1
27
|
module WatchDoge
|
2
28
|
module Notification
|
3
|
-
Dir["#{File.expand_path File.dirname(__FILE__)}/notification/*.rb"].each {|file| require file }
|
4
|
-
|
5
29
|
class << self
|
6
30
|
def push message
|
7
31
|
sources = WatchDoge.configuration.notifications
|
8
32
|
|
9
33
|
raise "can't find notification sources in configuration" if sources.empty?
|
10
34
|
|
11
|
-
sources.each do |
|
12
|
-
source_class = get_source_class
|
35
|
+
sources.each do |klass, source_args|
|
36
|
+
source_class = get_source_class klass
|
13
37
|
source = source_class.new source_args
|
14
38
|
source.push message
|
15
39
|
end
|
@@ -17,8 +41,7 @@ module WatchDoge
|
|
17
41
|
|
18
42
|
private
|
19
43
|
|
20
|
-
def get_source_class
|
21
|
-
klass = source_sym.to_s.split('_').collect(&:capitalize).join
|
44
|
+
def get_source_class klass
|
22
45
|
"WatchDoge::Notification::#{klass}".constantize
|
23
46
|
end
|
24
47
|
end
|
@@ -6,13 +6,17 @@ module WatchDoge
|
|
6
6
|
namespace :watchdoge do
|
7
7
|
desc "generating reference images"
|
8
8
|
task :make_reference => :environment do
|
9
|
-
WatchDoge::Regression::Manager.make_reference
|
9
|
+
WatchDoge::Regression::Manager.run_all_scenarios :make_reference
|
10
10
|
end
|
11
11
|
desc "scrap screenshots and compare with reference images"
|
12
12
|
task :regression => :environment do
|
13
|
-
WatchDoge::Regression::Manager.regression
|
13
|
+
WatchDoge::Regression::Manager.run_all_scenarios :regression
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
18
|
+
initializer "watchdoge.initialize" do |app|
|
19
|
+
WatchDoge.initialize!
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
@@ -1,3 +1,12 @@
|
|
1
|
+
# DSL for described regression scenario
|
2
|
+
|
3
|
+
# usage:
|
4
|
+
# compare 'https://google.com'
|
5
|
+
|
6
|
+
# spec: todo
|
7
|
+
|
8
|
+
# todo: finish document
|
9
|
+
|
1
10
|
module WatchDoge
|
2
11
|
module Regression
|
3
12
|
module DSL
|
@@ -8,15 +17,14 @@ module WatchDoge
|
|
8
17
|
end
|
9
18
|
|
10
19
|
def sign_in_as resoruce
|
11
|
-
|
12
20
|
puts "[sign_in_as] #{resoruce}"
|
13
|
-
WatchDoge.
|
21
|
+
WatchDoge.configuration.sign_in_proc.call(@worker, resoruce)
|
14
22
|
|
15
23
|
return unless block_given?
|
16
24
|
|
17
25
|
yield(@worker)
|
18
26
|
puts "sign_out #{resoruce}"
|
19
|
-
WatchDoge.
|
27
|
+
WatchDoge.configuration.sign_out_proc.call(@worker, resoruce)
|
20
28
|
end
|
21
29
|
|
22
30
|
def with_viewport **kwargs
|
@@ -27,13 +35,7 @@ module WatchDoge
|
|
27
35
|
|
28
36
|
def compare path, **kwargs
|
29
37
|
path = URI.join(WatchDoge.configuration.host, path).to_s
|
30
|
-
|
31
|
-
@reference_file_name =
|
32
|
-
if kwargs[:with]
|
33
|
-
kwargs[:with]
|
34
|
-
else
|
35
|
-
path.gsub(/\//, '_')
|
36
|
-
end
|
38
|
+
@reference_file_name = WatchDoge::Regression::Utils.path_to_filename(kwargs[:with] || path)
|
37
39
|
|
38
40
|
puts "[compare] #{path} #{ ('[with]'+ @reference_file_name) if kwargs[:with] }"
|
39
41
|
|
@@ -53,8 +55,24 @@ module WatchDoge
|
|
53
55
|
})
|
54
56
|
end
|
55
57
|
|
56
|
-
@worker.
|
57
|
-
@
|
58
|
+
@worker.resize_by_viewport(@view_port) do
|
59
|
+
case @regression_flag
|
60
|
+
when :make_reference
|
61
|
+
puts " -> make reference images on worker #{@worker}"
|
62
|
+
@worker.screenshot.save reference_image
|
63
|
+
when :regression
|
64
|
+
puts " -> compare reference images on worker #{@worker}"
|
65
|
+
reference_image = WatchDoge::Regression::Utils.load_png self.reference_image
|
66
|
+
cycle_image = WatchDoge::Regression::Utils.load_png @worker.screenshot.png
|
67
|
+
diff = WatchDoge::ImageDiff.diff(cycle_image, reference_image)
|
68
|
+
if diff
|
69
|
+
if ENV['WATCHDOGE_DEBUG']
|
70
|
+
WatchDoge::Notification.push cycle_image
|
71
|
+
WatchDoge::Notification.push reference_image
|
72
|
+
end
|
73
|
+
WatchDoge::Notification.push diff
|
74
|
+
end
|
75
|
+
end
|
58
76
|
end
|
59
77
|
|
60
78
|
@teardown_proc.call if @teardown_proc
|
@@ -1,33 +1,41 @@
|
|
1
|
+
# this file is part of Regression, mostly helpers methods
|
2
|
+
|
1
3
|
require 'digest'
|
2
4
|
|
3
5
|
module WatchDoge
|
4
6
|
module Regression
|
5
7
|
module Utils
|
6
|
-
|
7
|
-
|
8
|
+
class << self
|
9
|
+
def load_png input
|
10
|
+
raise 'Invalid Input' unless input.respond_to? :length
|
8
11
|
|
9
|
-
|
12
|
+
png_magic = ["89", "50", "4E", "47"]
|
10
13
|
|
11
|
-
|
12
|
-
|
14
|
+
is_png_blob =
|
15
|
+
(input.length > 3 && input[0..3].each_byte.map {|t| t.to_s(16).upcase} == png_magic)
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
if is_png_blob
|
18
|
+
ChunkyPNG::Image.from_blob input
|
19
|
+
else
|
20
|
+
ChunkyPNG::Image.from_file input
|
21
|
+
end
|
18
22
|
end
|
19
|
-
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
def path_to_filename path
|
25
|
+
path.gsub(/\//, '_')
|
26
|
+
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
def regression_dir
|
29
|
+
WatchDoge.configuration.regression_dir
|
30
|
+
end
|
31
|
+
|
32
|
+
def scenarios_dir
|
33
|
+
"#{regression_dir}/scenarios"
|
34
|
+
end
|
28
35
|
|
29
|
-
|
30
|
-
|
36
|
+
def reference_dir
|
37
|
+
"#{regression_dir}/reference"
|
38
|
+
end
|
31
39
|
end
|
32
40
|
end
|
33
41
|
end
|
data/lib/watchdoge/regression.rb
CHANGED
@@ -1,62 +1,44 @@
|
|
1
|
+
# Regression testing realtime screenshot with reference images for scenario described by DSL
|
2
|
+
|
3
|
+
# usage:
|
4
|
+
# WatchDoge::Regression.run_all_scenarios(:make_reference)
|
5
|
+
# WatchDoge::Regression.run_all_scenarios(:regression)
|
6
|
+
|
7
|
+
# spec:
|
8
|
+
# 1. rake tasks `rake watchdoge:make_reference` and `rake watchdoge:regression` while in Rails
|
9
|
+
# 2. generate scenario template by Rails generator
|
10
|
+
|
11
|
+
# todo: support run single scenario
|
12
|
+
|
13
|
+
require 'watchdoge/regression/dsl'
|
14
|
+
require 'watchdoge/regression/utils'
|
15
|
+
|
1
16
|
module WatchDoge
|
2
17
|
module Regression
|
3
|
-
Dir["#{File.expand_path File.dirname(__FILE__)}/regression/*.rb"].each {|file| require file }
|
4
|
-
|
5
18
|
class Manager
|
6
|
-
extend WatchDoge::Regression::Utils
|
7
19
|
include WatchDoge::Regression::DSL
|
8
20
|
|
9
21
|
class << self
|
10
|
-
def regression
|
11
|
-
run_all_scenarios :regression
|
12
|
-
end
|
13
|
-
def make_reference
|
14
|
-
run_all_scenarios :make_reference
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
22
|
def run_all_scenarios regression_flag
|
20
|
-
WatchDoge.hooks.before_regression.call
|
23
|
+
WatchDoge.hooks.before_regression.each { |t| t.call }
|
21
24
|
|
22
|
-
Dir["#{scenarios_dir}/*"].each do |scenario_path|
|
23
|
-
WatchDoge.hooks.before_scenario.call
|
25
|
+
Dir["#{WatchDoge::Regression::Utils.scenarios_dir}/*"].each do |scenario_path|
|
26
|
+
WatchDoge.hooks.before_scenario.each { |t| t.call }
|
24
27
|
|
25
28
|
mgr = self.new scenario_path, regression_flag: regression_flag
|
26
29
|
mgr.eval_scenario
|
27
30
|
|
28
|
-
WatchDoge.hooks.after_scenario.call
|
31
|
+
WatchDoge.hooks.after_scenario.each { |t| t.call }
|
29
32
|
end
|
30
33
|
|
31
|
-
WatchDoge.hooks.after_regression.call
|
34
|
+
WatchDoge.hooks.after_regression.each { |t| t.call }
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
35
38
|
def initialize scenario_path, regression_flag:
|
36
39
|
@scenario_path = scenario_path
|
37
40
|
@scenario_name = File.basename @scenario_path, '.rb'
|
38
|
-
|
39
|
-
case regression_flag
|
40
|
-
when :make_reference
|
41
|
-
@regression_proc = -> (worker) {
|
42
|
-
puts " -> make reference images on worker #{worker}"
|
43
|
-
worker.screenshot.save reference_image
|
44
|
-
}
|
45
|
-
when :regression
|
46
|
-
@regression_proc = -> (worker) {
|
47
|
-
puts " -> compare reference images on worker #{worker}"
|
48
|
-
reference_image = self.class.load_png self.reference_image
|
49
|
-
cycle_image = self.class.load_png worker.screenshot.png
|
50
|
-
diff = WatchDoge::ImageDiff.diff(cycle_image, reference_image)
|
51
|
-
if diff
|
52
|
-
if ENV['WATCHDOGE_DEBUG']
|
53
|
-
WatchDoge::Notification.push cycle_image
|
54
|
-
WatchDoge::Notification.push reference_image
|
55
|
-
end
|
56
|
-
WatchDoge::Notification.push diff
|
57
|
-
end
|
58
|
-
}
|
59
|
-
end
|
41
|
+
@regression_flag = regression_flag
|
60
42
|
|
61
43
|
@setup_proc = nil
|
62
44
|
@teardown_proc = nil
|
@@ -75,7 +57,7 @@ module WatchDoge
|
|
75
57
|
end
|
76
58
|
|
77
59
|
def reference_image
|
78
|
-
|
60
|
+
WatchDoge::Regression::Utils.reference_dir + "/#{@scenario_name}/#{@reference_file_name}.png"
|
79
61
|
end
|
80
62
|
end
|
81
63
|
end
|
data/lib/watchdoge/version.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
|
+
# WebdriverManager setup specific version of webdriver in configuration for Selenium
|
2
|
+
|
3
|
+
# usage:
|
4
|
+
# WatchDoge.configuration.firefox_version = '0.17.0'
|
5
|
+
# WatchDoge::WebdriverManager.new(:firefox)
|
6
|
+
|
7
|
+
# todo: shouldn't this class singleton with #setup instead of initializing instance?
|
8
|
+
|
1
9
|
require 'net_http_ssl_fix'
|
2
10
|
require 'webdrivers'
|
3
11
|
|
4
12
|
module WatchDoge
|
5
|
-
class
|
13
|
+
class WebdriverManager
|
6
14
|
attr_reader :driver
|
7
15
|
attr_reader :install_version
|
8
16
|
attr_reader :install_path
|
@@ -16,7 +24,7 @@ module WatchDoge
|
|
16
24
|
Webdrivers::Geckodriver
|
17
25
|
end
|
18
26
|
|
19
|
-
@install_version =
|
27
|
+
@install_version = WatchDoge.configuration.send("#{browser}_version") || @driver.latest_version.to_s
|
20
28
|
|
21
29
|
@install_path = "#{WatchDoge.configuration.web_drivers_dir}/#{browser}/#{@install_version}/"
|
22
30
|
Webdrivers.install_dir = @install_path
|
data/lib/watchdoge/worker.rb
CHANGED
@@ -1,45 +1,70 @@
|
|
1
|
+
# Worker contains Selenium browser interface. and installing required WebDriver binaries
|
2
|
+
|
3
|
+
# usage:
|
4
|
+
# worker = WatchDoge::Worker.new("WORKER_NAME")
|
5
|
+
# worker.goto "http://google.com"
|
6
|
+
# screenshot = worker.fullpage_screenshot
|
7
|
+
|
8
|
+
# spec:
|
9
|
+
# 1. Worker instance having customized configuration Selenium as its engine
|
10
|
+
# 2. Worker engine default to headless mode
|
11
|
+
# 3. Worker passing all missing method to its engine
|
12
|
+
|
13
|
+
# todo:
|
14
|
+
# 1. improve or remove cookie related functions, is it necessary for frontend regression?
|
15
|
+
|
1
16
|
module WatchDoge
|
2
17
|
class Worker
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
18
|
+
# options:
|
19
|
+
# 1. width: browser's width, default to 1280
|
20
|
+
# 2. height: browser's height, default to 768
|
21
|
+
# 3. http_client: instance of Selenium::WebDriver::Remote::Http
|
22
|
+
# 4. all acceptable arguments of Watir::Browser class
|
23
|
+
def initialize uid, options={}
|
24
|
+
@http = Selenium::WebDriver::Remote::Http::Default.new
|
25
|
+
@browser = WatchDoge.configuration.browser
|
26
|
+
|
27
|
+
# HTTP timeout default to 120 sec
|
28
|
+
@http.read_timeout = options.delete(:timeout) || WatchDoge.configuration.http_timeout
|
29
|
+
|
30
|
+
@uid = uid
|
7
31
|
|
8
|
-
|
32
|
+
default_options = {
|
9
33
|
width: 1280,
|
10
34
|
height: 768,
|
11
35
|
headless: true,
|
36
|
+
|
37
|
+
# Selenium HTTP configuration
|
12
38
|
http_client: @http
|
13
|
-
}.merge(
|
39
|
+
}.merge(options).merge(browser_profile)
|
14
40
|
|
15
|
-
|
16
|
-
|
41
|
+
# intall required version driver
|
42
|
+
WatchDoge::WebdriverManager.new @browser
|
43
|
+
@core = WatchDoge.configuration.engine.new @browser, default_options
|
17
44
|
|
18
45
|
# for generate uri hash
|
19
46
|
@landed_page = nil
|
20
47
|
|
21
|
-
#
|
22
|
-
@
|
48
|
+
# autosaving cookies while worker closed
|
49
|
+
@cookie_autosave = false
|
23
50
|
|
24
51
|
# flag for cookies loaded
|
25
52
|
@cookie_loaded = false
|
26
53
|
|
27
|
-
#
|
28
|
-
@width =
|
29
|
-
@height =
|
54
|
+
# width/height
|
55
|
+
@width = default_options[:width]
|
56
|
+
@height = default_options[:height]
|
30
57
|
@core.window.resize_to @width, @height
|
31
|
-
|
32
|
-
_after_initialize
|
33
58
|
end
|
34
59
|
|
35
60
|
def fullpage_screenshot **kwargs
|
36
|
-
|
61
|
+
resize_by_viewport(kwargs) do |resize|
|
37
62
|
return resize.screenshot
|
38
63
|
end
|
39
64
|
end
|
40
65
|
|
41
66
|
def close
|
42
|
-
|
67
|
+
save_cookie
|
43
68
|
@core.close
|
44
69
|
end
|
45
70
|
|
@@ -48,17 +73,12 @@ module WatchDoge
|
|
48
73
|
|
49
74
|
@landed_page = uri
|
50
75
|
|
51
|
-
|
76
|
+
load_cookie unless cookie_loaded?
|
52
77
|
|
53
78
|
@core.refresh
|
54
79
|
end
|
55
80
|
|
56
|
-
|
57
|
-
def core
|
58
|
-
@core
|
59
|
-
end
|
60
|
-
|
61
|
-
def resize_by_document **kwargs
|
81
|
+
def resize_by_viewport **kwargs
|
62
82
|
width = (kwargs.delete :width) || @core.body.width
|
63
83
|
height = (kwargs.delete :height) || @core.body.height
|
64
84
|
|
@@ -84,13 +104,8 @@ module WatchDoge
|
|
84
104
|
end
|
85
105
|
|
86
106
|
private
|
87
|
-
# callbacks before/after init
|
88
|
-
def _before_initialize
|
89
|
-
@http = Selenium::WebDriver::Remote::Http::Default.new
|
90
|
-
@browser = WatchDoge.configuration.browser
|
91
|
-
end
|
92
107
|
|
93
|
-
def
|
108
|
+
def browser_profile
|
94
109
|
case @browser
|
95
110
|
when :chrome
|
96
111
|
prefs = {}
|
@@ -102,35 +117,36 @@ module WatchDoge
|
|
102
117
|
}
|
103
118
|
when :firefox
|
104
119
|
profile = Selenium::WebDriver::Firefox::Profile.new
|
120
|
+
|
121
|
+
# basically, firefox default sanbox level to 4, which denied access to most folders
|
122
|
+
# that broken many env config such as FONTCONFIG_PATH
|
105
123
|
profile['security.sandbox.content.level'] = 2
|
106
|
-
profile['network.dns.disableIPv6'] = true
|
107
124
|
{
|
108
125
|
profile: profile
|
109
126
|
}
|
110
127
|
end
|
111
128
|
end
|
112
129
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
# for cookie load/save
|
117
|
-
def _cookie_loaded?
|
130
|
+
# Cookies
|
131
|
+
def cookie_loaded?
|
118
132
|
@cookie_loaded
|
119
133
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
134
|
+
|
135
|
+
def save_cookie
|
136
|
+
if @cookie_autosave
|
137
|
+
@core.cookies.save WatchDoge::CookiePool.source(@uid, @landed_page)
|
123
138
|
end
|
124
139
|
end
|
125
|
-
|
126
|
-
|
140
|
+
|
141
|
+
def load_cookie
|
142
|
+
source = WatchDoge::CookiePool.source(@uid, @landed_page)
|
127
143
|
return unless File.exist? source
|
128
144
|
|
129
145
|
@core.cookies.load source
|
130
146
|
@cookie_loaded = true
|
131
147
|
end
|
132
148
|
|
133
|
-
# try delegate all methods to
|
149
|
+
# try delegate all missing methods to selenium
|
134
150
|
def method_missing(method_name, *args)
|
135
151
|
result = @core.__send__(method_name, *args)
|
136
152
|
|
data/lib/watchdoge.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'pry'
|
2
|
-
require 'watchdoge/
|
2
|
+
require 'watchdoge/webdriver_manager'
|
3
3
|
require 'watchdoge/version'
|
4
4
|
require 'watchdoge/configuration'
|
5
5
|
require 'watchdoge/cookie_pool'
|
@@ -26,18 +26,18 @@ module WatchDoge
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def configuration
|
29
|
-
@
|
29
|
+
@configuration ||= Configuration.new
|
30
30
|
end
|
31
31
|
|
32
32
|
def initialize!
|
33
|
-
|
33
|
+
config_file = ENV['WATCHDOGE_CONFIG'] ||
|
34
34
|
if defined?(Rails)
|
35
|
-
"#{
|
35
|
+
"#{Rails.root}/config/watchdoge.rb"
|
36
36
|
else
|
37
37
|
"#{Dir.pwd}/watchdoge.rb"
|
38
38
|
end
|
39
39
|
|
40
|
-
load
|
40
|
+
load config_file
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
data/watchdoge.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: watchdoge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shen Lee
|
@@ -115,16 +115,16 @@ files:
|
|
115
115
|
- lib/watchdoge/configuration.rb
|
116
116
|
- lib/watchdoge/cookie_pool.rb
|
117
117
|
- lib/watchdoge/image_diff.rb
|
118
|
-
- lib/watchdoge/install.rb
|
119
118
|
- lib/watchdoge/notification.rb
|
120
119
|
- lib/watchdoge/notification/matter_most.rb
|
121
|
-
- lib/watchdoge/notification/
|
120
|
+
- lib/watchdoge/notification/slack_webhook.rb
|
122
121
|
- lib/watchdoge/rails/generator.rb
|
123
122
|
- lib/watchdoge/rails/railtie.rb
|
124
123
|
- lib/watchdoge/regression.rb
|
125
124
|
- lib/watchdoge/regression/dsl.rb
|
126
125
|
- lib/watchdoge/regression/utils.rb
|
127
126
|
- lib/watchdoge/version.rb
|
127
|
+
- lib/watchdoge/webdriver_manager.rb
|
128
128
|
- lib/watchdoge/worker.rb
|
129
129
|
- watchdoge.gemspec
|
130
130
|
homepage:
|