motion-screenspecs 0.0.1
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 +7 -0
- data/README.md +111 -0
- data/lib/motion-screenspecs.rb +207 -0
- data/lib/spec/helpers.rb +76 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0e1ee4821ad5489d2e5e5b0b32e5d189733f0c30
|
4
|
+
data.tar.gz: 707ccb1260a5cf1d8607983cfa22bb0639b6451c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d8a7f61d656df6c054faadf2bfd8f8b965bf40b558c6370f0fc169b960893f303600743b9a8f205d920b2d4f6620960ac04b038c3e9518bbebe86991dfa19ab3
|
7
|
+
data.tar.gz: 0e17a6418a9631c08ebd252b80d4a4efff5241b8d93d997cb4c2c3739a3ed8f394b3fc33e1eeee3bd5b676a6f33297f6db5a4f549c390b930da18f9af138fa9b
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# motion-screenspecs
|
2
|
+
|
3
|
+
Test your RubyMotion app regressions using screenshot comparison (similar to [Huxley](https://github.com/facebook/huxley) and [Wraith](https://github.com/BBC-News/wraith)):
|
4
|
+
|
5
|
+
```
|
6
|
+
$ rake spec
|
7
|
+
|
8
|
+
AppScreenshots
|
9
|
+
- should take screenshots
|
10
|
+
|
11
|
+
AppScreenshots.menu
|
12
|
+
- should be <= 5.0% difference
|
13
|
+
|
14
|
+
AppScreenshots.timeline
|
15
|
+
- should be <= 5.0% difference [FAILED - was 10.75%]
|
16
|
+
|
17
|
+
Bacon::Error: was 10.75%
|
18
|
+
spec.rb:698:in `satisfy:': AppScreenshots.timeline - should be <= 5.0% difference
|
19
|
+
spec.rb:438:in `execute_block'
|
20
|
+
spec.rb:402:in `run_postponed_block:'
|
21
|
+
spec.rb:397:in `resume'
|
22
|
+
|
23
|
+
3 specifications (3 requirements), 1 failures, 0 errors
|
24
|
+
```
|
25
|
+
|
26
|
+

|
27
|
+
|
28
|
+
(calculated diffs are truer than visual representation)
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
Add this line to your application's Gemfile:
|
33
|
+
|
34
|
+
gem 'motion-screenspecs'
|
35
|
+
|
36
|
+
And then execute:
|
37
|
+
|
38
|
+
$ bundle
|
39
|
+
|
40
|
+
Or install it yourself as:
|
41
|
+
|
42
|
+
$ gem install motion-screenspecs
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
motion-screenspecs works in unison with [motion-screenshots](https://github.com/usepropeller/motion-screenshots). Here are the steps you need to get everything working:
|
47
|
+
|
48
|
+
1. Create a subclass of `Motion::Screenshots::Base` (see the [motion-screenshots README](https://github.com/usepropeller/motion-screenshots/blob/master/README.md)).
|
49
|
+
|
50
|
+
2. Create the following directory structure:
|
51
|
+
|
52
|
+
```
|
53
|
+
spec/
|
54
|
+
screenshots/
|
55
|
+
[YourScreenshotSubclass]
|
56
|
+
expectations/
|
57
|
+
[title of your screenshot].png
|
58
|
+
[title of your other screenshot].png
|
59
|
+
[etc].png
|
60
|
+
```
|
61
|
+
|
62
|
+
The images in `expectations` are known values for your app. You can take these screenshots manually or using motion-screenshots' `rake screenshots`.
|
63
|
+
|
64
|
+
Failing image diffs will be saved in `spec/screenshots/[YourScreenshotSubclass]/failures`. All results from the latest test are saved to `spec/screenshots/[YourScreenshotSubclass]/results`.
|
65
|
+
|
66
|
+
3. Add a call to `tests_screenshots` in your specs:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
describe "Screenshots" do
|
70
|
+
tests_screenshots AppScreenshots
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
The [sample app](sample) is a complete example with a failing test.
|
75
|
+
|
76
|
+
Hats off to [Jeff Kreeftmeijer](http://jeffkreeftmeijer.com/2011/comparing-images-and-creating-image-diffs/) for the image diffing help!
|
77
|
+
|
78
|
+
## Configuration
|
79
|
+
|
80
|
+
There's a couple of configuration options you can use:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
Motion::Project::App.setup do |app|
|
84
|
+
# Set your tolerance % for image differences
|
85
|
+
Motion::Screenspecs.set_tolerance(5.0, app)
|
86
|
+
|
87
|
+
# Set how long the tests will wait for your screenshots to finish
|
88
|
+
Motion::Screenspecs.set_screenshot_timeout(120, app)
|
89
|
+
|
90
|
+
# Set how long the tests will wait for a given image to finish diffing
|
91
|
+
Motion::Screenspecs.set_diff_timeout(20, app)
|
92
|
+
|
93
|
+
# Set whether or not your failed diffs will open in Finder upon finishing tests
|
94
|
+
Motion::Screenspecs.open_failures_at_exit = true
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
`rake spec`
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
1. Fork it
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create new Pull Request
|
107
|
+
|
108
|
+
## Contact
|
109
|
+
|
110
|
+
[Clay Allsopp](http://clayallsopp.com/)
|
111
|
+
[@clayallsopp](https://twitter.com/clayallsopp)
|
@@ -0,0 +1,207 @@
|
|
1
|
+
unless defined?(Motion::Project::Config)
|
2
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'motion-screenshots'
|
6
|
+
require 'motion-env'
|
7
|
+
|
8
|
+
require 'webrick'
|
9
|
+
require 'oily_png'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'shellwords'
|
12
|
+
|
13
|
+
is_spec_mode = Rake.application.top_level_tasks.include?('spec')
|
14
|
+
|
15
|
+
if is_spec_mode
|
16
|
+
class Motion::Project::Config
|
17
|
+
|
18
|
+
def spec_core_files_with_screenshots
|
19
|
+
files = spec_core_files_without_screenshots
|
20
|
+
|
21
|
+
lib_helper = File.join(File.dirname(__FILE__), 'spec', 'helpers.rb')
|
22
|
+
files << lib_helper unless files.include?(lib_helper)
|
23
|
+
files
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method "spec_core_files_without_screenshots", "spec_core_files"
|
27
|
+
alias_method "spec_core_files", "spec_core_files_with_screenshots"
|
28
|
+
end
|
29
|
+
|
30
|
+
class Motion::Project::App
|
31
|
+
class << self
|
32
|
+
def build_with_screenspecs(platform, opts = {})
|
33
|
+
unless File.exist?('vendor/Pods/KSScreenshotManager')
|
34
|
+
Rake::Task["pod:install"].reenable
|
35
|
+
Rake::Task["pod:install"].invoke
|
36
|
+
end
|
37
|
+
build_without_screenspecs(platform, opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method "build_without_screenspecs", "build"
|
41
|
+
alias_method "build", "build_with_screenspecs"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Motion
|
47
|
+
module Screenspecs
|
48
|
+
PORT = 9678
|
49
|
+
TOLERANCE_ENV = '_motion-screenspecs-tolerance'
|
50
|
+
SCREENSHOT_TIMEOUT_ENV = '_motion-screenspecs-screenshot-timeout'
|
51
|
+
DIFF_TIMEOUT_ENV = '_motion-screenspecs-diff-timeout'
|
52
|
+
|
53
|
+
def self.tolerance
|
54
|
+
@tolerance
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.set_tolerance(tolerance, config)
|
58
|
+
@tolerance = tolerance
|
59
|
+
config.env[TOLERANCE_ENV] = tolerance
|
60
|
+
tolerance
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.set_screenshot_timeout(timeout, config)
|
64
|
+
config.env[SCREENSHOT_TIMEOUT_ENV] = timeout
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.set_diff_timeout(timeout, config)
|
68
|
+
config.env[DIFF_TIMEOUT_ENV] = timeout
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.open_failures_at_exit?
|
72
|
+
!!@open_failures_at_exit
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.open_failures_at_exit=(open)
|
76
|
+
@open_failures_at_exit = open
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.failures
|
80
|
+
@failures ||= []
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.screenshots_root(screenshot_class)
|
84
|
+
"spec/screenshots/#{screenshot_class}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.start_server!
|
88
|
+
# Start a web server to bounce file paths to-and-from the
|
89
|
+
# RubyMotion-CRuby barrier
|
90
|
+
@web_server ||= begin
|
91
|
+
server = WEBrick::HTTPServer.new(:Port => Motion::Screenspecs::PORT, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [])
|
92
|
+
server.mount '/', Motion::Screenspecs::Servlet
|
93
|
+
at_exit {
|
94
|
+
server.shutdown
|
95
|
+
}
|
96
|
+
Thread.start do
|
97
|
+
server.start
|
98
|
+
end
|
99
|
+
server
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
104
|
+
def do_GET (request, response)
|
105
|
+
if (title = request.query["title"]) &&
|
106
|
+
(screenshot_path = request.query["screenshot_path"]) &&
|
107
|
+
(screenshot_class = request.query['screenshot_class'])
|
108
|
+
screenshot_path.gsub!("file://", "")
|
109
|
+
screenshots_root = Motion::Screenspecs.screenshots_root(screenshot_class)
|
110
|
+
expectation_path = File.expand_path(File.join(screenshots_root, "expectations", "#{title}.png"))
|
111
|
+
result_path = File.expand_path(File.join(screenshots_root, "results", "#{title}.png"))
|
112
|
+
failure_path = File.expand_path(File.join(screenshots_root, "failures", "#{title}.png"))
|
113
|
+
FileUtils.mkdir_p(File.dirname(failure_path))
|
114
|
+
FileUtils.mkdir_p(File.dirname(result_path))
|
115
|
+
|
116
|
+
File.delete(result_path) if File.exists?(result_path)
|
117
|
+
temp_result_path = File.join(File.dirname(result_path), File.basename(screenshot_path))
|
118
|
+
FileUtils.cp(screenshot_path, File.dirname(result_path))
|
119
|
+
FileUtils.mv(temp_result_path, result_path)
|
120
|
+
|
121
|
+
File.delete(failure_path) if File.exists?(failure_path)
|
122
|
+
|
123
|
+
percentage = Motion::Screenspecs::ImageDiff.new.percentage(expectation_path, screenshot_path, failure_path)
|
124
|
+
success = percentage < Motion::Screenspecs.tolerance
|
125
|
+
response.status = success ? 200 : 400
|
126
|
+
response.content_type = "text/plain"
|
127
|
+
response.body = "%05.2f" % percentage
|
128
|
+
else
|
129
|
+
response.status = 404
|
130
|
+
response.body = "You did not provide the correct parameters"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class ImageDiff
|
136
|
+
include ChunkyPNG::Color
|
137
|
+
|
138
|
+
# via http://jeffkreeftmeijer.com/2011/comparing-images-and-creating-image-diffs/
|
139
|
+
def percentage(image_a, image_b, failure_path)
|
140
|
+
images = [
|
141
|
+
ChunkyPNG::Image.from_file(image_a),
|
142
|
+
ChunkyPNG::Image.from_file(image_b)
|
143
|
+
]
|
144
|
+
|
145
|
+
output = ChunkyPNG::Image.new(images.first.width, images.last.height, WHITE)
|
146
|
+
|
147
|
+
diff = []
|
148
|
+
|
149
|
+
images.first.height.times do |y|
|
150
|
+
images.first.row(y).each_with_index do |pixel, x|
|
151
|
+
unless pixel == images.last[x,y]
|
152
|
+
score = Math.sqrt(
|
153
|
+
(r(images.last[x,y]) - r(pixel)) ** 2 +
|
154
|
+
(g(images.last[x,y]) - g(pixel)) ** 2 +
|
155
|
+
(b(images.last[x,y]) - b(pixel)) ** 2
|
156
|
+
) / Math.sqrt(MAX ** 2 * 3)
|
157
|
+
|
158
|
+
output[x,y] = rgb(
|
159
|
+
r(pixel) + r(images.last[x,y]) - 2 * [r(pixel), r(images.last[x,y])].min,
|
160
|
+
g(pixel) + g(images.last[x,y]) - 2 * [g(pixel), g(images.last[x,y])].min,
|
161
|
+
b(pixel) + b(images.last[x,y]) - 2 * [b(pixel), b(images.last[x,y])].min
|
162
|
+
)
|
163
|
+
diff << score
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
summed = diff.inject {|sum, value| sum + value}
|
169
|
+
if summed
|
170
|
+
percent = (summed / images.first.pixels.length) * 100
|
171
|
+
else
|
172
|
+
percent = 0
|
173
|
+
end
|
174
|
+
if percent >= Motion::Screenspecs.tolerance
|
175
|
+
output.save(failure_path)
|
176
|
+
Motion::Screenspecs.failures << failure_path
|
177
|
+
end
|
178
|
+
percent
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
185
|
+
Motion::Project::App.setup do |app|
|
186
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion/**/*.rb")))
|
187
|
+
|
188
|
+
Motion::Screenspecs.set_tolerance(5.0, app)
|
189
|
+
Motion::Screenspecs.set_screenshot_timeout(120, app)
|
190
|
+
Motion::Screenspecs.set_diff_timeout(20, app)
|
191
|
+
Motion::Screenspecs.open_failures_at_exit = true
|
192
|
+
|
193
|
+
if is_spec_mode
|
194
|
+
app.pods do
|
195
|
+
pod 'KSScreenshotManager'
|
196
|
+
end
|
197
|
+
|
198
|
+
Motion::Screenspecs.start_server!
|
199
|
+
|
200
|
+
at_exit {
|
201
|
+
if Motion::Screenspecs.open_failures_at_exit?
|
202
|
+
folder_path = Dir.glob('spec/**/failures').first
|
203
|
+
`open #{folder_path.shellescape}` unless Motion::Screenspecs.failures.empty?
|
204
|
+
end
|
205
|
+
}
|
206
|
+
end
|
207
|
+
end
|
data/lib/spec/helpers.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Bacon
|
2
|
+
class Context
|
3
|
+
def tests_screenshots(screenshot_class)
|
4
|
+
describe screenshot_class do
|
5
|
+
shared_screenshots = screenshot_class.send(:shared)
|
6
|
+
shared_screenshots.exitOnComplete = false
|
7
|
+
if shared_screenshots.respond_to?("isLoggingEnabled")
|
8
|
+
shared_screenshots.loggingEnabled = false
|
9
|
+
end
|
10
|
+
tolerance = ENV['_motion-screenspecs-tolerance']
|
11
|
+
screenshot_timeout = ENV['_motion-screenspecs-screenshot-timeout']
|
12
|
+
diff_timeout = ENV['_motion-screenspecs-diff-timeout']
|
13
|
+
|
14
|
+
it "should take screenshots" do
|
15
|
+
existing_after = shared_screenshots.screenshot_groups.last.instance_variable_get("@after_actions")
|
16
|
+
shared_screenshots.screenshot_groups.last.after do
|
17
|
+
existing_after.call if existing_after
|
18
|
+
|
19
|
+
# resume tests
|
20
|
+
resume
|
21
|
+
end
|
22
|
+
|
23
|
+
screenshot_class.start!
|
24
|
+
wait_max screenshot_timeout do
|
25
|
+
|
26
|
+
true.should == true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
shared_screenshots.screenshot_groups.each do |group|
|
31
|
+
title = group.instance_variable_get('@title')
|
32
|
+
before do
|
33
|
+
path_url = shared_screenshots.screenshotsURL
|
34
|
+
|
35
|
+
is_retina = UIScreen.mainScreen.scale != 1.0
|
36
|
+
density = is_retina ? "@2x" : ""
|
37
|
+
device_prefix = nil
|
38
|
+
|
39
|
+
if UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
|
40
|
+
device_prefix = "ipad"
|
41
|
+
elsif UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone
|
42
|
+
device_prefix = "iphone#{CGRectGetHeight(UIScreen.mainScreen.bounds).to_i}"
|
43
|
+
end
|
44
|
+
|
45
|
+
device_prefix << density
|
46
|
+
|
47
|
+
file_name = "#{device_prefix}-#{NSLocale.currentLocale.localeIdentifier}-#{title}.png"
|
48
|
+
path = path_url.URLByAppendingPathComponent(file_name)
|
49
|
+
@screenshot_path = path.absoluteString
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#{screenshot_class}.#{title}" do
|
53
|
+
it "should be < #{tolerance}% difference" do
|
54
|
+
|
55
|
+
url = NSURL.URLWithString "http://localhost:9678?screenshot_class=#{screenshot_class}&title=#{title}&screenshot_path=#{@screenshot_path}"
|
56
|
+
request = NSURLRequest.requestWithURL(url)
|
57
|
+
NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.mainQueue,
|
58
|
+
completionHandler:->(ns_url_response, data, error){
|
59
|
+
@response = ns_url_response
|
60
|
+
@data = data
|
61
|
+
resume
|
62
|
+
})
|
63
|
+
|
64
|
+
wait_max diff_timeout do
|
65
|
+
body = NSString.alloc.initWithData(@data, encoding:NSUTF8StringEncoding)
|
66
|
+
@response.statusCode.should.satisfy("was #{body}%") {|object|
|
67
|
+
object == 200
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-screenspecs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Clay Allsopp
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: motion-screenshots
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oily_png
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: motion-env
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Test your RubyMotion apps using screenshot comparison.
|
70
|
+
email:
|
71
|
+
- clay.allsopp@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- README.md
|
77
|
+
- lib/motion-screenspecs.rb
|
78
|
+
- lib/spec/helpers.rb
|
79
|
+
homepage: https://github.com/usepropeller/motion-screenspecs
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.0.3
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: Test your RubyMotion apps using screenshot comparison.
|
103
|
+
test_files: []
|