dom_glancy 1.0.1 → 1.1.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 +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
|