furter 0.0.3 → 0.0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,5 @@
1
1
  language: ruby
2
2
  rvm: 1.9.3
3
+ before_install:
4
+ - sudo apt-get update -qq
5
+ - sudo apt-get install -qq libavahi-compat-libdnssd-dev
data/Changelog CHANGED
@@ -1,3 +1,7 @@
1
+ === Version 0.3.1 / 2013-04-23
2
+ * added a new slider accessor
3
+ * added a views accessor to define which view controllers should be on a screen
4
+
1
5
  === Version 0.3 / 2013-04-03
2
6
  Initial release with support for text, label, button, view, alert_button,
3
7
  table, table_item, map_pin and switch controls
data/README.md CHANGED
@@ -1,20 +1,76 @@
1
1
  # Furter
2
2
 
3
- TODO: Write a gem description
3
+ A gem to assist in building page-object like structures for testing iOS applications. `furter` uses [`frank-cucumber`](http://testingwithfrank.com/) to automate iOS applications using page-objects.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  Add this line to your application's Gemfile:
8
8
 
9
- gem 'furter', :git => 'https://github.com/leviwilson/furter.git'
9
+ source 'https://rubygems.org'
10
+ gem 'furter'
10
11
 
11
12
  And then execute:
12
13
 
13
14
  $ bundle
15
+
16
+ ### Setting Up Cucumber
17
+ After your `Gemfile` has been updated, add these lines to your `features/support/env.rb` file in `cucumber`:
14
18
 
15
- ## Usage
19
+ ```ruby
20
+ require 'furter'
21
+ require 'rspec-expectations'
16
22
 
17
- TODO: Write usage instructions here
23
+ World(Furter::Navigation)
24
+
25
+ APP_BUNDLE_PATH = File.expand_path( '../../../Frank/frankified_build/YourApp.app', __FILE__ )
26
+
27
+ Frank::Cucumber::FrankHelper.use_shelley_from_now_on
28
+
29
+ Before do
30
+ version = '6.1'
31
+ idiom = 'iPhone'
32
+
33
+ launch_app APP_BUNDLE_PATH, version, idiom
34
+ end
35
+ ```
36
+
37
+ ## Defining Screens
38
+ Simply create a new class that describes the screen that you are working with and include `Furter`.
39
+
40
+ ```ruby
41
+ class LoginScreen
42
+ include Furter
43
+
44
+ text(:username, :label => 'usernameField')
45
+ text(:password, :label => 'passwordField')
46
+ button(:login, :text => 'Login')
47
+ end
48
+ ```
49
+
50
+ In your step definition, use the `Furter::Navigation#on` method when using your page-object. This method will create your screen and then wait until all animations have stopped before letting you interact with your page-object.
51
+
52
+ ```ruby
53
+ When ^I login to my application$ do
54
+ on(LoginScreen) do |screen|
55
+ screen.username = 'user@example.com'
56
+ screen.password = '$3cr3t`
57
+ screen.login
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### Waiting for Screens
63
+ If you would like for `furter` to wait until your screen becomes "active" (perhaps after some asynchronous call has come back), simply define an `active?` method and `furter` will wait until this returns `true` before interacting with it.
64
+
65
+ ```ruby
66
+ class LandingScreen
67
+ include Furter
68
+
69
+ def active?
70
+ has_text? 'You have successfully logged in!'
71
+ end
72
+ end
73
+ ```
18
74
 
19
75
  ## Contributing
20
76
 
@@ -15,6 +15,7 @@
15
15
  @property (weak, nonatomic) IBOutlet UIButton *textButton;
16
16
  @property (weak, nonatomic) IBOutlet UIButton *labeledButton;
17
17
  @property (weak, nonatomic) IBOutlet UILabel *labeledLabel;
18
+ @property (weak, nonatomic) IBOutlet UISlider *slider;
18
19
 
19
20
  @property (strong, nonatomic) id detailItem;
20
21
 
@@ -14,6 +14,7 @@
14
14
  @end
15
15
 
16
16
  @implementation DetailViewController
17
+ @synthesize slider;
17
18
 
18
19
  #pragma mark - Managing the detail item
19
20
 
@@ -47,6 +48,7 @@
47
48
  [self configureView];
48
49
  [self.labeledButton setAccessibilityLabel:@"labeledButtonId"];
49
50
  [self.labeledLabel setAccessibilityLabel:@"labelByAccessibilityLabel"];
51
+ [slider setAccessibilityLabel:@"sliderByLabel"];
50
52
  }
