dom_glancy 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -2
- data/Gemfile +4 -0
- data/README.md +5 -0
- data/app/controllers/dom_glancy_controller.rb +9 -12
- data/app/views/dom_glancy/about.html.erb +2 -2
- data/app/views/dom_glancy/path_config.html.erb +10 -8
- data/app/views/dom_glancy/show.html.erb +3 -18
- data/app/views/layouts/dom_glancy.html.erb +10 -19
- data/app/views/shared/_dom_glancy_nav.html.erb +1 -1
- data/app/views/shared/_dom_set.html.erb +17 -2
- data/lib/assets/javascripts/dom_glancy/dom_glancy.js +37 -0
- data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/1236_grid.css +0 -0
- data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/720_grid.css +0 -0
- data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/986_grid.css +0 -0
- data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/dom_glancy.css +35 -4
- data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/normalize.css +0 -0
- data/lib/dom_glancy.rb +6 -2
- data/lib/dom_glancy/analysis_reporter.rb +39 -0
- data/lib/dom_glancy/analyzer.rb +139 -0
- data/lib/dom_glancy/change_anlyzer.rb +22 -0
- data/lib/dom_glancy/dom_glancy.rb +35 -83
- data/lib/dom_glancy/element.rb +63 -12
- data/lib/dom_glancy/file_name_builder.rb +21 -0
- data/lib/dom_glancy/map_file.rb +14 -0
- data/lib/dom_glancy/page_mapper.rb +81 -0
- data/lib/dom_glancy/svg.rb +41 -2
- data/lib/dom_glancy/version.rb +1 -1
- data/test/page_objects/dom_glancy/show_page.rb +6 -0
- data/test/selenium/mapping_test.rb +28 -5
- data/test/selenium/viewer_test.rb +2 -2
- data/test/selenium_test_helper.rb +26 -43
- data/test/test_app/app/assets/stylesheets/local_app.css +9 -1
- data/test/test_app/app/views/local/index.html.erb +11 -1
- data/test/test_helper.rb +7 -5
- data/test/test_helpers/artifacts_helpers.rb +54 -0
- data/test/test_helpers/location_helpers.rb +5 -3
- data/test/test_objects/options_file_1.yaml +2145 -0
- data/test/test_objects/options_file_2.yaml +2177 -0
- data/test/test_objects/test_objects.rb +57 -6
- data/test/unit/analysis_reporter_test.rb +28 -0
- data/test/unit/analyzer_test.rb +146 -0
- data/test/unit/change_analyzer_test.rb +72 -0
- data/test/unit/dom_glancy_test.rb +16 -11
- data/test/unit/element_test.rb +56 -0
- data/test/unit/file_name_builder_test.rb +28 -0
- data/test/unit/map_file_test.rb +47 -0
- data/test/unit/page_mapper_test.rb +27 -0
- data/test/unit/svg_test.rb +17 -0
- metadata +35 -13
- data/app/assets/javascripts/application.js +0 -7
- data/lib/dom_glancy/analysis.rb +0 -146
- data/lib/dom_glancy/locations.rb +0 -16
- data/test/unit/analysis_test.rb +0 -110
@@ -0,0 +1,22 @@
|
|
1
|
+
module DomGlancy
|
2
|
+
class ChangeAnalyzer
|
3
|
+
attr_accessor :change_factors
|
4
|
+
|
5
|
+
def initialize(change_factors = [])
|
6
|
+
@change_factors = Array.new(change_factors)[0..4]
|
7
|
+
|
8
|
+
@change_factors << 0 if @change_factors.length == 0
|
9
|
+
@change_factors << 1 if @change_factors.length == 1
|
10
|
+
@change_factors << 1.2 if @change_factors.length == 2
|
11
|
+
@change_factors << 2 if @change_factors.length == 3
|
12
|
+
@change_factors << 5 if @change_factors.length == 4
|
13
|
+
end
|
14
|
+
|
15
|
+
def compare(element1, element2)
|
16
|
+
change_info = element1.change_info(element2)
|
17
|
+
deltas = change_info.map { |changed| (element1.send(changed.to_sym) - element2.send(changed.to_sym)).abs }
|
18
|
+
comparison_value = deltas.inject { |sum, n| n + n * deltas.count * @change_factors[deltas.count] }
|
19
|
+
comparison_value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,114 +2,65 @@ module DomGlancy
|
|
2
2
|
class DomGlancy
|
3
3
|
def page_map_same?(test_root)
|
4
4
|
purge_old_files_before_test(test_root)
|
5
|
+
fnb = ::DomGlancy::FileNameBuilder.new(test_root)
|
5
6
|
|
6
|
-
result, msg =
|
7
|
+
result, msg = ::DomGlancy::PageMapper.new.run(test_root)
|
7
8
|
return [result, msg] unless result
|
8
9
|
|
9
10
|
result, msg = master_file_exists?(test_root)
|
10
11
|
return [result, msg] unless result
|
11
12
|
|
12
|
-
result, msg, current_data =
|
13
|
+
result, msg, current_data = ::DomGlancy::MapFile.new.read(fnb.current)
|
13
14
|
return [result, msg] unless result
|
14
15
|
|
15
|
-
result, msg, master_data =
|
16
|
+
result, msg, master_data = ::DomGlancy::MapFile.new.read(fnb.master)
|
16
17
|
return [result, msg] unless result
|
17
18
|
|
18
|
-
|
19
|
+
analyzer = ::DomGlancy::Analyzer.new(master_data, current_data, test_root)
|
20
|
+
analysis_data = analyzer.analyze
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
File.delete DomGlancy.current_filename(test_root) if result
|
24
|
-
|
25
|
-
[result, msg]
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
def read_map_file(filename)
|
30
|
-
results = [true, '', nil]
|
31
|
-
begin
|
32
|
-
results[2] = YAML::load( File.open( filename ) )
|
33
|
-
rescue Exception => e
|
34
|
-
results = [false, "Error reading data from file: #{filename}", nil]
|
22
|
+
unless analysis_data[:same]
|
23
|
+
analysis_reporter = ::DomGlancy::AnalysisReporter.new(test_root, analyzer.set_current_not_master, analyzer.set_master_not_current, analyzer.set_changed_master, analyzer.changed_element_pairs)
|
24
|
+
analysis_reporter.create_diff_file
|
35
25
|
end
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def map_current_file(test_root)
|
41
|
-
filename = DomGlancy.current_filename(test_root)
|
27
|
+
msg = console_failure_string(analysis_data)
|
28
|
+
result = analysis_data[:same]
|
42
29
|
|
43
|
-
|
44
|
-
begin
|
45
|
-
data = perform_mapping_operation.to_yaml
|
46
|
-
File.open(filename, 'w') { |file| file.write(data) }
|
47
|
-
rescue Exception => e
|
48
|
-
result = [false, "map current file error: #{e.message}"]
|
49
|
-
end
|
30
|
+
File.delete fnb.current if result
|
50
31
|
|
51
|
-
result
|
32
|
+
[result, msg]
|
52
33
|
end
|
53
34
|
|
54
|
-
|
55
|
-
original_dimensions = Capybara.current_session.driver.browser.manage.window.size
|
56
|
-
width = Capybara.current_session.evaluate_script('window.innerWidth - document.documentElement.clientWidth').to_i
|
35
|
+
private
|
57
36
|
|
58
|
-
|
37
|
+
def console_failure_string(analysis_data)
|
38
|
+
return '' if analysis_data[:same]
|
59
39
|
|
60
|
-
|
40
|
+
fnb = ::DomGlancy::FileNameBuilder.new(analysis_data[:test_root])
|
61
41
|
|
62
|
-
|
42
|
+
msg = ["\n------- DOM Comparison Failure ------"]
|
43
|
+
msg << "Elements not in master: #{analysis_data[:not_in_master].count}"
|
44
|
+
msg << "Elements not in current: #{analysis_data[:not_in_current].count}"
|
45
|
+
msg << "Changed elements: #{analysis_data[:changed_element_pairs].count}"
|
46
|
+
msg << "Files:"
|
47
|
+
msg << "\tcurrent: #{fnb.current}"
|
48
|
+
msg << "\tmaster: #{fnb.master}"
|
49
|
+
msg << "\tdifference: #{fnb.diff}"
|
50
|
+
msg << "Bless this current data set:"
|
51
|
+
msg << "\t#{blessing_copy_string(analysis_data[:test_root])}"
|
52
|
+
msg<< "-------------------------------------"
|
63
53
|
|
64
|
-
|
54
|
+
msg.join("\n")
|
65
55
|
end
|
66
56
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
treeUp: function() {
|
72
|
-
var treeWalker = document.createTreeWalker(
|
73
|
-
document.body,
|
74
|
-
NodeFilter.SHOW_ELEMENT,
|
75
|
-
{ acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } },
|
76
|
-
false
|
77
|
-
);
|
78
|
-
|
79
|
-
var nodeList = [];
|
80
|
-
|
81
|
-
while(treeWalker.nextNode()){
|
82
|
-
var cn = treeWalker.currentNode;
|
83
|
-
var node_details = {
|
84
|
-
"height" : cn.clientHeight,
|
85
|
-
"width" : cn.clientWidth,
|
86
|
-
"id" : cn.id,
|
87
|
-
"tag" : cn.tagName,
|
88
|
-
"class" : cn.className,
|
89
|
-
"top" : cn.offsetTop,
|
90
|
-
"left" : cn.offsetLeft,
|
91
|
-
"visible" : dom_glancy.isVisible(cn)
|
92
|
-
}
|
93
|
-
nodeList.push(node_details);
|
94
|
-
}
|
95
|
-
|
96
|
-
return(nodeList);
|
97
|
-
},
|
98
|
-
|
99
|
-
isVisible: function(elem) {
|
100
|
-
return elem.offsetWidth > 0 || elem.offsetHeight > 0;
|
101
|
-
}
|
102
|
-
};
|
103
|
-
return dom_glancy.treeUp();
|
104
|
-
JS
|
105
|
-
|
106
|
-
resize_browser_for_scrollbar do
|
107
|
-
Capybara.current_session.driver.browser.execute_script(page_map_js)
|
108
|
-
end
|
57
|
+
def blessing_copy_string(test_root)
|
58
|
+
fnb = ::DomGlancy::FileNameBuilder.new(test_root)
|
59
|
+
"cp #{fnb.current} #{fnb.master}"
|
109
60
|
end
|
110
61
|
|
111
62
|
def master_file_exists?(test_root)
|
112
|
-
filename = DomGlancy.
|
63
|
+
filename = ::DomGlancy::FileNameBuilder.new(test_root).master
|
113
64
|
result = File.exist?(filename)
|
114
65
|
msg = result ? '' : make_missing_master_failure_report(test_root)
|
115
66
|
[result, msg]
|
@@ -126,7 +77,8 @@ module DomGlancy
|
|
126
77
|
end
|
127
78
|
|
128
79
|
def purge_old_files_before_test(test_root)
|
129
|
-
|
80
|
+
old_current_file = ::DomGlancy::FileNameBuilder.new(test_root).current
|
81
|
+
File.delete old_current_file if File.exist?(old_current_file)
|
130
82
|
|
131
83
|
filename_pattern = File.join(::DomGlancy.configuration.diff_file_location, "#{test_root}__*__diff.yaml")
|
132
84
|
Dir[filename_pattern].each { |file| file.delete(file) if File.exist?(file) }
|
data/lib/dom_glancy/element.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module DomGlancy
|
2
|
-
|
3
2
|
class DOMElement
|
4
3
|
## element looks like this in archive.
|
5
4
|
# {"id"=>"", "height"=>238, "visible"=>true, "tag"=>"DIV", "width"=>720, "class"=>"grid", "left"=>43, "top"=>14}
|
@@ -16,16 +15,16 @@ module DomGlancy
|
|
16
15
|
attr_accessor :similarity
|
17
16
|
|
18
17
|
def initialize(h = {})
|
19
|
-
@tag = h[
|
20
|
-
@left = h[
|
21
|
-
@top = h[
|
22
|
-
@height = h[
|
23
|
-
@width = h[
|
24
|
-
@klass = h[
|
25
|
-
@id = h[
|
26
|
-
@style = h[
|
27
|
-
@visible = h[
|
28
|
-
@similarity = h[
|
18
|
+
@tag = h['tag']
|
19
|
+
@left = h['left']
|
20
|
+
@top = h['top']
|
21
|
+
@height = h['height']
|
22
|
+
@width = h['width']
|
23
|
+
@klass = h['class']
|
24
|
+
@id = h['id']
|
25
|
+
@style = h['style']
|
26
|
+
@visible = h['visible']
|
27
|
+
@similarity = h['similarity'] || 0
|
29
28
|
end
|
30
29
|
|
31
30
|
def same_element?(anOther)
|
@@ -38,7 +37,7 @@ module DomGlancy
|
|
38
37
|
same = same_element?(anOther) &&
|
39
38
|
similar_size?(anOther, 0) &&
|
40
39
|
similar_location?(anOther, 0) &&
|
41
|
-
same_size?(anOther)
|
40
|
+
same_size?(anOther) &&
|
42
41
|
same_visibility?(anOther)
|
43
42
|
end
|
44
43
|
|
@@ -60,6 +59,22 @@ module DomGlancy
|
|
60
59
|
similar && (@left - anOther.left).abs <= similarity
|
61
60
|
end
|
62
61
|
|
62
|
+
def similar_top?(anOther, similarity = 0)
|
63
|
+
(@top - anOther.top).abs <= similarity
|
64
|
+
end
|
65
|
+
|
66
|
+
def similar_left?(anOther, similarity = 0)
|
67
|
+
(@left - anOther.left).abs <= similarity
|
68
|
+
end
|
69
|
+
|
70
|
+
def similar_width?(anOther, similarity = 0)
|
71
|
+
(@width - anOther.width).abs <= similarity
|
72
|
+
end
|
73
|
+
|
74
|
+
def similar_height?(anOther, similarity = 0)
|
75
|
+
(@height - anOther.height).abs <= similarity
|
76
|
+
end
|
77
|
+
|
63
78
|
def same_tag?(anOther)
|
64
79
|
@tag == anOther.tag
|
65
80
|
end
|
@@ -88,5 +103,41 @@ module DomGlancy
|
|
88
103
|
@style == anOther.style
|
89
104
|
end
|
90
105
|
|
106
|
+
def similarity_level(anOther)
|
107
|
+
level = 0
|
108
|
+
if same_element?(anOther) && same_style?(anOther)
|
109
|
+
level += 1 if similar_location?(anOther, @similarity) and !same_location?(anOther)
|
110
|
+
level += 1 if similar_size?(anOther, @similarity) and !same_size?(anOther)
|
111
|
+
end
|
112
|
+
level
|
113
|
+
end
|
114
|
+
|
115
|
+
def change_level(anOther)
|
116
|
+
change_info(anOther).length
|
117
|
+
end
|
118
|
+
|
119
|
+
def change_info(anOther)
|
120
|
+
info = []
|
121
|
+
if same_element?(anOther)
|
122
|
+
info << 'left' unless similar_left?(anOther, @similarity)
|
123
|
+
info << 'top' unless similar_top?(anOther, @similarity)
|
124
|
+
info << 'height' unless similar_height?(anOther, @similarity)
|
125
|
+
info << 'width' unless similar_width?(anOther, @similarity)
|
126
|
+
end
|
127
|
+
info
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_hash
|
131
|
+
{
|
132
|
+
'id' => @id,
|
133
|
+
'height' => @height,
|
134
|
+
'visible' => @visible,
|
135
|
+
'tag' => @tag,
|
136
|
+
'width' => @width,
|
137
|
+
'class' => @klass,
|
138
|
+
'left' => @left,
|
139
|
+
'top' => @top
|
140
|
+
}
|
141
|
+
end
|
91
142
|
end
|
92
143
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DomGlancy
|
2
|
+
class FileNameBuilder
|
3
|
+
attr_accessor :test_root
|
4
|
+
|
5
|
+
def initialize(test_root = '')
|
6
|
+
@test_root = test_root
|
7
|
+
end
|
8
|
+
|
9
|
+
def master
|
10
|
+
File.join(::DomGlancy.configuration.master_file_location, "#{@test_root}_master.yaml")
|
11
|
+
end
|
12
|
+
|
13
|
+
def current
|
14
|
+
File.join(::DomGlancy.configuration.current_file_location, "#{@test_root}.yaml")
|
15
|
+
end
|
16
|
+
|
17
|
+
def diff
|
18
|
+
File.join(::DomGlancy.configuration.diff_file_location, "#{@test_root}_diff.html")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module DomGlancy
|
2
|
+
class MapFile
|
3
|
+
def read(filename)
|
4
|
+
results = [true, '', nil]
|
5
|
+
begin
|
6
|
+
results[2] = YAML::load( File.open( filename ) )
|
7
|
+
rescue Exception => e
|
8
|
+
results = [false, "Error reading data from file: #{filename}", nil]
|
9
|
+
end
|
10
|
+
|
11
|
+
results
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module DomGlancy
|
2
|
+
class PageMapper
|
3
|
+
|
4
|
+
def run(test_root)
|
5
|
+
filename = ::DomGlancy::FileNameBuilder.new(test_root).current
|
6
|
+
|
7
|
+
result = [true, '']
|
8
|
+
begin
|
9
|
+
data = map_page.to_yaml
|
10
|
+
File.open(filename, 'w') { |file| file.write(data) }
|
11
|
+
rescue Exception => e
|
12
|
+
result = [false, "map current file error: #{e.message}"]
|
13
|
+
end
|
14
|
+
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def map_page
|
21
|
+
page_map_js = mapping_javascript
|
22
|
+
resize_browser_for_scrollbar do
|
23
|
+
Capybara.current_session.driver.browser.execute_script(page_map_js)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def mapping_javascript
|
28
|
+
<<-JS
|
29
|
+
var dom_glancy = {
|
30
|
+
|
31
|
+
treeUp: function() {
|
32
|
+
var treeWalker = document.createTreeWalker(
|
33
|
+
document.body,
|
34
|
+
NodeFilter.SHOW_ELEMENT,
|
35
|
+
{ acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } },
|
36
|
+
false
|
37
|
+
);
|
38
|
+
|
39
|
+
var nodeList = [];
|
40
|
+
|
41
|
+
while(treeWalker.nextNode()){
|
42
|
+
var cn = treeWalker.currentNode;
|
43
|
+
var node_details = {
|
44
|
+
"height" : cn.clientHeight,
|
45
|
+
"width" : cn.clientWidth,
|
46
|
+
"id" : cn.id,
|
47
|
+
"tag" : cn.tagName,
|
48
|
+
"class" : cn.className,
|
49
|
+
"top" : cn.offsetTop,
|
50
|
+
"left" : cn.offsetLeft,
|
51
|
+
"visible" : dom_glancy.isVisible(cn)
|
52
|
+
}
|
53
|
+
nodeList.push(node_details);
|
54
|
+
}
|
55
|
+
|
56
|
+
return(nodeList);
|
57
|
+
},
|
58
|
+
|
59
|
+
isVisible: function(elem) {
|
60
|
+
return elem.offsetWidth > 0 || elem.offsetHeight > 0;
|
61
|
+
}
|
62
|
+
};
|
63
|
+
return dom_glancy.treeUp();
|
64
|
+
JS
|
65
|
+
end
|
66
|
+
|
67
|
+
def resize_browser_for_scrollbar
|
68
|
+
original_dimensions = Capybara.current_session.driver.browser.manage.window.size
|
69
|
+
width = Capybara.current_session.evaluate_script('window.innerWidth - document.documentElement.clientWidth').to_i
|
70
|
+
|
71
|
+
Capybara.current_session.driver.browser.manage.window.resize_to(original_dimensions.width + width, original_dimensions.height) if width > 0
|
72
|
+
|
73
|
+
result = yield
|
74
|
+
|
75
|
+
Capybara.current_session.driver.browser.manage.window.resize_to(original_dimensions.width, original_dimensions.height) if width > 0
|
76
|
+
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/dom_glancy/svg.rb
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
module DomGlancy
|
2
|
-
class
|
3
|
-
|
2
|
+
class SVG
|
3
|
+
@set_current_not_master
|
4
|
+
@set_master_not_current
|
5
|
+
@set_changed_master
|
6
|
+
|
7
|
+
def initialize(set_current_not_master, set_master_not_current, set_changed_master)
|
8
|
+
@set_current_not_master = set_current_not_master
|
9
|
+
@set_master_not_current = set_master_not_current
|
10
|
+
@set_changed_master = set_changed_master
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_svg
|
14
|
+
add_ids
|
15
|
+
|
16
|
+
rectangles = make_rectangles
|
17
|
+
|
4
18
|
width, height = get_window_size_from_rectangles(rectangles)
|
5
19
|
s = svg_start(width, height)
|
6
20
|
|
@@ -13,6 +27,31 @@ module DomGlancy
|
|
13
27
|
s += "\n"
|
14
28
|
end
|
15
29
|
|
30
|
+
private
|
31
|
+
|
32
|
+
def add_ids
|
33
|
+
js_id = 0
|
34
|
+
@set_master_not_current.each do |item|
|
35
|
+
item[:js_id] = js_id
|
36
|
+
js_id += 1
|
37
|
+
end
|
38
|
+
@set_current_not_master.each do |item|
|
39
|
+
item[:js_id] = js_id
|
40
|
+
js_id += 1
|
41
|
+
end
|
42
|
+
@set_changed_master.each do |item|
|
43
|
+
item[:js_id] = js_id
|
44
|
+
js_id += 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_rectangles
|
49
|
+
rectangles = @set_current_not_master.map { |item| item.merge(format__not_in_master) }
|
50
|
+
rectangles << @set_master_not_current.map { |item| item.merge(format__not_in_current) }
|
51
|
+
rectangles << @set_changed_master.map { |item| item.merge(format__same_but_different) }
|
52
|
+
rectangles.flatten!
|
53
|
+
end
|
54
|
+
|
16
55
|
def get_window_size_from_rectangles(rectangles)
|
17
56
|
width = 0
|
18
57
|
height = 0
|