zucchini-ios 0.6.0 → 0.6.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.
- data/.gitignore +1 -0
- data/CHANGELOG.md +50 -0
- data/README.md +7 -2
- data/lib/screenshot.rb +2 -1
- data/lib/uia/base.coffee +12 -3
- data/lib/uia/screen.coffee +7 -1
- data/lib/version.rb +1 -1
- data/spec/lib/config_spec.rb +54 -0
- data/spec/lib/detector_spec.rb +52 -0
- data/spec/lib/feature_spec.rb +69 -0
- data/spec/lib/generator_spec.rb +27 -0
- data/spec/lib/report_spec.rb +31 -0
- data/spec/lib/screenshot_spec.rb +109 -0
- data/spec/sample_screenshots/01_LandscapeLeft_Screenshot.png +0 -0
- data/spec/sample_screenshots/02_LandscapeRight_Screenshot.png +0 -0
- data/spec/sample_screenshots/03_PortraitUpsideDown_Screenshot.png +0 -0
- data/spec/sample_setup/feature_one/feature.zucchini +1 -0
- data/spec/sample_setup/feature_one/masks/retina_ios5/06_sign up_spinner.png +0 -0
- data/spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner.png +0 -0
- data/spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner_error.png +0 -0
- data/spec/sample_setup/feature_one/run_data/Run 1/06_sign up_spinner.png +0 -0
- data/spec/sample_setup/feature_three/feature.zucchini +1 -0
- data/spec/sample_setup/feature_two/feature.zucchini +1 -0
- data/spec/sample_setup/feature_two/masks/retina_ios5/06_sign up_spinner.png +0 -0
- data/spec/sample_setup/feature_two/reference/retina_ios5/06_sign up_spinner.png +0 -0
- data/spec/sample_setup/feature_two/reference/retina_ios5/06_sign up_spinner_error.png +0 -0
- data/spec/sample_setup/feature_two/run_data/Run 1/06_sign up_spinner.png +0 -0
- data/spec/sample_setup/support/config.yml +16 -0
- data/spec/sample_setup/support/lib/helpers.coffee +3 -0
- data/spec/sample_setup/support/masks/retina_ios5.png +0 -0
- data/spec/sample_setup/support/screens/splash.coffee +8 -0
- data/spec/sample_setup_2/support/config.yml +15 -0
- data/spec/sample_setup_3/support/config.yml +13 -0
- data/spec/spec_helper.rb +18 -0
- data/zucchini-ios.gemspec +28 -0
- metadata +125 -73
- data/Gemfile.lock +0 -30
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
## 0.6.1 / 2013-07-28
|
2
|
+
* Add Rotate device screen action - [@phatmann][], [#19][]
|
3
|
+
* Fix screenshot code to work with the latest version of ImageMagick - [@phatmann][], [#17][]
|
4
|
+
* Raise error if screen anchor is not found - [@phatmann][], [#16][]
|
5
|
+
|
6
|
+
## 0.6.0 / 2013-03-30
|
7
|
+
* Add a mask for retina iPad - [@phatmann][], [#8][]
|
8
|
+
* Fix alert Confirm / Cancel actions so that they match to end of line - [@phatmann][], [#9][]
|
9
|
+
* Allow app path to be set via an environment variable - [@phatmann][], [#10][]
|
10
|
+
* Screenshot orientation matching device orientation - [@Jaco][]-Pretorius, [#13][]
|
11
|
+
|
12
|
+
## 0.5.9 / 2013-02-04
|
13
|
+
* Add default device flag - [@insanehunter][], [#6][]
|
14
|
+
|
15
|
+
## 0.5.8 / 2013-02-03
|
16
|
+
* Add `zucchini approve` command to use screenshots from the last run as references - [@insanehunter][], [#5][]
|
17
|
+
|
18
|
+
## 0.5.7 / 2012-12-16
|
19
|
+
* Always generate report, even if it is not displayed - [@vaskas][]
|
20
|
+
* Add `Select the date` action - [@NathanSudell][]
|
21
|
+
* Work around Instruments 4.5 throw-related crash - [@vaskas][]
|
22
|
+
* Add `--silent (-s)` flag to prevent report from opening - [@vaskas][]
|
23
|
+
* Add `Type` and `Clear` textfield actions - [@NathanSudell][], [#3][]
|
24
|
+
* Add `Confirm the alert` and `Cancel the alert` action - [@NathanSudell][], [#1][]
|
25
|
+
|
26
|
+
## 0.5.6 / 2012-12-02
|
27
|
+
* Fix clamp 0.5.0 compatibility - [@vaskas][]
|
28
|
+
* Fix imagemagick 6.7.7-6 (possibly, other versions too) - [@vaskas][]
|
29
|
+
* Fix Instruments template detection for Xcode 4.5 - [@vaskas][]
|
30
|
+
* Fixed config to take relative paths to the app - [@coomans][]
|
31
|
+
* Quote path params in call to instruments - [@kconnor][]
|
32
|
+
<!--- The following link definition list is generated by PimpMyChangelog --->
|
33
|
+
[#1]: https://github.com/src/zucchini/issues/1
|
34
|
+
[#3]: https://github.com/src/zucchini/issues/3
|
35
|
+
[#5]: https://github.com/src/zucchini/issues/5
|
36
|
+
[#6]: https://github.com/src/zucchini/issues/6
|
37
|
+
[#8]: https://github.com/src/zucchini/issues/8
|
38
|
+
[#9]: https://github.com/src/zucchini/issues/9
|
39
|
+
[#10]: https://github.com/src/zucchini/issues/10
|
40
|
+
[#13]: https://github.com/src/zucchini/issues/13
|
41
|
+
[#16]: https://github.com/src/zucchini/issues/16
|
42
|
+
[#17]: https://github.com/src/zucchini/issues/17
|
43
|
+
[#19]: https://github.com/src/zucchini/issues/19
|
44
|
+
[@Jaco-Pretorius]: https://github.com/Jaco-Pretorius
|
45
|
+
[@NathanSudell]: https://github.com/NathanSudell
|
46
|
+
[@coomans]: https://github.com/coomans
|
47
|
+
[@insanehunter]: https://github.com/insanehunter
|
48
|
+
[@kconnor]: https://github.com/kconnor
|
49
|
+
[@phatmann]: https://github.com/phatmann
|
50
|
+
[@vaskas]: https://github.com/vaskas
|
data/README.md
CHANGED
@@ -35,7 +35,7 @@ zucchini generate --feature /path/to/my_project/features/my_feature
|
|
35
35
|
|
36
36
|
Start hacking by modifying features/my_feature/feature.zucchini and features/support/screens/welcome.coffee.
|
37
37
|
|
38
|
-
Alternatively, check out the [zucchini-demo](https://github.com/
|
38
|
+
Alternatively, check out the [zucchini-demo](https://github.com/zucchini-src/zucchini-demo) project featuring an easy to explore Zucchini setup around Apple's CoreDataBooks sample.
|
39
39
|
|
40
40
|
Running on the device
|
41
41
|
--------------------------------
|
@@ -77,8 +77,13 @@ devices:
|
|
77
77
|
app: ./Build/Products/Debug-iphoneos/CoreDataBooks.app
|
78
78
|
```
|
79
79
|
|
80
|
+
If you do not want to hard-code the app path in your config files, you can use the environment variable `ZUCCHINI_APP`:
|
80
81
|
|
81
|
-
|
82
|
+
```
|
83
|
+
ZUCCHINI_APP="/path/to/app" zucchini...
|
84
|
+
```
|
85
|
+
|
86
|
+
Run Zucchini and watch the simulator go!
|
82
87
|
|
83
88
|
```
|
84
89
|
ZUCCHINI_DEVICE="iOS Simulator" zucchini run /path/to/my_feature
|
data/lib/screenshot.rb
CHANGED
@@ -63,7 +63,8 @@ class Zucchini::Screenshot
|
|
63
63
|
|
64
64
|
compare_command = "compare -metric AE -fuzz 2% -dissimilarity-threshold 1 -subimage-search"
|
65
65
|
out = `#{compare_command} \"#{@masked_paths[:specifically]}\" \"#{@test_path}\" \"#{@diff_path}\" 2>&1`
|
66
|
-
|
66
|
+
out.chomp!
|
67
|
+
@diff = (out == '0') ? [:passed, nil] : [:failed, out]
|
67
68
|
@diff = [:pending, @diff[1]] if @pending
|
68
69
|
else
|
69
70
|
@diff = [:failed, "no reference or pending screenshot for #{@device[:screen]}\n"]
|
data/lib/uia/base.coffee
CHANGED
@@ -35,17 +35,16 @@ UIAElement.prototype.$ = (name) ->
|
|
35
35
|
elem
|
36
36
|
|
37
37
|
target.waitForElement = (element) ->
|
38
|
-
return unless element
|
38
|
+
return false unless element
|
39
39
|
found = false
|
40
40
|
counter = 0
|
41
41
|
while not found and (counter < 10)
|
42
42
|
if element.isValid() and element.isVisible()
|
43
43
|
found = true
|
44
|
-
puts "Got the awaited element: #{element}"
|
45
|
-
@delay 1
|
46
44
|
else
|
47
45
|
@delay 0.5
|
48
46
|
counter++
|
47
|
+
return found
|
49
48
|
|
50
49
|
isNullElement = (elem) -> elem.toString() == "[object UIAElementNil]"
|
51
50
|
|
@@ -71,6 +70,16 @@ class Zucchini
|
|
71
70
|
catch e
|
72
71
|
raise "Screen '#{screenName}' not defined"
|
73
72
|
|
73
|
+
if screen.anchor
|
74
|
+
element = screen.anchor()
|
75
|
+
found = target.waitForElement(element)
|
76
|
+
|
77
|
+
if found
|
78
|
+
puts "Found anchor for screen '#{screenName}'"
|
79
|
+
target.delay 1
|
80
|
+
else
|
81
|
+
raise "Could not find anchor for screen '#{screenName}'"
|
82
|
+
|
74
83
|
for line in lines.slice(1)
|
75
84
|
functionFound = false
|
76
85
|
for regExpText, func of screen.actions
|
data/lib/uia/screen.coffee
CHANGED
@@ -11,7 +11,6 @@ class Screen
|
|
11
11
|
target.captureScreenWithName("#{orientation}_#{name}")
|
12
12
|
|
13
13
|
constructor: (@name) ->
|
14
|
-
if @anchor then target.waitForElement @anchor()
|
15
14
|
|
16
15
|
elements: {}
|
17
16
|
actions :
|
@@ -67,3 +66,10 @@ class Screen
|
|
67
66
|
raise "Counldn't find the month #{dateParts[2]}" unless counter <12
|
68
67
|
# Set Year
|
69
68
|
view.pickers()[0].wheels()[2].selectValue(dateParts[3])
|
69
|
+
|
70
|
+
'Show elements' : ->
|
71
|
+
view.logElementTree()
|
72
|
+
|
73
|
+
'Rotate device to "([^"]*)"$': (orientation) ->
|
74
|
+
orientation = if orientation == "landscape" then UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT else UIA_DEVICE_ORIENTATION_PORTRAIT
|
75
|
+
target.setDeviceOrientation(orientation)
|
data/lib/version.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zucchini::Config do
|
4
|
+
describe "app" do
|
5
|
+
context "app environment variable" do
|
6
|
+
before(:all) { ENV['ZUCCHINI_APP'] = 'foo.app' }
|
7
|
+
|
8
|
+
it "should use environment variable if app not in config" do
|
9
|
+
Zucchini::Config.base_path = "spec/sample_setup_3"
|
10
|
+
Zucchini::Config.app.should include 'foo.app'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should not use environment variable if app in config" do
|
14
|
+
Zucchini::Config.base_path = "spec/sample_setup"
|
15
|
+
Zucchini::Config.app.should include 'MyApp.app'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "device" do
|
21
|
+
before(:all) { Zucchini::Config.base_path = "spec/sample_setup" }
|
22
|
+
|
23
|
+
context "device present in config.yml" do
|
24
|
+
it "should return the device hash" do
|
25
|
+
Zucchini::Config.device("My iDevice").should eq({:name =>"My iDevice", :udid =>"lolffb28d74a6fraj2156090784avasc50725dd0", :screen =>"ipad_ios5"})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "device not present in config.yml" do
|
30
|
+
it "should raise an error" do
|
31
|
+
expect { Zucchini::Config.device("My Android Phone")}.to raise_error "Device not listed in config.yml"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "default device" do
|
36
|
+
it "should use default device if device name argument is nil" do
|
37
|
+
Zucchini::Config.device(nil).should eq({:name =>"Default Device", :screen =>"low_ios5", :udid => nil})
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should raise error if no default device provided" do
|
41
|
+
Zucchini::Config.base_path = "spec/sample_setup_2"
|
42
|
+
expect { Zucchini::Config.device(nil) }.to raise_error "Neither default device nor ZUCCHINI_DEVICE environment variable was set"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "url" do
|
48
|
+
before(:all) { Zucchini::Config.base_path = "spec/sample_setup" }
|
49
|
+
|
50
|
+
it "should return a full URL string for a given server name" do
|
51
|
+
Zucchini::Config.url('backend', '/api').should eq "http://192.168.1.2:8080/api"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zucchini::Detector do
|
4
|
+
before do
|
5
|
+
detector.path = "spec/sample_setup/feature_one"
|
6
|
+
ENV['ZUCCHINI_DEVICE'] = 'My iDevice'
|
7
|
+
end
|
8
|
+
|
9
|
+
let (:detector) { Zucchini::Detector.new(nil) }
|
10
|
+
|
11
|
+
describe "execute" do
|
12
|
+
subject { lambda { detector.execute } }
|
13
|
+
|
14
|
+
context "feature directory doesn't exist" do
|
15
|
+
before { detector.path = "spec/sample_setup/erroneous_feature" }
|
16
|
+
it { should raise_error "Directory spec/sample_setup/erroneous_feature does not exist" }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "device hasn't been found" do
|
20
|
+
before { ENV['ZUCCHINI_DEVICE'] = 'My Android Phone' }
|
21
|
+
it { should raise_error "Device not listed in config.yml" }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "detect features" do
|
26
|
+
subject { lambda { detector.detect_features(@path) } }
|
27
|
+
|
28
|
+
context "path to a single feature" do
|
29
|
+
before { @path = "spec/sample_setup/feature_one" }
|
30
|
+
it "should detect it" do
|
31
|
+
subject.call[0].path.should eq @path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "path to a directory with features" do
|
36
|
+
before { @path = File.expand_path("spec/sample_setup") }
|
37
|
+
it "should detect all features in it" do
|
38
|
+
subject.call.length.should eq 3
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "path to a non-feature directory" do
|
43
|
+
before do
|
44
|
+
@path = File.expand_path("spec/sample_setup/bad_feature")
|
45
|
+
FileUtils.mkdir(@path)
|
46
|
+
end
|
47
|
+
|
48
|
+
it { should raise_error }
|
49
|
+
after { FileUtils.rmdir(@path) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zucchini::Feature do
|
4
|
+
let(:path) { './spec/sample_setup/feature_one' }
|
5
|
+
let(:feature) { Zucchini::Feature.new(path) }
|
6
|
+
|
7
|
+
after(:all) { FileUtils.rm_rf Dir.glob("#{path}/run_data/feature.*") }
|
8
|
+
|
9
|
+
describe "#compile_js" do
|
10
|
+
before { feature.compile_js }
|
11
|
+
|
12
|
+
it "should strip comments from the feature file" do
|
13
|
+
File.read("#{feature.run_data_path}/feature.coffee").index('#').should be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "feature.js output" do
|
17
|
+
subject { File.read("#{feature.run_data_path}/feature.js") }
|
18
|
+
|
19
|
+
it "should include screen definitions" do
|
20
|
+
should match /SplashScreen = \(function/
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should include Zucchini runtime" do
|
24
|
+
should match /Zucchini.run = /
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should include custom libraries from support/lib" do
|
28
|
+
should match /Helpers.example = /
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "approve" do
|
34
|
+
subject { lambda { feature.approve "reference" } }
|
35
|
+
|
36
|
+
context "no previous run data" do
|
37
|
+
before { feature.path = './spec/sample_setup/feature_three' }
|
38
|
+
it { should raise_error "Directory ./spec/sample_setup/feature_three doesn't contain previous run data" }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "copies screenshots to reference directory" do
|
42
|
+
before do
|
43
|
+
feature.path = './spec/sample_setup/feature_three'
|
44
|
+
feature.device = {screen: 'retina_ios5'}
|
45
|
+
|
46
|
+
# Copying some random image to run screenshots.
|
47
|
+
@screenshot_path = "#{feature.path}/run_data/Run\ 1/screenshot.png"
|
48
|
+
FileUtils.mkdir_p(File.dirname(@screenshot_path))
|
49
|
+
FileUtils.copy_file("./spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner.png", @screenshot_path)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should copy screenshot to reference directory" do
|
53
|
+
feature.approve "reference"
|
54
|
+
(File.exists? "#{feature.path}/reference/retina_ios5/screenshot.png").should eq true
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should copy screenshot to pending directory" do
|
58
|
+
feature.approve "pending"
|
59
|
+
(File.exists? "#{feature.path}/pending/retina_ios5/screenshot.png").should eq true
|
60
|
+
end
|
61
|
+
|
62
|
+
after do
|
63
|
+
FileUtils.rm_rf("#{feature.path}/run_data")
|
64
|
+
FileUtils.rm_rf("#{feature.path}/reference")
|
65
|
+
FileUtils.rm_rf("#{feature.path}/pending")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zucchini::Generator do
|
4
|
+
let(:project_path) { "/tmp/z_test_project" }
|
5
|
+
let(:feature_path) { "#{project_path}/features/rspec_feature" }
|
6
|
+
let(:generator) { Zucchini::Generator.new(nil) }
|
7
|
+
|
8
|
+
after(:all) { FileUtils.rm_rf(project_path) }
|
9
|
+
|
10
|
+
context "generate a project" do
|
11
|
+
before { generator.path = project_path }
|
12
|
+
it "should create a project directory" do
|
13
|
+
generator.stub!(:project?).and_return(true)
|
14
|
+
generator.execute
|
15
|
+
File.exists?("#{project_path}/features/support/config.yml").should be_true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "generate a feature" do
|
20
|
+
before { generator.path = feature_path }
|
21
|
+
it "should create a feature directory" do
|
22
|
+
generator.stub!(:feature?).and_return(true)
|
23
|
+
generator.execute
|
24
|
+
File.exists?("#{feature_path}/feature.zucchini").should be_true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zucchini::Report do
|
4
|
+
let (:device) { { :name => "iPad 2", :screen => "ipad_ios5", :udid => "rspec012345" } }
|
5
|
+
let (:feature) do
|
6
|
+
fake_screenshots = (1..7).to_a.map do |num|
|
7
|
+
screenshot = Zucchini::Screenshot.new("#{num}.screen_#{num}.png", device)
|
8
|
+
screenshot.diff = (num > 3) ? [:passed, nil] : [:failed, "120\n"]
|
9
|
+
screenshot
|
10
|
+
end
|
11
|
+
|
12
|
+
feature = Zucchini::Feature.new("/my/sample/feature")
|
13
|
+
feature.device = device
|
14
|
+
feature.stub!(:screenshots).and_return(fake_screenshots)
|
15
|
+
feature
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:html_path) { '/tmp/zucchini_rspec_report.html' }
|
19
|
+
after { FileUtils.rm(html_path) }
|
20
|
+
|
21
|
+
subject { Zucchini::Report.new([feature], false, html_path) }
|
22
|
+
before { Zucchini::Report.any_instance.stub(:log) }
|
23
|
+
|
24
|
+
its(:text) { should eq "feature:\n4 passed, 3 failed, 0 pending\n\nFailed:\n 1.screen_1.png: 120\n 2.screen_2.png: 120\n 3.screen_3.png: 120\n" }
|
25
|
+
|
26
|
+
it "should produce a a correct HTML report" do
|
27
|
+
report = subject.html
|
28
|
+
report.scan(/<dl class="passed.*screen/).length.should eq 4
|
29
|
+
report.scan(/<dl class="failed.*screen/).length.should eq 3
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
def md5(blob)
|
6
|
+
Digest::MD5.hexdigest(blob)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Zucchini::Screenshot do
|
10
|
+
let (:device) { { :name => "iPhone 4S", :screen => "retina_ios5", :udid => "rspec987654" } }
|
11
|
+
let (:base_path) { "spec/sample_setup/feature_one" }
|
12
|
+
let (:original_path) { "#{base_path}/run_data/Run\ 1/06_sign\ up_spinner.png" }
|
13
|
+
|
14
|
+
let (:screenshot) do
|
15
|
+
screenshot = Zucchini::Screenshot.new(original_path, device)
|
16
|
+
screenshot.masked_paths = { :globally => "#{base_path}/globally_masked.png", :specifically => "#{base_path}/specifically_masked.png" }
|
17
|
+
screenshot.mask
|
18
|
+
screenshot
|
19
|
+
end
|
20
|
+
|
21
|
+
after(:all) do
|
22
|
+
screenshot.masked_paths.each { |k, path| FileUtils.rm(path) }
|
23
|
+
FileUtils.rm_rf("#{base_path}/run_data/Masked")
|
24
|
+
FileUtils.rm_rf(screenshot.diff_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "mask" do
|
28
|
+
before do
|
29
|
+
@md5 = {
|
30
|
+
:original => md5(File.read(original_path)),
|
31
|
+
:globally_masked => md5(File.read(screenshot.masked_paths[:globally])),
|
32
|
+
:specifically_masked => md5(File.read(screenshot.masked_paths[:specifically]))
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should apply a standard global mask based on the screen" do
|
37
|
+
File.exists?(screenshot.masked_paths[:globally]).should be true
|
38
|
+
@md5[:globally_masked].should_not be_equal @md5[:original]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should apply a screenshot-specific mask if it exists" do
|
42
|
+
File.exists?(screenshot.masked_paths[:specifically]).should be true
|
43
|
+
@md5[:specifically_masked].should_not be_equal @md5[:original]
|
44
|
+
@md5[:specifically_masked].should_not be_equal @md5[:globally_masked]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "compare" do
|
49
|
+
context "images are identical" do
|
50
|
+
it "should have a passed indicator in the diff" do
|
51
|
+
screenshot.compare
|
52
|
+
screenshot.diff.should eq [:passed, nil]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "images are different" do
|
57
|
+
it "should have a failed indicator in the diff" do
|
58
|
+
screenshot.stub!(:mask_reference)
|
59
|
+
screenshot.test_path = "#{base_path}/reference/#{device[:screen]}/06_sign\ up_spinner_error.png"
|
60
|
+
screenshot.compare
|
61
|
+
screenshot.diff.should eq [:failed, "3017"]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "mask reference" do
|
67
|
+
it "should create masked versions of reference screenshots" do
|
68
|
+
screenshot.mask_reference
|
69
|
+
|
70
|
+
File.exists?(screenshot.test_path).should be_true
|
71
|
+
md5(File.read(screenshot.test_path)).should_not be_equal md5(File.read("#{base_path}/reference/#{device[:screen]}/06_sign\ up_spinner.png"))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "rotate" do
|
76
|
+
let (:sample_screenshots_path) { "spec/sample_screenshots" }
|
77
|
+
let (:reference_screenshot_path) { File.join(sample_screenshots_path, "rotated/01_Screenshot.png") }
|
78
|
+
let (:reference_md5) { md5(File.read(reference_screenshot_path)) }
|
79
|
+
let (:temp_dir) { Dir.mktmpdir }
|
80
|
+
before do
|
81
|
+
`cp #{File.join(sample_screenshots_path, "*")} #{temp_dir}`
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should rotate the landscape-left screenshot" do
|
85
|
+
screenshot = Zucchini::Screenshot.new(File.join(temp_dir,'01_LandscapeLeft_Screenshot.png'), nil, true)
|
86
|
+
screenshot.rotate
|
87
|
+
File.exists?(File.join(temp_dir,'01_LandscapeLeft_Screenshot.png')).should be_false
|
88
|
+
File.exists?(File.join(temp_dir,'01_Screenshot.png')).should be_true
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should rotate the landscape-right screenshot" do
|
92
|
+
screenshot = Zucchini::Screenshot.new(File.join(temp_dir,'02_LandscapeRight_Screenshot.png'), nil, true)
|
93
|
+
screenshot.rotate
|
94
|
+
File.exists?(File.join(temp_dir,'02_LandscapeRight_Screenshot.png')).should be_false
|
95
|
+
File.exists?(File.join(temp_dir,'02_Screenshot.png')).should be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should rotate the upside-down screenshot" do
|
99
|
+
screenshot = Zucchini::Screenshot.new(File.join(temp_dir,'03_PortraitUpsideDown_Screenshot.png'), nil, true)
|
100
|
+
screenshot.rotate
|
101
|
+
File.exists?(File.join(temp_dir,'02_PortraitUpsideDown_Screenshot.png')).should be_false
|
102
|
+
File.exists?(File.join(temp_dir,'03_Screenshot.png')).should be_true
|
103
|
+
end
|
104
|
+
|
105
|
+
after do
|
106
|
+
FileUtils.remove_entry temp_dir
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
# Start on the "Splash" screen
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
# Start on the "Menu" screen
|
@@ -0,0 +1 @@
|
|
1
|
+
# Start on the "Menu" screen
|
Binary file
|
Binary file
|
Binary file
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'clamp'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
$LOAD_PATH << File.expand_path("#{File.dirname(__FILE__)}/..")
|
5
|
+
require 'lib/config'
|
6
|
+
require 'lib/screenshot'
|
7
|
+
require 'lib/report'
|
8
|
+
require 'lib/feature'
|
9
|
+
require 'lib/detector'
|
10
|
+
require 'lib/runner'
|
11
|
+
require 'lib/generator'
|
12
|
+
require 'lib/approver'
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.color_enabled = true
|
16
|
+
config.tty = true
|
17
|
+
#config.formatter = :documentation
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'version'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "zucchini-ios"
|
8
|
+
s.version = Zucchini::VERSION
|
9
|
+
s.date = Date.today.to_s
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ["Vasily Mikhaylichenko", "Rajesh Kumar", "Kevin O'Neill"]
|
12
|
+
s.license = "BSD"
|
13
|
+
s.email = ["vaskas@zucchiniframework.org"]
|
14
|
+
s.homepage = "http://www.zucchiniframework.org"
|
15
|
+
s.summary = %q{Functional testing framework for iOS-powered devices}
|
16
|
+
s.description = %q{Zucchini follows simple walkthrough scenarios for your iOS app, takes screenshots and compares them to the reference ones.}
|
17
|
+
|
18
|
+
s.add_runtime_dependency "clamp"
|
19
|
+
s.add_development_dependency "rspec"
|
20
|
+
s.add_development_dependency "watchr"
|
21
|
+
|
22
|
+
s.files = `git ls-files | grep -vE '(web|.watchr)'`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
24
|
+
s.executables = %w(zucchini)
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
|
27
|
+
s.required_ruby_version = '>= 1.9.2'
|
28
|
+
end
|
metadata
CHANGED
@@ -1,74 +1,79 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: zucchini-ios
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 6
|
8
|
-
- 0
|
9
|
-
version: 0.6.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Vasily Mikhaylichenko
|
13
9
|
- Rajesh Kumar
|
14
10
|
- Kevin O'Neill
|
15
11
|
autorequire:
|
16
12
|
bindir: bin
|
17
13
|
cert_chain: []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
dependencies:
|
22
|
-
- !ruby/object:Gem::Dependency
|
14
|
+
date: 2013-07-28 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
23
17
|
name: clamp
|
24
|
-
|
25
|
-
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
- 0
|
31
|
-
version: "0"
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ! '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0'
|
32
24
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: rspec
|
36
25
|
prerelease: false
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: rspec
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
44
40
|
type: :development
|
45
|
-
version_requirements: *id002
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: watchr
|
48
41
|
prerelease: false
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: watchr
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
56
|
type: :development
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
description: Zucchini follows simple walkthrough scenarios for your iOS app, takes
|
65
|
+
screenshots and compares them to the reference ones.
|
66
|
+
email:
|
60
67
|
- vaskas@zucchiniframework.org
|
61
|
-
executables:
|
68
|
+
executables:
|
62
69
|
- zucchini
|
63
70
|
extensions: []
|
64
|
-
|
65
71
|
extra_rdoc_files: []
|
66
|
-
|
67
|
-
files:
|
72
|
+
files:
|
68
73
|
- .gitignore
|
69
74
|
- .travis.yml
|
75
|
+
- CHANGELOG.md
|
70
76
|
- Gemfile
|
71
|
-
- Gemfile.lock
|
72
77
|
- LICENSE.md
|
73
78
|
- README.md
|
74
79
|
- Rakefile
|
@@ -91,6 +96,33 @@ files:
|
|
91
96
|
- lib/uia/base.coffee
|
92
97
|
- lib/uia/screen.coffee
|
93
98
|
- lib/version.rb
|
99
|
+
- spec/lib/config_spec.rb
|
100
|
+
- spec/lib/detector_spec.rb
|
101
|
+
- spec/lib/feature_spec.rb
|
102
|
+
- spec/lib/generator_spec.rb
|
103
|
+
- spec/lib/report_spec.rb
|
104
|
+
- spec/lib/screenshot_spec.rb
|
105
|
+
- spec/sample_screenshots/01_LandscapeLeft_Screenshot.png
|
106
|
+
- spec/sample_screenshots/02_LandscapeRight_Screenshot.png
|
107
|
+
- spec/sample_screenshots/03_PortraitUpsideDown_Screenshot.png
|
108
|
+
- spec/sample_setup/feature_one/feature.zucchini
|
109
|
+
- spec/sample_setup/feature_one/masks/retina_ios5/06_sign up_spinner.png
|
110
|
+
- spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner.png
|
111
|
+
- spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner_error.png
|
112
|
+
- spec/sample_setup/feature_one/run_data/Run 1/06_sign up_spinner.png
|
113
|
+
- spec/sample_setup/feature_three/feature.zucchini
|
114
|
+
- spec/sample_setup/feature_two/feature.zucchini
|
115
|
+
- spec/sample_setup/feature_two/masks/retina_ios5/06_sign up_spinner.png
|
116
|
+
- spec/sample_setup/feature_two/reference/retina_ios5/06_sign up_spinner.png
|
117
|
+
- spec/sample_setup/feature_two/reference/retina_ios5/06_sign up_spinner_error.png
|
118
|
+
- spec/sample_setup/feature_two/run_data/Run 1/06_sign up_spinner.png
|
119
|
+
- spec/sample_setup/support/config.yml
|
120
|
+
- spec/sample_setup/support/lib/helpers.coffee
|
121
|
+
- spec/sample_setup/support/masks/retina_ios5.png
|
122
|
+
- spec/sample_setup/support/screens/splash.coffee
|
123
|
+
- spec/sample_setup_2/support/config.yml
|
124
|
+
- spec/sample_setup_3/support/config.yml
|
125
|
+
- spec/spec_helper.rb
|
94
126
|
- templates/feature/feature.zucchini
|
95
127
|
- templates/feature/masks/retina_ios5/.gitkeep
|
96
128
|
- templates/feature/pending/retina_ios5/.gitkeep
|
@@ -104,37 +136,57 @@ files:
|
|
104
136
|
- templates/project/features/support/masks/low_ios4.png
|
105
137
|
- templates/project/features/support/masks/retina_ios5.png
|
106
138
|
- templates/project/features/support/screens/welcome.coffee
|
107
|
-
|
139
|
+
- zucchini-ios.gemspec
|
108
140
|
homepage: http://www.zucchiniframework.org
|
109
|
-
licenses:
|
141
|
+
licenses:
|
110
142
|
- BSD
|
111
143
|
post_install_message:
|
112
144
|
rdoc_options: []
|
113
|
-
|
114
|
-
require_paths:
|
145
|
+
require_paths:
|
115
146
|
- lib
|
116
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
- 1
|
122
|
-
- 9
|
123
|
-
- 2
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
124
152
|
version: 1.9.2
|
125
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
version: "0"
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
132
159
|
requirements: []
|
133
|
-
|
134
160
|
rubyforge_project:
|
135
|
-
rubygems_version: 1.
|
161
|
+
rubygems_version: 1.8.23
|
136
162
|
signing_key:
|
137
163
|
specification_version: 3
|
138
164
|
summary: Functional testing framework for iOS-powered devices
|
139
|
-
test_files:
|
140
|
-
|
165
|
+
test_files:
|
166
|
+
- spec/lib/config_spec.rb
|
167
|
+
- spec/lib/detector_spec.rb
|
168
|
+
- spec/lib/feature_spec.rb
|
169
|
+
- spec/lib/generator_spec.rb
|
170
|
+
- spec/lib/report_spec.rb
|
171
|
+
- spec/lib/screenshot_spec.rb
|
172
|
+
- spec/sample_screenshots/01_LandscapeLeft_Screenshot.png
|
173
|
+
- spec/sample_screenshots/02_LandscapeRight_Screenshot.png
|
174
|
+
- spec/sample_screenshots/03_PortraitUpsideDown_Screenshot.png
|
175
|
+
- spec/sample_setup/feature_one/feature.zucchini
|
176
|
+
- spec/sample_setup/feature_one/masks/retina_ios5/06_sign up_spinner.png
|
177
|
+
- spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner.png
|
178
|
+
- spec/sample_setup/feature_one/reference/retina_ios5/06_sign up_spinner_error.png
|
179
|
+
- spec/sample_setup/feature_one/run_data/Run 1/06_sign up_spinner.png
|
180
|
+
- spec/sample_setup/feature_three/feature.zucchini
|
181
|
+
- spec/sample_setup/feature_two/feature.zucchini
|
182
|
+
- spec/sample_setup/feature_two/masks/retina_ios5/06_sign up_spinner.png
|
183
|
+
- spec/sample_setup/feature_two/reference/retina_ios5/06_sign up_spinner.png
|
184
|
+
- spec/sample_setup/feature_two/reference/retina_ios5/06_sign up_spinner_error.png
|
185
|
+
- spec/sample_setup/feature_two/run_data/Run 1/06_sign up_spinner.png
|
186
|
+
- spec/sample_setup/support/config.yml
|
187
|
+
- spec/sample_setup/support/lib/helpers.coffee
|
188
|
+
- spec/sample_setup/support/masks/retina_ios5.png
|
189
|
+
- spec/sample_setup/support/screens/splash.coffee
|
190
|
+
- spec/sample_setup_2/support/config.yml
|
191
|
+
- spec/sample_setup_3/support/config.yml
|
192
|
+
- spec/spec_helper.rb
|
data/Gemfile.lock
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
zucchini-ios (0.5.9)
|
5
|
-
clamp
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: http://rubygems.org/
|
9
|
-
specs:
|
10
|
-
clamp (0.5.0)
|
11
|
-
diff-lcs (1.1.3)
|
12
|
-
rake (10.0.2)
|
13
|
-
rspec (2.11.0)
|
14
|
-
rspec-core (~> 2.11.0)
|
15
|
-
rspec-expectations (~> 2.11.0)
|
16
|
-
rspec-mocks (~> 2.11.0)
|
17
|
-
rspec-core (2.11.1)
|
18
|
-
rspec-expectations (2.11.2)
|
19
|
-
diff-lcs (~> 1.1.3)
|
20
|
-
rspec-mocks (2.11.2)
|
21
|
-
watchr (0.7)
|
22
|
-
|
23
|
-
PLATFORMS
|
24
|
-
ruby
|
25
|
-
|
26
|
-
DEPENDENCIES
|
27
|
-
rake
|
28
|
-
rspec
|
29
|
-
watchr
|
30
|
-
zucchini-ios!
|