zucchini-ios 0.5.6 → 0.5.7

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/.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