zucchini-ios 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ before_install:
6
+ - npm install coffee-script
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
+ group :test do
4
+ gem 'rake'
5
+ end
6
+
3
7
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- zucchini-ios (0.5.5)
4
+ zucchini-ios (0.5.6)
5
5
  clamp
6
6
 
7
7
  GEM
@@ -9,6 +9,7 @@ GEM
9
9
  specs:
10
10
  clamp (0.5.0)
11
11
  diff-lcs (1.1.3)
12
+ rake (10.0.2)
12
13
  rspec (2.11.0)
13
14
  rspec-core (~> 2.11.0)
14
15
  rspec-expectations (~> 2.11.0)
@@ -23,6 +24,7 @@ PLATFORMS
23
24
  ruby
24
25
 
25
26
  DEPENDENCIES
27
+ rake
26
28
  rspec
27
29
  watchr
28
30
  zucchini-ios!
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://api.travis-ci.org/zucchini-src/zucchini.png)](http://travis-ci.org/zucchini-src/zucchini)
2
+
1
3
  Pre-requisites
2
4
  --------------
3
5
  1. Mac OS X >= 10.6
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ desc "Run all RSpec tests"
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/lib/report.rb CHANGED
@@ -2,29 +2,40 @@ require 'erb'
2
2
  require 'lib/report/view'
3
3
 
4
4
  class Zucchini::Report
5
-
6
- def self.text(features)
7
- features.map do |f|
5
+
6
+ def initialize(features, ci = false, html_path = '/tmp/zucchini_report.html')
7
+ @features, @ci, @html_path = [features, ci, html_path]
8
+ generate!
9
+ end
10
+
11
+ def text
12
+ @features.map do |f|
8
13
  failed_list = f.stats[:failed].empty? ? "" : "\n\nFailed:\n" + f.stats[:failed].map { |s| " #{s.file_name}: #{s.diff[1]}" }.join
9
14
  summary = f.stats.map { |key, set| "#{set.length.to_s} #{key}" }.join(", ")
10
-
15
+
11
16
  "#{f.name}:\n#{summary}#{failed_list}"
12
17
  end.join("\n\n")
13
18
  end
14
-
15
- def self.html(features, ci, report_html_path = "/tmp/zucchini_report.html" )
16
- template_path = File.expand_path("#{File.dirname(__FILE__)}/report/template.erb")
17
-
18
- report = Zucchini::ReportView.new(features, ci)
19
- html = (ERB.new(File.open(template_path).read)).result(report.get_binding)
20
-
21
- File.open(report_html_path, 'w+') { |f| f.write(html) }
22
- report_html_path
19
+
20
+ def html
21
+ @html ||= begin
22
+ template_path = File.expand_path("#{File.dirname(__FILE__)}/report/template.erb")
23
+
24
+ view = Zucchini::ReportView.new(@features, @ci)
25
+ compiled = (ERB.new(File.open(template_path).read)).result(view.get_binding)
26
+
27
+ File.open(@html_path, 'w+') { |f| f.write(compiled) }
28
+ compiled
29
+ end
23
30
  end
24
-
25
- def self.present(features, ci)
26
- puts self.text(features)
27
- system "open #{self.html(features, ci)}"
31
+
32
+ def generate!
33
+ log text
34
+ html
28
35
  end
29
-
30
- end
36
+
37
+ def open; system "open #{@html_path}"; end
38
+
39
+ def log(buf); puts buf; end
40
+
41
+ end
data/lib/runner.rb CHANGED
@@ -5,6 +5,8 @@ class Zucchini::Runner < Clamp::Command
5
5
 
6
6
  option %W(-c --collect), :flag, "only collect the screenshots from the device"
7
7
  option %W(-p --compare), :flag, "perform screenshots comparison based on the last collection"
8
+ option %W(-s --silent), :flag, "do not open the report"
9
+
8
10
  option "--ci", :flag, "produce a CI version of the report after comparison"
9
11
 
10
12
  def execute
@@ -36,7 +38,11 @@ class Zucchini::Runner < Clamp::Command
36
38
 
37
39
  compare_threads.each { |name, t| t.abort_on_exception = true; t.join }
38
40
 