51
53
 
52
54
  - (void)didReceiveMemoryWarning
@@ -107,6 +107,12 @@
107
107
  <segue destination="2m6-NE-oGx" kind="push" id="g3T-p0-Sec"/>
108
108
  </connections>
109
109
  </button>
110
+ <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="wB4-9F-vl7">
111
+ <accessibility key="accessibilityConfiguration" label="sliderByLabel"/>
112
+ <constraints>
113
+ <constraint firstAttribute="width" constant="288" id="dCS-tQ-uoe"/>
114
+ </constraints>
115
+ </slider>
110
116
  </subviews>
111
117
  <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
112
118
  <constraints>
@@ -125,6 +131,7 @@
125
131
  <constraint firstItem="MgB-Y1-P3J" firstAttribute="leading" secondItem="HNa-e4-2od" secondAttribute="leading" type="default" id="TI6-aB-T6l"/>
126
132
  <constraint firstItem="6Hu-uu-EUY" firstAttribute="trailing" secondItem="MgB-Y1-P3J" secondAttribute="trailing" type="default" id="Tkv-gE-Bb0"/>
127
133
  <constraint firstItem="79f-eU-bDt" firstAttribute="top" secondItem="26" secondAttribute="top" constant="23" id="WAb-82-oiE"/>
134
+ <constraint firstAttribute="bottom" secondItem="wB4-9F-vl7" secondAttribute="bottom" constant="250" id="bJ6-kP-IlL"/>
128
135
  <constraint firstItem="HNa-e4-2od" firstAttribute="trailing" secondItem="mEd-bW-xG3" secondAttribute="trailing" type="default" id="e4T-wS-zIa"/>
129
136
  <constraint firstItem="27" firstAttribute="centerY" secondItem="26" secondAttribute="centerY" type="default" id="fbb-bQ-YI1"/>
130
137
  <constraint firstItem="MgB-Y1-P3J" firstAttribute="top" secondItem="26" secondAttribute="top" constant="120" id="fxO-Da-wfr"/>
@@ -133,6 +140,7 @@
133
140
  <constraint firstItem="n4M-Kb-ePv" firstAttribute="trailing" secondItem="6Hu-uu-EUY" secondAttribute="trailing" type="default" id="kXJ-R6-5W3"/>
134
141
  <constraint firstItem="79f-eU-bDt" firstAttribute="baseline" secondItem="mEd-bW-xG3" secondAttribute="baseline" type="default" id="piK-8v-q9x"/>
135
142
  <constraint firstItem="Nyi-ck-8hR" firstAttribute="top" secondItem="nbu-fr-Znd" secondAttribute="top" type="default" id="wtS-Ul-nbp"/>
143
+ <constraint firstItem="wB4-9F-vl7" firstAttribute="leading" secondItem="26" secondAttribute="leading" constant="206" id="zbX-ES-KTX"/>
136
144
  </constraints>
137
145
  <simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
138
146
  </view>
@@ -144,6 +152,7 @@
144
152
  <outlet property="labeledButton" destination="6Hu-uu-EUY" id="lP4-Wp-TLL"/>
145
153
  <outlet property="labeledLabel" destination="27" id="GUQ-6K-Ygb"/>
146
154
  <outlet property="placeholderTextField" destination="mEd-bW-xG3" id="WJN-WF-CeJ"/>
155
+ <outlet property="slider" destination="wB4-9F-vl7" id="tyV-Zf-nQd"/>
147
156
  <outlet property="textButton" destination="MgB-Y1-P3J" id="dgB-Uo-P2L"/>
