likadan 0.0.2 → 0.0.3
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/bin/likadan +25 -7
- data/lib/likadan_action.rb +29 -0
- data/lib/likadan_runner.rb +39 -35
- data/lib/likadan_server.rb +37 -15
- data/lib/likadan_utils.rb +29 -0
- data/lib/public/likadan-runner.js +55 -10
- data/lib/views/index.erb +12 -1
- data/lib/views/review.erb +44 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bdf5b94276da8220ca5344b009e26e11e900afc
|
4
|
+
data.tar.gz: 55145af24ef76484e796629f18a980f5c332ff38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8109fd809d7d81f6c99779a972446e27402a7632b8cedc4a0288eecff586e7bbb3ee7e187694b7478de00a91c94c1672fdfdc6b956516370ef003144eb0bde2b
|
7
|
+
data.tar.gz: 038492a9885d98a506d0279a35a20a22e6db256200e598fc7a3c9813ff707c11c637d5010c6bda27f738b34d6de313ffe0004e3a6c6ff8295adaf5fd063d3dff
|
data/bin/likadan
CHANGED
@@ -1,12 +1,30 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
require 'likadan_utils'
|
4
|
+
|
5
|
+
action = ARGV[0] || 'run'
|
6
|
+
case action
|
7
|
+
when 'run'
|
8
|
+
Thread.new do # trivial example work thread
|
9
|
+
begin
|
10
|
+
require 'likadan_runner'
|
11
|
+
ensure
|
12
|
+
puts 'Press Enter to exit...'
|
13
|
+
gets
|
14
|
+
exit
|
15
|
+
end
|
8
16
|
end
|
9
|
-
|
17
|
+
require 'likadan_server'
|
10
18
|
|
11
|
-
|
19
|
+
when 'review'
|
20
|
+
system 'open', LikadanUtils.construct_url('/review')
|
21
|
+
require 'likadan_server'
|
12
22
|
|
23
|
+
when 'approve', 'reject'
|
24
|
+
require 'likadan_action'
|
25
|
+
abort 'Missing example name' unless example_name = ARGV[1]
|
26
|
+
abort 'Missing viewport width' unless width = ARGV[2]
|
27
|
+
LikadanAction.new(example_name, width).send(action)
|
28
|
+
else
|
29
|
+
abort "Unknown action \"#{action}\""
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'likadan_utils'
|
2
|
+
require 'FileUtils'
|
3
|
+
|
4
|
+
class LikadanAction
|
5
|
+
def initialize(example_name, width)
|
6
|
+
@example_name = example_name
|
7
|
+
@width = width
|
8
|
+
end
|
9
|
+
|
10
|
+
def approve
|
11
|
+
diff_path = LikadanUtils.path_to(@example_name, @width, 'diff.png')
|
12
|
+
baseline_path = LikadanUtils.path_to(@example_name, @width, 'baseline.png')
|
13
|
+
candidate_path = LikadanUtils.path_to(@example_name, @width, 'candidate.png')
|
14
|
+
|
15
|
+
FileUtils.rm(diff_path, force: true)
|
16
|
+
|
17
|
+
if File.exist? candidate_path
|
18
|
+
FileUtils.mv(candidate_path, baseline_path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def reject
|
23
|
+
diff_path = LikadanUtils.path_to(@example_name, @width, 'diff.png')
|
24
|
+
candidate_path = LikadanUtils.path_to(@example_name, @width, 'candidate.png')
|
25
|
+
|
26
|
+
FileUtils.rm(diff_path, force: true)
|
27
|
+
FileUtils.rm(candidate_path, force: true)
|
28
|
+
end
|
29
|
+
end
|
data/lib/likadan_runner.rb
CHANGED
@@ -6,53 +6,57 @@ require 'diffux_core/snapshot_comparison_image/before'
|
|
6
6
|
require 'diffux_core/snapshot_comparison_image/overlayed'
|
7
7
|
require 'diffux_core/snapshot_comparison_image/after'
|
8
8
|
require 'chunky_png'
|
9
|
-
require '
|
9
|
+
require 'likadan_utils'
|
10
10
|
|
11
11
|
driver = Selenium::WebDriver.for :firefox
|
12
12
|
begin
|
13
|
-
|
14
|
-
snapshots_folder = config['snapshots_folder'] || './snapshots'
|
15
|
-
|
16
|
-
driver.navigate.to 'http://localhost:4567/'
|
13
|
+
driver.navigate.to LikadanUtils.construct_url('/')
|
17
14
|
|
18
15
|
while current = driver.execute_script('return window.likadan.next()') do
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
dirname = File.dirname(file)
|
23
|
-
unless File.directory?(dirname)
|
24
|
-
FileUtils.mkdir_p(dirname)
|
25
|
-
end
|
26
|
-
|
27
|
-
previous_png = Dir.glob(File.join(dirname, 'snapshot_*.png')).max do |a,b|
|
28
|
-
File.ctime(a) <=> File.ctime(b)
|
29
|
-
end
|
16
|
+
current['viewportWidths'].each do |width|
|
17
|
+
# Resize window to the right size before rendering
|
18
|
+
driver.manage.window.resize_to(width, width)
|
30
19
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
to_crop.save(file)
|
20
|
+
# Render the example
|
21
|
+
rendered = driver.execute_script('return window.likadan.renderCurrent()')
|
22
|
+
output_file = LikadanUtils.path_to(current['name'], width, 'candidate.png')
|
35
23
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
ChunkyPNG::Image.from_file(previous_png),
|
41
|
-
ChunkyPNG::Image.from_file(file),
|
42
|
-
).compare!
|
24
|
+
# Create the folder structure if it doesn't already exist
|
25
|
+
unless File.directory?(dirname = File.dirname(output_file))
|
26
|
+
FileUtils.mkdir_p(dirname)
|
27
|
+
end
|
43
28
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
29
|
+
# Save and crop the screenshot
|
30
|
+
driver.save_screenshot(output_file)
|
31
|
+
cropped = ChunkyPNG::Image.from_file(output_file)
|
32
|
+
cropped.crop!(0, 0, rendered['width'], rendered['height'])
|
33
|
+
cropped.save(output_file)
|
34
|
+
|
35
|
+
print "Checking \"#{current['name']}\" at #{width}px... "
|
36
|
+
|
37
|
+
# Run the diff if needed
|
38
|
+
baseline_file = LikadanUtils.path_to(current['name'], width, 'baseline.png')
|
39
|
+
|
40
|
+
if File.exist? baseline_file
|
41
|
+
comparison = Diffux::SnapshotComparer.new(
|
42
|
+
ChunkyPNG::Image.from_file(baseline_file),
|
43
|
+
cropped
|
44
|
+
).compare!
|
45
|
+
|
46
|
+
if img = comparison[:diff_image]
|
47
|
+
diff_output = LikadanUtils.path_to(current['name'], width, 'diff.png')
|
48
|
+
img.save(diff_output)
|
49
|
+
puts "#{comparison[:diff_in_percent]}% (#{diff_output})"
|
50
|
+
else
|
51
|
+
File.delete(output_file)
|
52
|
+
puts 'No diff.'
|
53
|
+
end
|
48
54
|
else
|
49
|
-
|
55
|
+
File.rename(output_file, baseline_file)
|
56
|
+
puts "First snapshot created (#{baseline_file})"
|
50
57
|
end
|
51
|
-
else
|
52
|
-
puts "First snapshot created (#{file})"
|
53
58
|
end
|
54
59
|
end
|
55
60
|
ensure
|
56
61
|
driver.quit
|
57
62
|
end
|
58
|
-
|
data/lib/likadan_server.rb
CHANGED
@@ -1,30 +1,42 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'yaml'
|
3
|
-
|
4
|
-
|
5
|
-
config = {
|
6
|
-
'source_files' => [],
|
7
|
-
'stylesheets' => [],
|
8
|
-
'port' => 4567
|
9
|
-
}
|
10
|
-
config_file = '.likadan.yaml'
|
11
|
-
if File.exist? config_file
|
12
|
-
config = config.merge(YAML.load_file(config_file))
|
13
|
-
end
|
14
|
-
config
|
15
|
-
end
|
3
|
+
require 'likadan_utils'
|
4
|
+
require 'likadan_action'
|
16
5
|
|
17
6
|
class LikadanServer < Sinatra::Base
|
18
7
|
configure do
|
19
8
|
enable :static
|
20
|
-
set :port,
|
9
|
+
set :port, LikadanUtils.config['port']
|
10
|
+
end
|
11
|
+
|
12
|
+
def current_snapshots
|
13
|
+
prepare_file = lambda do |file|
|
14
|
+
width_dir = File.expand_path('..', file)
|
15
|
+
name_dir = File.expand_path('..', width_dir)
|
16
|
+
{
|
17
|
+
name: File.basename(name_dir),
|
18
|
+
width: File.basename(width_dir).sub('@', '').to_i,
|
19
|
+
file: file,
|
20
|
+
}
|
21
|
+
end
|
22
|
+
diff_files = Dir.glob("#{LikadanUtils.config['snapshots_folder']}/**/diff.png")
|
23
|
+
baselines = Dir.glob("#{LikadanUtils.config['snapshots_folder']}/**/baseline.png")
|
24
|
+
{
|
25
|
+
diffs: diff_files.map(&prepare_file),
|
26
|
+
baselines: baselines.map(&prepare_file)
|
27
|
+
}
|
21
28
|
end
|
22
29
|
|
23
30
|
get '/' do
|
24
|
-
@config =
|
31
|
+
@config = LikadanUtils.config
|
25
32
|
erb :index
|
26
33
|
end
|
27
34
|
|
35
|
+
get '/review' do
|
36
|
+
@snapshots = current_snapshots
|
37
|
+
erb :review
|
38
|
+
end
|
39
|
+
|
28
40
|
get '/resource' do
|
29
41
|
file = params[:file]
|
30
42
|
if file.start_with? 'http'
|
@@ -34,5 +46,15 @@ class LikadanServer < Sinatra::Base
|
|
34
46
|
end
|
35
47
|
end
|
36
48
|
|
49
|
+
post '/reject' do
|
50
|
+
LikadanAction.new(params[:name], params[:width]).reject
|
51
|
+
redirect back
|
52
|
+
end
|
53
|
+
|
54
|
+
post '/approve' do
|
55
|
+
LikadanAction.new(params[:name], params[:width]).approve
|
56
|
+
redirect back
|
57
|
+
end
|
58
|
+
|
37
59
|
run!
|
38
60
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class LikadanUtils
|
4
|
+
def self.config
|
5
|
+
@@config ||= {
|
6
|
+
'snapshots_folder' => './snapshots',
|
7
|
+
'source_files' => [],
|
8
|
+
'stylesheets' => [],
|
9
|
+
'port' => 4567
|
10
|
+
}.merge(YAML.load_file('.likadan.yaml'))
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.normalize_name(name)
|
14
|
+
name.gsub(/[^a-zA-Z0-9\-_]/, '_')
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.path_to(name, width, file_name)
|
18
|
+
File.join(
|
19
|
+
config['snapshots_folder'],
|
20
|
+
normalize_name(name),
|
21
|
+
"@#{width}",
|
22
|
+
file_name
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.construct_url(absolute_path)
|
27
|
+
return "http://localhost:#{config['port']}#{absolute_path}"
|
28
|
+
end
|
29
|
+
end
|
@@ -1,28 +1,73 @@
|
|
1
1
|
window.likadan = {
|
2
2
|
defined: [],
|
3
|
+
currentIndex: 0,
|
4
|
+
currentExample: undefined,
|
3
5
|
|
4
|
-
define: function(name, func) {
|
6
|
+
define: function(name, func, viewportWidths, options) {
|
5
7
|
this.defined.push({
|
6
8
|
name: name,
|
7
|
-
func: func
|
9
|
+
func: func,
|
10
|
+
viewportWidths: viewportWidths || [1024],
|
11
|
+
options: options || {
|
12
|
+
snapshotEntireScreen: false
|
13
|
+
}
|
8
14
|
});
|
9
15
|
},
|
10
16
|
|
11
17
|
next: function() {
|
12
|
-
|
13
|
-
if (!
|
18
|
+
this.currentExample = this.defined[this.currentIndex];
|
19
|
+
if (!this.currentExample) {
|
14
20
|
return;
|
15
21
|
}
|
16
|
-
|
22
|
+
this.currentIndex++;
|
23
|
+
return this.currentExample;
|
24
|
+
},
|
25
|
+
|
26
|
+
getOutputElement: function() {
|
27
|
+
return document.getElementById('likadan-output');
|
28
|
+
},
|
29
|
+
|
30
|
+
clearVisibleElements: function() {
|
31
|
+
var out = this.getOutputElement();
|
32
|
+
var allElements = Array.prototype.slice.call(document.querySelectorAll('body > *'));
|
33
|
+
allElements.forEach(function(element) {
|
34
|
+
if (element === out) {
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
var style = window.getComputedStyle(element);
|
38
|
+
if (style.display !== 'none') {
|
39
|
+
element.parentNode.removeChild(element);
|
40
|
+
}
|
41
|
+
});
|
42
|
+
},
|
43
|
+
|
44
|
+
renderCurrent: function() {
|
45
|
+
var out = this.getOutputElement();
|
17
46
|
out.innerHTML = '';
|
18
|
-
next.func(out);
|
19
47
|
|
20
|
-
|
48
|
+
// We need to clear out anything currently visible. There's no guarantee
|
49
|
+
// that examples only render inside the .likadan-output container.
|
50
|
+
this.clearVisibleElements();
|
51
|
+
|
52
|
+
var elem = this.currentExample.func();
|
53
|
+
if (elem.getDOMNode) {
|
54
|
+
// Soft-dependency to React here. If the thing returned has a
|
55
|
+
// `getDOMNode` method, call it to get the real DOM node.
|
56
|
+
elem = elem.getDOMNode();
|
57
|
+
}
|
58
|
+
out.appendChild(elem);
|
21
59
|
|
60
|
+
var width = elem.offsetWidth;
|
61
|
+
var height = elem.offsetHeight;
|
62
|
+
|
63
|
+
if (this.currentExample.options.snapshotEntireScreen) {
|
64
|
+
width = window.innerWidth;
|
65
|
+
height = window.innerHeight;
|
66
|
+
}
|
22
67
|
return {
|
23
|
-
name:
|
24
|
-
width:
|
25
|
-
height:
|
68
|
+
name: this.currentExample.name,
|
69
|
+
width: width,
|
70
|
+
height: height
|
26
71
|
};
|
27
72
|
}
|
28
73
|
};
|
data/lib/views/index.erb
CHANGED
@@ -1,12 +1,23 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
+
<style type="text/css">
|
5
|
+
* {
|
6
|
+
-webkit-transition: none !important;
|
7
|
+
-moz-transition: none !important;
|
8
|
+
transition: none !important;
|
9
|
+
-webkit-animation-duration: 0 !important;
|
10
|
+
-moz-animation-duration: 0s !important;
|
11
|
+
animation-duration: 0s !important;
|
12
|
+
}
|
13
|
+
</style>
|
14
|
+
|
4
15
|
<% @config['stylesheets'].each do |stylesheet| %>
|
5
16
|
<link rel="stylesheet" type="text/css"
|
6
17
|
href="/resource?file=<%= ERB::Util.url_encode(stylesheet) %>">
|
7
18
|
<% end %>
|
8
19
|
</head>
|
9
|
-
<body style="margin: 0;">
|
20
|
+
<body style="margin: 0; background-color: #fff;">
|
10
21
|
<div id="likadan-output"/>
|
11
22
|
<script src="/likadan-runner.js"></script>
|
12
23
|
<% @config['source_files'].each do |source_file| %>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<style type="text/css">
|
5
|
+
body {
|
6
|
+
background-color: #f0f0f0;
|
7
|
+
font-family: helvetica, arial;
|
8
|
+
}
|
9
|
+
form {
|
10
|
+
display: inline-block;
|
11
|
+
}
|
12
|
+
</style>
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<h1>Likadan Review Tool</h1>
|
16
|
+
<h2>DIFFS</h2>
|
17
|
+
<% @snapshots[:diffs].each do |diff| %>
|
18
|
+
<h3>
|
19
|
+
<%= diff[:name] %> @ <%= diff[:width] %>
|
20
|
+
</h3>
|
21
|
+
<p><img src="/resource?file=<%= ERB::Util.url_encode(diff[:file]) %>"></p>
|
22
|
+
<form style="display: inline-block"
|
23
|
+
action="/approve?name=<%= diff[:name] %>&width=<%= diff[:width] %>"
|
24
|
+
method="POST">
|
25
|
+
<button type="submit">Approve</button>
|
26
|
+
</form>
|
27
|
+
<form style="display: inline-block"
|
28
|
+
action="/reject?name=<%= diff[:name] %>&width=<%= diff[:width] %>"
|
29
|
+
method="POST">
|
30
|
+
<button type="submit">Reject</button>
|
31
|
+
</form>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<hr>
|
35
|
+
|
36
|
+
<h2>BASELINES</h2>
|
37
|
+
<% @snapshots[:baselines].each do |baseline| %>
|
38
|
+
<h3>
|
39
|
+
<%= baseline[:name] %> @ <%= baseline[:width] %>
|
40
|
+
</h3>
|
41
|
+
<p><img src="/resource?file=<%= ERB::Util.url_encode(baseline[:file]) %>"></p>
|
42
|
+
<% end %>
|
43
|
+
</body>
|
44
|
+
</html>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: likadan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henric Trotzig
|
@@ -118,10 +118,13 @@ extensions: []
|
|
118
118
|
extra_rdoc_files: []
|
119
119
|
files:
|
120
120
|
- bin/likadan
|
121
|
+
- lib/likadan_action.rb
|
121
122
|
- lib/likadan_runner.rb
|
122
123
|
- lib/likadan_server.rb
|
124
|
+
- lib/likadan_utils.rb
|
123
125
|
- lib/public/likadan-runner.js
|
124
126
|
- lib/views/index.erb
|
127
|
+
- lib/views/review.erb
|
125
128
|
homepage: http://rubygems.org/gems/likadan
|
126
129
|
licenses:
|
127
130
|
- MIT
|
@@ -142,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
145
|
version: '0'
|
143
146
|
requirements: []
|
144
147
|
rubyforge_project:
|
145
|
-
rubygems_version: 2.
|
148
|
+
rubygems_version: 2.4.5
|
146
149
|
signing_key:
|
147
150
|
specification_version: 4
|
148
151
|
summary: Likadan
|