39
- Zucchini::Report.present(features, ci?) unless (collect? && !compare?)
41
+ unless (collect? && !compare?)
42
+ report = Zucchini::Report.new(features, ci?)
43
+ report.open unless silent?
44
+ end
45
+
40
46
  features.inject(true){ |result, feature| result &= feature.succeeded }
41
47
  end
42
48
 
data/lib/uia/base.coffee CHANGED
@@ -3,17 +3,23 @@ Function::bind = (context) ->
3
3
  fun = this
4
4
  ->
5
5
  fun.apply context, arguments
6
-
6
+
7
7
  String::camelCase = ->
8
8
  @replace /([\-\ ][A-Za-z])/g, ($1) ->
9
9
  $1.toUpperCase().replace /[\-\ ]/g, ""
10
10
 
11
11
  extend = (obj, mixin) ->
12
- obj[name] = method for name, method of mixin
12
+ obj[name] = method for name, method of mixin
13
13
  obj
14
14
 
15
- puts = (text) ->
16
- UIALogger.logMessage text
15
+ puts = (text) -> UIALogger.logMessage text
16
+
17
+ # Instruments 4.5 crash when a JS primitive is thrown
18
+ # http://apple.stackexchange.com/questions/69484/unknown-xcode-instruments-crash
19
+ raise = (message) -> throw new Error(message)
20
+
21
+ # Prevent UIA from auto handling alerts
22
+ UIATarget.onAlert = (alert) -> return true
17
23
 
18
24
  target = UIATarget.localTarget()
19
25
  app = target.frontMostApp()
@@ -29,7 +35,7 @@ UIAElement.prototype.$ = (name) ->
29
35
  elem
30
36
 
31
37
  target.waitForElement = (element) ->
32
- return unless element
38
+ return unless element
33
39
  found = false
34
40
  counter = 0
35
41
  while not found and (counter < 10)
@@ -41,12 +47,14 @@ target.waitForElement = (element) ->
41
47
  @delay 0.5
42
48
  counter++
43
49
 
50
+ isNullElement = (elem) -> elem.toString() == "[object UIAElementNil]"
51
+
44
52
  screensCount = 0
45
53
  target.captureScreenWithName_ = target.captureScreenWithName
46
54
  target.captureScreenWithName = (screenName) ->
47
55
  screensCountText = (if (++screensCount < 10) then "0" + screensCount else screensCount)
48
56
  @captureScreenWithName_ screensCountText + "_" + screenName
49
-
57
+
50
58
  class Zucchini
51
59
  @run: (featureText) ->
52
60
  sections = featureText.trim().split(/\n\s*\n/)
@@ -55,13 +63,13 @@ class Zucchini
55
63
  lines = section.split(/\n/)
56
64
 
57
65
  screenMatch = lines[0].match(/.+ on the "([^"]*)" screen:$/)
58
- throw "Line '#{lines[0]}' doesn't define a screen context" unless screenMatch
66
+ raise "Line '#{lines[0]}' doesn't define a screen context" unless screenMatch
59
67
 
60
68
  screenName = screenMatch[1]
61
69
  try
62
70
  screen = eval("new #{screenName.camelCase()}Screen")
63
- catch error
64
- throw "Screen '#{screenName}' not defined"
71
+ catch e
72
+ raise "Screen '#{screenName}' not defined"
65
73
 
66
74
  for line in lines.slice(1)
67
75
  functionFound = false
@@ -69,5 +77,5 @@ class Zucchini
69
77
  match = line.trim().match(new RegExp(regExpText))
70
78
  if match
71
79
  functionFound = true
72
- func.bind(screen)(match[1])
73
- throw "Action for line '#{line}' not defined" unless functionFound
80
+ func.bind(screen)(match[1],match[2])
81
+ raise "Action for line '#{line}' not defined" unless functionFound
@@ -1,8 +1,8 @@
1
1
  class Screen
2
2
  constructor: (@name) ->
3
3
  if @anchor then target.waitForElement @anchor()
4
-
5
- elements: {}
4
+
5
+ elements: {}
6
6
  actions :
7
7
  'Take a screenshot$' : ->
8
8
  target.captureScreenWithName(@name)