148
157
  </connections>
149
158
  </viewController>
@@ -0,0 +1,9 @@
1
+ Feature: Working with sliders
2
+
3
+ Scenario: Interacting with the slider
4
+ Then we can interact with the raw view of the slider identified by "label"
5
+
6
+ Scenario: Getting and Setting value of the slider
7
+ Given we can interact with the raw view of the slider identified by "label"
8
+ When I set the progress of the slider identified by "label" to "0.2"
9
+ Then I can see the progress of the slider identified by "label" is "0.2"
@@ -4,4 +4,4 @@ end
4
4
 
5
5
  Then /^I should end up on the "([^"]*)"$/ do |where|
6
6
  on(where.to_class).should be_active
7
- end
7
+ end
@@ -0,0 +1,11 @@
1
+ Then /^we can interact with the raw view of the slider identified by "([^"]*)"$/ do |how|
2
+ on(DetailPage).send("slider_#{how}_view").should be_kind_of(Furter::Accessors::View)
3
+ end
4
+
5
+ When /^I set the progress of the slider identified by "([^"]*)" to "([^"]*)"$/ do |how, value|
6
+ on(DetailPage).send("slider_#{how}_move", value.to_f)
7
+ end
8
+
9
+ Then /^I can see the progress of the slider identified by "([^"]*)" is "([^"]*)"$/ do |how, value|
10
+ on(DetailPage).send("slider_#{how}_progress").should eq(value.to_f)
11
+ end
@@ -12,4 +12,8 @@ class DetailPage
12
12
  button(:go_elsewhere, :text => 'Go Elsewhere')
13
13
 
14
14
  switch(:switch_toggle, :label => 'switchToggle')
15
+
16
+ slider(:slider_label, :label=> 'sliderByLabel')
17
+
18
+ views('DetailViewController')
15
19
  end
@@ -10,6 +10,7 @@ require 'furter/accessors/table'
10
10
  require 'furter/accessors/table_item'
11
11
  require 'furter/accessors/text'
12
12
  require 'furter/accessors/label'
13
+ require 'furter/accessors/slider'
13
14
 
14
15
  module Furter
15
16
  include Frank::Cucumber::FrankHelper
@@ -46,8 +47,7 @@ module Furter
46
47
  touch locator
47
48
  end
48
49
  end
49
-
50
- def active?
51
- true
52
- end
53
50
  end
51
+
52
+
53
+
@@ -83,5 +83,23 @@ module Furter
83
83
  Furter::Accessors::Switch.new(locator)
84
84
  end
85
85
  end
86
+
87
+ def slider(name, locator)
88
+ define_method("#{name}_view") do
89
+ Furter::Accessors::Slider.new(locator)
90
+ end
91
+ define_method("#{name}_move") do |value|
92
+ Furter::Accessors::Slider.new(locator).move value
93
+ end
94
+ define_method("#{name}_progress") do
95
+ Furter::Accessors::Slider.new(locator).progress
96
+ end
97
+ end
98
+
99
+ def views(*view_classes)
100
+ define_method(:active?) do
101
+ (view_classes & Furter::Accessors::View.new.next_responders) == view_classes
102
+ end
103
+ end
86
104
  end
87
105
  end
@@ -0,0 +1,20 @@
1
+ module Furter
2
+ module Accessors
3
+ class Slider < View
4
+
5
+ def move(value)
6
+ frankly_map(selector, 'setValue:animated:', value, 'YES')
7
+ end
8
+
9
+ def progress
10
+ frankly_map(selector, 'value')[0]
11
+ end
12
+
13
+ def view_class
14
+ return "view:'#{@locator[:type]}'" if @locator[:type]
15
+ "view:'UISlider'"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -19,6 +19,12 @@ module Furter
19
19
  frankly_map(selector, 'isEnabled')[0]
20
20
  end
21
21
 
22
+ def next_responders
23
+ frankly_map("view:'UIView'", 'nextResponder').map do |r|
24
+ r.gsub(/[<>]/,'')
25
+ end
26
+ end
27
+
22
28
  private
23
29
  def selector
24
30
  "#{view_class} #{selector_how}#{selector_extra}"
@@ -16,7 +16,7 @@ module Furter
16
16
  wait_a_bit_for_animations
17
17
  wait_until(:message => "Expected #{cls} to be active", :timeout => 30) do
18
18
  screen.active?
19
- end
19
+ end if screen.respond_to?(:active?)
20
20
  wait_a_bit_for_animations
21
21
  block.call screen if block
22
22
  screen
@@ -1,3 +1,3 @@
1
1
  module Furter
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.3.1"
3
3
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Furter::Accessors::Slider do
4
+ let(:slider) { Furter::Accessors::Slider.new(:label => 'id') }
5
+ let(:selector) { slider.send(:selector) }
6
+
7
+ context 'locating slider' do
8
+ it 'can be found by id' do
9
+ id_slider = Furter::Accessors::Slider.new(:label => 'someSliderLabel')
10
+ id_slider.send(:selector).should eq("view:'UISlider' marked:'someSliderLabel'")
11
+ end
12
+ end
13
+
14
+ context 'moving slider' do
15
+ it "can move the slider value" do
16
+ slider.should_receive(:frankly_map).with(selector,"setValue:animated:", 0.20, "YES")
17
+ slider.move 0.20
18
+ end
19
+ it "can get progress value of slider" do
20
+ slider.should_receive(:frankly_map).with(selector, 'value').and_return([0.20])
21
+ slider.progress.should eq(0.20)
22
+ end
23
+ end
24
+ end
@@ -40,4 +40,9 @@ describe Furter::Accessors::View do
40
40
  view.should_receive(:frankly_map).with(selector, 'isEnabled').and_return([true])
41
41
  view.should be_enabled
42
42
  end
43
- end
43
+
44
+ it 'should know about next responders' do
45
+ view.should_receive(:frankly_map).with("view:'UIView'", 'nextResponder').and_return(['<First>', '<Second>', '<Third>'])
46
+ view.next_responders.should eq ['First', 'Second', 'Third']
47
+ end
48
+ end
@@ -8,6 +8,7 @@ class SomePage
8
8
  view(:generic_view, :label => 'id')
9
9
  button(:button_field, :label => 'id')
10
10
  switch(:switch_field, :label => 'id')
11
+ views('First', 'Fifth', 'Seventh')
11
12
  end
12
13
 
13
14
  describe Furter::Accessors do
@@ -113,4 +114,27 @@ describe Furter::Accessors do
113
114
  screen.switch_field_view.should be(switch)
114
115
  end
115
116
  end
117
+
118
+ context 'views' do
119
+ let(:view) { double('View') }
120
+
121
+ before(:each) do
122
+ Furter::Accessors::View.should_receive(:new).and_return(view)
123
+ end
124
+
125
+ it 'is active if it has all of the views' do
126
+ view.should_receive(:next_responders).and_return(['First', 'Fifth', 'Seventh'])
127
+ screen.should be_active
128
+ end
129
+
130
+ it 'does not care about the order' do
131
+ view.should_receive(:next_responders).and_return(['Seventh', 'First', 'Fifth'])
132
+ screen.should be_active
133
+ end
134
+
135
+ it 'is inactive if not all views are represented' do
136
+ view.should_receive(:next_responders).and_return(['First', 'Fifth', 'Not The Seventh'])
137
+ screen.should_not be_active
138
+ end
139
+ end
116
140
  end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ class NavigationScreen; end
4
+
5
+ RSpec.configure do |c|
6
+ c.include Furter::Navigation, :navigation => true
7
+ end
8
+
9
+ describe Furter::Navigation, :navigation => true do
10
+ let(:screen) { double('navigation screen') }
11
+
12
+ before(:each) do
13
+ NavigationScreen.should_receive(:new).and_return(screen)
14
+ end
15
+
16
+ it 'returns the screen that was asked for' do
17
+ on(NavigationScreen).should be(screen)
18
+ end
19
+
20
+ it 'cares about active? if you do' do
21
+ screen.should_receive(:active?).and_return(true)
22
+ on(NavigationScreen)
23
+ end
24
+
25
+ it 'gives you your object when it is ready' do
26
+ screen.should_receive(:call_me)
27
+ on(NavigationScreen) do |s|
28
+ s.call_me
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: furter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-03 00:00:00.000000000 Z
12
+ date: 2013-04-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: frank-cucumber
@@ -146,9 +146,11 @@ files:
146
146
  - features/button.feature
147
147
  - features/label.feature
148
148
  - features/navigation.feature
149
+ - features/slider.feature
149
150
  - features/step_definitions/button_steps.rb
150
151
  - features/step_definitions/label_steps.rb
151
152
  - features/step_definitions/navigation_steps.rb
153
+ - features/step_definitions/slider_steps.rb
152
154
  - features/step_definitions/switches_steps.rb
153
155
  - features/step_definitions/text_steps.rb
154
156
  - features/support/core_ext/string.rb
@@ -165,6 +167,7 @@ files:
165
167
  - lib/furter/accessors/button.rb
166
168
  - lib/furter/accessors/label.rb
167
169
  - lib/furter/accessors/map_pin.rb
170
+ - lib/furter/accessors/slider.rb
168
171
  - lib/furter/accessors/switch.rb
169
172
  - lib/furter/accessors/table.rb
170
173
  - lib/furter/accessors/table_item.rb
@@ -174,10 +177,12 @@ files:
174
177
  - lib/furter/version.rb
175
178
  - spec/lib/furter/accessors/button_spec.rb
176
179
  - spec/lib/furter/accessors/label_spec.rb
180
+ - spec/lib/furter/accessors/slider_spec.rb
177
181
  - spec/lib/furter/accessors/switch_spec.rb
178
182
  - spec/lib/furter/accessors/text_spec.rb
179
183
  - spec/lib/furter/accessors/view_spec.rb
180
184
  - spec/lib/furter/accessors_spec.rb
185
+ - spec/lib/furter/navigation_spec.rb
181
186
  - spec/spec_helper.rb
182
187
  homepage: ''
183
188
  licenses: []
@@ -193,7 +198,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
198
  version: '0'
194
199
  segments:
195
200
  - 0
196
- hash: -3139935095822533556
201
+ hash: -3754935544043957014
197
202
  required_rubygems_version: !ruby/object:Gem::Requirement
198
203
  none: false
199
204
  requirements:
@@ -202,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
207
  version: '0'
203
208
  segments:
204
209
  - 0
205
- hash: -3139935095822533556
210
+ hash: -3754935544043957014
206
211
  requirements: []
207
212
  rubyforge_project:
208
213
  rubygems_version: 1.8.25
@@ -213,9 +218,11 @@ test_files:
213
218
  - features/button.feature
214
219
  - features/label.feature
215
220
  - features/navigation.feature
221
+ - features/slider.feature
216
222
  - features/step_definitions/button_steps.rb
217
223
  - features/step_definitions/label_steps.rb
218
224
  - features/step_definitions/navigation_steps.rb
225
+ - features/step_definitions/slider_steps.rb
219
226
  - features/step_definitions/switches_steps.rb
220
227
  - features/step_definitions/text_steps.rb
221
228
  - features/support/core_ext/string.rb
@@ -227,8 +234,10 @@ test_files:
227
234
  - features/text.feature
228
235
  - spec/lib/furter/accessors/button_spec.rb
229
236
  - spec/lib/furter/accessors/label_spec.rb
237
+ - spec/lib/furter/accessors/slider_spec.rb
230
238
  - spec/lib/furter/accessors/switch_spec.rb
231
239
  - spec/lib/furter/accessors/text_spec.rb
232
240
  - spec/lib/furter/accessors/view_spec.rb
233
241
  - spec/lib/furter/accessors_spec.rb
242
+ - spec/lib/furter/navigation_spec.rb
234
243
  - spec/spec_helper.rb