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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/Gemfile +4 -0
  4. data/README.md +5 -0
  5. data/app/controllers/dom_glancy_controller.rb +9 -12
  6. data/app/views/dom_glancy/about.html.erb +2 -2
  7. data/app/views/dom_glancy/path_config.html.erb +10 -8
  8. data/app/views/dom_glancy/show.html.erb +3 -18
  9. data/app/views/layouts/dom_glancy.html.erb +10 -19
  10. data/app/views/shared/_dom_glancy_nav.html.erb +1 -1
  11. data/app/views/shared/_dom_set.html.erb +17 -2
  12. data/lib/assets/javascripts/dom_glancy/dom_glancy.js +37 -0
  13. data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/1236_grid.css +0 -0
  14. data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/720_grid.css +0 -0
  15. data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/986_grid.css +0 -0
  16. data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/dom_glancy.css +35 -4
  17. data/{app/assets/stylesheets → lib/assets/stylesheets/dom_glancy}/normalize.css +0 -0
  18. data/lib/dom_glancy.rb +6 -2
  19. data/lib/dom_glancy/analysis_reporter.rb +39 -0
  20. data/lib/dom_glancy/analyzer.rb +139 -0
  21. data/lib/dom_glancy/change_anlyzer.rb +22 -0
  22. data/lib/dom_glancy/dom_glancy.rb +35 -83
  23. data/lib/dom_glancy/element.rb +63 -12
  24. data/lib/dom_glancy/file_name_builder.rb +21 -0
  25. data/lib/dom_glancy/map_file.rb +14 -0
  26. data/lib/dom_glancy/page_mapper.rb +81 -0
  27. data/lib/dom_glancy/svg.rb +41 -2
  28. data/lib/dom_glancy/version.rb +1 -1
  29. data/test/page_objects/dom_glancy/show_page.rb +6 -0
  30. data/test/selenium/mapping_test.rb +28 -5
  31. data/test/selenium/viewer_test.rb +2 -2
  32. data/test/selenium_test_helper.rb +26 -43
  33. data/test/test_app/app/assets/stylesheets/local_app.css +9 -1
  34. data/test/test_app/app/views/local/index.html.erb +11 -1
  35. data/test/test_helper.rb +7 -5
  36. data/test/test_helpers/artifacts_helpers.rb +54 -0
  37. data/test/test_helpers/location_helpers.rb +5 -3
  38. data/test/test_objects/options_file_1.yaml +2145 -0
  39. data/test/test_objects/options_file_2.yaml +2177 -0
  40. data/test/test_objects/test_objects.rb +57 -6
  41. data/test/unit/analysis_reporter_test.rb +28 -0
  42. data/test/unit/analyzer_test.rb +146 -0
  43. data/test/unit/change_analyzer_test.rb +72 -0
  44. data/test/unit/dom_glancy_test.rb +16 -11
  45. data/test/unit/element_test.rb +56 -0
  46. data/test/unit/file_name_builder_test.rb +28 -0
  47. data/test/unit/map_file_test.rb +47 -0
  48. data/test/unit/page_mapper_test.rb +27 -0
  49. data/test/unit/svg_test.rb +17 -0
  50. metadata +35 -13
  51. data/app/assets/javascripts/application.js +0 -7
  52. data/lib/dom_glancy/analysis.rb +0 -146
  53. data/lib/dom_glancy/locations.rb +0 -16
  54. 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 = map_current_file(test_root)
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 = read_map_file(DomGlancy.current_filename(test_root))
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 = read_map_file(DomGlancy.master_filename(test_root))
16
+ result, msg, master_data = ::DomGlancy::MapFile.new.read(fnb.master)
16
17
  return [result, msg] unless result
17
18
 
18
- analysis_data = analyze(master_data, current_data, test_root)
19
+ analyzer = ::DomGlancy::Analyzer.new(master_data, current_data, test_root)
20
+ analysis_data = analyzer.analyze
19
21
 
20
- msg = make_analysis_failure_report(analysis_data)
21
- result = analysis_data[:same]
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
- results
38
- end
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
- result = [true, '']
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
- def resize_browser_for_scrollbar
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
- Capybara.current_session.driver.browser.manage.window.resize_to(original_dimensions.width + width, original_dimensions.height) if width > 0
37
+ def console_failure_string(analysis_data)
38
+ return '' if analysis_data[:same]
59
39
 
60
- result = yield
40
+ fnb = ::DomGlancy::FileNameBuilder.new(analysis_data[:test_root])
61
41
 
62
- Capybara.current_session.driver.browser.manage.window.resize_to(original_dimensions.width, original_dimensions.height) if width > 0
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
- result
54
+ msg.join("\n")
65
55
  end
66
56
 
67
- def perform_mapping_operation
68
- page_map_js = <<-JS
69
- var dom_glancy = {
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.master_filename(test_root)
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
- File.delete DomGlancy.current_filename(test_root) if File.exist?(DomGlancy.current_filename(test_root))
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) }
@@ -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[:tag] || h['tag']
20
- @left = h[:left] || h['left']
21
- @top = h[:top] || h['top']
22
- @height = h[:height] || h['height']
23
- @width = h[:width] || h['width']
24
- @klass = h[:class] || h['class']
25
- @id = h[:id] || h['id']
26
- @style = h[:style] || h['style']
27
- @visible = h[:visible] || h['visible']
28
- @similarity = h[:similarity] || h['similarity'] || 0
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
@@ -1,6 +1,20 @@
1
1
  module DomGlancy
2
- class DomGlancy
3
- def generate_svg(rectangles)
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