@@ -11,11 +11,48 @@ class Screen
11
11
  target.captureScreenWithName(name)
12
12
 
13
13
  'Tap "([^"]*)"$' : (element) ->
14
- throw "Element '#{element}' not defined for the screen '#{@name}'" unless @elements[element]
14
+ raise "Element '#{element}' not defined for the screen '#{@name}'" unless @elements[element]
15
15
  @elements[element]().tap()
16
16
 
17
+ 'Confirm "([^"]*)"$' : (element) ->
18
+ @actions['Tap "([^"]*)"$'].bind(this)(element)
19
+
17
20
  'Wait for "([^"]*)" second[s]*$' : (seconds) ->
18
21
  target.delay(seconds)
19
22
 
20
- 'Confirm "([^"]*)"$' : (element) ->
21
- @actions['Tap "([^"]*)"$'].bind(this)(element)
23
+ 'Type "([^"]*)" in the "([^"]*)" field$': (text,element) ->
24
+ raise "Element '#{element}' not defined for the screen '#{@name}'" unless @elements[element]
25
+ @elements[element]().tap()
26
+ app.keyboard().typeString text
27
+
28
+ 'Clear the "([^"]*)" field$': (element) ->
29
+ raise "Element '#{element}' not defined for the screen '#{@name}'" unless @elements[element]
30
+ @elements[element]().setValue ""
31
+
32
+ 'Cancel the alert' : ->
33
+ alert = app.alert()
34
+ raise "No alert found to dismiss on screen '#{@name}'" if isNullElement alert
35
+ alert.cancelButton().tap()
36
+
37
+ 'Confirm the alert' : ->
38
+ alert = app.alert()
39
+ raise "No alert found to dismiss on screen '#{@name}'" if isNullElement alert
40
+ alert.defaultButton().tap()
41
+
42
+ 'Select the date "([^"]*)"$' : (dateString) ->
43
+ datePicker = view.pickers()[0]
44
+ raise "No date picker available to enter the date #{dateString}" unless (not isNullElement datePicker) and datePicker.isVisible()
45
+ dateParts = dateString.match(/^(\d{2}) (\D*) (\d{4})$/)
46
+ raise "Date is in the wrong format. Need DD Month YYYY. Got #{dateString}" unless dateParts?
47
+ # Set Day
48
+ view.pickers()[0].wheels()[0].selectValue(dateParts[1])
49
+ # Set Month
50
+ counter = 0
51
+ monthWheel = view.pickers()[0].wheels()[1]
52
+ while monthWheel.value() != dateParts[2] and counter<12
53
+ counter++
54
+ monthWheel.tapWithOptions({tapOffset:{x:0.5, y:0.33}})
55
+ target.delay(0.4)
56
+ raise "Counldn't find the month #{dateParts[2]}" unless counter <12
57
+ # Set Year
58
+ view.pickers()[0].wheels()[2].selectValue(dateParts[3])
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Zucchini
2
- VERSION = "0.5.6"
2
+ VERSION = "0.5.7"
3
3
  end
@@ -8,27 +8,24 @@ describe Zucchini::Report do
8
8
  screenshot.diff = (num > 3) ? [:passed, nil] : [:failed, "120\n"]
9
9
  screenshot
10
10
  end
11
-
11
+
12
12
  feature = Zucchini::Feature.new("/my/sample/feature")
13
13
  feature.device = device
14
14
  feature.stub!(:screenshots).and_return(fake_screenshots)
15
15
  feature
16
16
  end
17
-
18
- describe "text" do
19
- it "should return the correct summary text" do
20
- Zucchini::Report.text([feature]).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"
21
- end
22
- end
23
-
24
- describe "HTML" do
25
- let(:report_html_path) { "/tmp/zucchini_rspec_report.html" }
26
- after { FileUtils.rm(report_html_path) }
27
-
28
- it "should produce a a correct HTML report" do
29
- report = File.open(Zucchini::Report.html([feature], false, report_html_path)).read
30
- report.scan(/<dl class="passed.*screen/).length.should eq 4
31
- report.scan(/<dl class="failed.*screen/).length.should eq 3
32
- 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
33
30
  end
34
- end
31
+ end
@@ -8,17 +8,19 @@ Gem::Specification.new do |s|
8
8
  s.date = Date.today.to_s
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.authors = ["Vasily Mikhaylichenko", "Rajesh Kumar", "Kevin O'Neill"]
11
- s.email = ["vasily@playup.com"]
11
+ s.email = ["vaskas@zucchiniframework.org"]
12
12
  s.homepage = "http://www.zucchiniframework.org"
13
13
  s.summary = %q{Functional testing framework for iOS-powered devices}
14
14
  s.description = %q{Zucchini follows simple walkthrough scenarios for your iOS app, takes screenshots and compares them to the reference ones.}
15
15
 
16
- s.add_runtime_dependency "clamp"
16
+ s.add_runtime_dependency "clamp"
17
17
  s.add_development_dependency "rspec"
18
18
  s.add_development_dependency "watchr"
19
19
 
20
- s.files = `git ls-files | grep -vE '(web|.watchr)'`.split("\n")
21
- s.test_files = `git ls-files -- spec/*`.split("\n")
22
- s.executables = %w(zucchini)
23
- s.require_paths = ["lib"]
20
+ s.files = `git ls-files | grep -vE '(web|.watchr)'`.split("\n")
21
+ s.test_files = `git ls-files -- spec/*`.split("\n")
22
+ s.executables = %w(zucchini)
23
+ s.require_paths = ["lib"]
24
+
25
+ s.required_ruby_version = '>= 1.9.2'
24
26
  end
metadata CHANGED
@@ -1,74 +1,81 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: zucchini-ios
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 5
8
- - 6
9
- version: 0.5.6
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.7
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
- date: 2012-12-03 00:00:00 +11:00
20
- default_executable:
21
- dependencies:
22
- - !ruby/object:Gem::Dependency
14
+ date: 2012-12-17 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
23
17
  name: clamp
24
- prerelease: false
25
- requirement: &id001 !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- segments:
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
- requirement: &id002 !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
- version: "0"
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
- requirement: &id003 !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- segments:
54
- - 0
55
- version: "0"
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
- version_requirements: *id003
58
- description: Zucchini follows simple walkthrough scenarios for your iOS app, takes screenshots and compares them to the reference ones.
59
- email:
60
- - vasily@playup.com
61
- executables:
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:
67
+ - vaskas@zucchiniframework.org
68
+ executables:
62
69
  - zucchini
63
70
  extensions: []
64
-
65
71
  extra_rdoc_files: []
66
-
67
- files:
72
+ files:
68
73
  - .gitignore
74
+ - .travis.yml
69
75
  - Gemfile
70
76
  - Gemfile.lock
71
77
  - README.md
78
+ - Rakefile
72
79
  - bin/zucchini
73
80
  - lib/config.rb
74
81
  - lib/feature.rb
@@ -119,38 +126,32 @@ files:
119
126
  - templates/project/features/support/masks/low_ios4.png
120
127
  - templates/project/features/support/masks/retina_ios5.png
121
128
  - templates/project/features/support/screens/welcome.coffee
122
- - zucchini.gemspec
123
- has_rdoc: true
129
+ - zucchini-ios.gemspec
124
130
  homepage: http://www.zucchiniframework.org
125
131
  licenses: []
126
-
127
132
  post_install_message:
128
133
  rdoc_options: []
129
-
130
- require_paths:
134
+ require_paths:
131
135
  - lib
132
- required_ruby_version: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - ">="
135
- - !ruby/object:Gem::Version
136
- segments:
137
- - 0
138
- version: "0"
139
- required_rubygems_version: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - ">="
142
- - !ruby/object:Gem::Version
143
- segments:
144
- - 0
145
- version: "0"
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: 1.9.2
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
146
148
  requirements: []
147
-
148
149
  rubyforge_project:
149
- rubygems_version: 1.3.6
150
+ rubygems_version: 1.8.24
150
151
  signing_key:
151
152
  specification_version: 3
152
153
  summary: Functional testing framework for iOS-powered devices
153
- test_files:
154
+ test_files:
154
155
  - spec/lib/config_spec.rb
155
156
  - spec/lib/feature_spec.rb
156
157
  - spec/lib/generator_spec.rb