furter 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.travis.yml +2 -0
- data/Changelog +3 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +25 -0
- data/Rakefile +30 -0
- data/app/FurterApp/Frank/features/my_first.feature +12 -0
- data/app/FurterApp/Frank/features/step_definitions/launch_steps.rb +20 -0
- data/app/FurterApp/Frank/features/support/env.rb +8 -0
- data/app/FurterApp/Frank/frankify.xcconfig +6 -0
- data/app/FurterApp/Frank/libCocoaAsyncSocket.a +0 -0
- data/app/FurterApp/Frank/libCocoaAsyncSocketMac.a +0 -0
- data/app/FurterApp/Frank/libCocoaHTTPServer.a +0 -0
- data/app/FurterApp/Frank/libCocoaHTTPServerMac.a +0 -0
- data/app/FurterApp/Frank/libCocoaLumberjack.a +0 -0
- data/app/FurterApp/Frank/libCocoaLumberjackMac.a +0 -0
- data/app/FurterApp/Frank/libFrank.a +0 -0
- data/app/FurterApp/Frank/libFrankMac.a +0 -0
- data/app/FurterApp/Frank/libShelley.a +0 -0
- data/app/FurterApp/Frank/libShelleyMac.a +0 -0
- data/app/FurterApp/FurterApp.xcodeproj/project.pbxproj +807 -0
- data/app/FurterApp/FurterApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- data/app/FurterApp/FurterApp.xcodeproj/project.xcworkspace/xcuserdata/lwilson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/app/FurterApp/FurterApp.xcodeproj/xcuserdata/lwilson.xcuserdatad/xcschemes/FurterApp.xcscheme +86 -0
- data/app/FurterApp/FurterApp.xcodeproj/xcuserdata/lwilson.xcuserdatad/xcschemes/xcschememanagement.plist +22 -0
- data/app/FurterApp/FurterApp/AppDelegate.h +22 -0
- data/app/FurterApp/FurterApp/AppDelegate.m +161 -0
- data/app/FurterApp/FurterApp/Default-568h@2x.png +0 -0
- data/app/FurterApp/FurterApp/Default.png +0 -0
- data/app/FurterApp/FurterApp/Default@2x.png +0 -0
- data/app/FurterApp/FurterApp/DetailViewController.h +23 -0
- data/app/FurterApp/FurterApp/DetailViewController.m +80 -0
- data/app/FurterApp/FurterApp/FurterApp-Info.plist +59 -0
- data/app/FurterApp/FurterApp/FurterApp-Prefix.pch +15 -0
- data/app/FurterApp/FurterApp/FurterApp.xcdatamodeld/.xccurrentversion +8 -0
- data/app/FurterApp/FurterApp/FurterApp.xcdatamodeld/FurterApp.xcdatamodel/contents +9 -0
- data/app/FurterApp/FurterApp/MasterViewController.h +22 -0
- data/app/FurterApp/FurterApp/MasterViewController.m +235 -0
- data/app/FurterApp/FurterApp/en.lproj/InfoPlist.strings +2 -0
- data/app/FurterApp/FurterApp/en.lproj/MainStoryboard_iPad.storyboard +260 -0
- data/app/FurterApp/FurterApp/en.lproj/MainStoryboard_iPhone.storyboard +129 -0
- data/app/FurterApp/FurterApp/main.m +18 -0
- data/cucumber.yml +2 -0
- data/features/button.feature +12 -0
- data/features/label.feature +10 -0
- data/features/navigation.feature +5 -0
- data/features/step_definitions/button_steps.rb +7 -0
- data/features/step_definitions/label_steps.rb +8 -0
- data/features/step_definitions/navigation_steps.rb +7 -0
- data/features/step_definitions/switches_steps.rb +12 -0
- data/features/step_definitions/text_steps.rb +25 -0
- data/features/support/core_ext/string.rb +9 -0
- data/features/support/env.rb +25 -0
- data/features/support/hooks.rb +3 -0
- data/features/support/screens/detail.rb +15 -0
- data/features/support/screens/elsewhere_page.rb +7 -0
- data/features/switches.feature +7 -0
- data/features/text.feature +15 -0
- data/furter.gemspec +26 -0
- data/lib/furter.rb +53 -0
- data/lib/furter/accessors.rb +87 -0
- data/lib/furter/accessors/alert_button.rb +18 -0
- data/lib/furter/accessors/button.rb +17 -0
- data/lib/furter/accessors/label.rb +13 -0
- data/lib/furter/accessors/map_pin.rb +18 -0
- data/lib/furter/accessors/switch.rb +20 -0
- data/lib/furter/accessors/table.rb +15 -0
- data/lib/furter/accessors/table_item.rb +10 -0
- data/lib/furter/accessors/text.rb +27 -0
- data/lib/furter/accessors/view.rb +46 -0
- data/lib/furter/navigation.rb +25 -0
- data/lib/furter/version.rb +3 -0
- data/spec/lib/furter/accessors/button_spec.rb +23 -0
- data/spec/lib/furter/accessors/label_spec.rb +24 -0
- data/spec/lib/furter/accessors/switch_spec.rb +28 -0
- data/spec/lib/furter/accessors/text_spec.rb +39 -0
- data/spec/lib/furter/accessors/view_spec.rb +43 -0
- data/spec/lib/furter/accessors_spec.rb +116 -0
- data/spec/spec_helper.rb +18 -0
- metadata +234 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Furter
|
2
|
+
module Accessors
|
3
|
+
class AlertButton < View
|
4
|
+
include Frank::Cucumber::FrankHelper
|
5
|
+
|
6
|
+
def click
|
7
|
+
wait_for_nothing_to_be_animating
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def selector
|
13
|
+
"view:'UIAlertView' view:'UIAlertButton' marked:'#{@locator[:text]}'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Furter
|
2
|
+
module Accessors
|
3
|
+
class MapPin < View
|
4
|
+
include Frank::Cucumber::FrankHelper
|
5
|
+
|
6
|
+
def click
|
7
|
+
wait_for_nothing_to_be_animating
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def selector
|
13
|
+
"view:'MKPinAnnotationView' marked:'#{@locator[:text]}'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Furter
|
2
|
+
module Accessors
|
3
|
+
class Switch < View
|
4
|
+
include Furter
|
5
|
+
|
6
|
+
def on?
|
7
|
+
frankly_map(selector, 'isOn')[0]
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(on_or_off)
|
11
|
+
click if on_or_off != on?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def view_class
|
16
|
+
"view:'UISwitch'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Furter
|
2
|
+
module Accessors
|
3
|
+
class Table < View
|
4
|
+
def options
|
5
|
+
frankly_map("#{selector} label", "text").compact.map(&:strip).reject(&:empty?)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def selector
|
10
|
+
"view:'UITableView' marked:'#{@locator[:label]}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Furter
|
2
|
+
module Accessors
|
3
|
+
class Text < View
|
4
|
+
alias_method :editable?, :enabled?
|
5
|
+
|
6
|
+
def get_text
|
7
|
+
frankly_map(selector, "text")[0]
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_text(text)
|
11
|
+
frankly_map(selector, "becomeFirstResponder")
|
12
|
+
frankly_map(selector, "setText:", text)
|
13
|
+
frankly_map(selector, "endEditing:", true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_text?(text)
|
17
|
+
element_exists "view marked:'#{text}'"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def selector
|
22
|
+
return "textField placeholder:'#{@locator[:placeholder]}'" if @locator[:placeholder]
|
23
|
+
return "textField marked:'#{@locator[:label]}'" if @locator[:label]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Furter
|
2
|
+
module Accessors
|
3
|
+
class View
|
4
|
+
include Furter, Frank::Cucumber::FrankHelper, Frank::Cucumber::WaitHelper
|
5
|
+
|
6
|
+
def initialize(locator={})
|
7
|
+
@locator = locator
|
8
|
+
end
|
9
|
+
|
10
|
+
def click
|
11
|
+
wait_for_and_touch selector
|
12
|
+
end
|
13
|
+
|
14
|
+
def visible?
|
15
|
+
element_is_not_hidden selector
|
16
|
+
end
|
17
|
+
|
18
|
+
def enabled?
|
19
|
+
frankly_map(selector, 'isEnabled')[0]
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def selector
|
24
|
+
"#{view_class} #{selector_how}#{selector_extra}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def selector_how
|
28
|
+
case
|
29
|
+
when @locator[:label]
|
30
|
+
"marked:'#{@locator[:label]}'"
|
31
|
+
when @locator[:text]
|
32
|
+
"text:'#{@locator[:text]}'"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def selector_extra
|
37
|
+
" #{@locator[:extra]}" if @locator[:extra]
|
38
|
+
end
|
39
|
+
|
40
|
+
def view_class
|
41
|
+
return "view:'#{@locator[:type]}'" if @locator[:type]
|
42
|
+
"view"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'page_navigation'
|
2
|
+
|
3
|
+
module Furter
|
4
|
+
module Navigation
|
5
|
+
include PageNavigation, Frank::Cucumber::FrankHelper
|
6
|
+
|
7
|
+
def wait_a_bit_for_animations
|
8
|
+
begin
|
9
|
+
wait_for_nothing_to_be_animating 3
|
10
|
+
rescue
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def on(cls, &block)
|
15
|
+
screen = cls.new
|
16
|
+
wait_a_bit_for_animations
|
17
|
+
wait_until(:message => "Expected #{cls} to be active", :timeout => 30) do
|
18
|
+
screen.active?
|
19
|
+
end
|
20
|
+
wait_a_bit_for_animations
|
21
|
+
block.call screen if block
|
22
|
+
screen
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Furter::Accessors::Button do
|
4
|
+
let(:button) { Furter::Accessors::Button.new(:text => 'Some Text') }
|
5
|
+
let(:selector) { button.send(:selector) }
|
6
|
+
|
7
|
+
context 'locating buttons' do
|
8
|
+
it 'can be found by text' do
|
9
|
+
text_button = Furter::Accessors::Button.new(:text => 'Some Text')
|
10
|
+
text_button.send(:selector).should eq("button label text:'Some Text'")
|
11
|
+
end
|
12
|
+
|
13
|
+
it ':text is the same as :label' do
|
14
|
+
label_button = Furter::Accessors::Button.new(:label => 'Some Text')
|
15
|
+
label_button.send(:selector).should eq("button marked:'Some Text'")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can be a custom button type as well' do
|
19
|
+
custom_button = Furter::Accessors::Button.new(:label => 'Some Label', :type => 'MyButtonType')
|
20
|
+
custom_button.send(:selector).should eq("view:'MyButtonType' marked:'Some Label'")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Furter::Accessors::Label do
|
4
|
+
let(:label) { Furter::Accessors::Label.new(:label => 'someLabel')}
|
5
|
+
let(:selector) { label.send(:selector) }
|
6
|
+
|
7
|
+
context 'locating labels' do
|
8
|
+
it 'can be found by accessibility label' do
|
9
|
+
label_control = Furter::Accessors::Label.new(:label => 'someAccessibilityLabel')
|
10
|
+
label_control.send(:selector).should eq("label marked:'someAccessibilityLabel'")
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can be found by text' do
|
14
|
+
label_control = Furter::Accessors::Label.new(:text => 'Text Value')
|
15
|
+
label_control.send(:selector).should eq("label text:'Text Value'")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'knows the text value' do
|
20
|
+
label.should_receive(:frankly_map).with(selector, 'text').and_return(['expected text'])
|
21
|
+
label.text.should eq('expected text')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Furter::Accessors::Switch do
|
4
|
+
let(:switch) { Furter::Accessors::Switch.new(:label => 'accessibilityLabel') }
|
5
|
+
|
6
|
+
context 'locating switches' do
|
7
|
+
it 'can be found by label' do
|
8
|
+
switch.send(:selector).should eq("view:'UISwitch' marked:'accessibilityLabel'")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'knows if it is on' do
|
13
|
+
switch.should_receive(:frankly_map).with(anything, 'isOn').and_return([false])
|
14
|
+
switch.should_not be_on
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can turn it off or on' do
|
18
|
+
switch.should_receive(:on?).and_return(true, false)
|
19
|
+
switch.should_receive(:click).twice
|
20
|
+
[false, true].each { |on_or_off| switch.set on_or_off }
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'will only toggle if in the expected state' do
|
24
|
+
switch.should_receive(:frankly_map).with(anything, 'isOn').and_return([false])
|
25
|
+
switch.should_not_receive(:click)
|
26
|
+
switch.set false
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Furter::Accessors::Text do
|
4
|
+
let(:placeholder) { Furter::Accessors::Text.new(:placeholder => 'Placeholder Text') }
|
5
|
+
let(:label) { Furter::Accessors::Text.new(:label => 'accessibilityLabel') }
|
6
|
+
|
7
|
+
context 'locating text fields' do
|
8
|
+
it 'should work by placeholder' do
|
9
|
+
placeholder.send(:selector).should eq("textField placeholder:'Placeholder Text'")
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should work by accessibility label' do
|
13
|
+
label.send(:selector).should eq("textField marked:'accessibilityLabel'")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can get text' do
|
18
|
+
placeholder.should_receive(:frankly_map).with(anything, 'text').and_return(['expected text'])
|
19
|
+
placeholder.get_text.should eq('expected text')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can set text' do
|
23
|
+
placeholder.should_receive(:frankly_map).with(anything, 'becomeFirstResponder').ordered
|
24
|
+
placeholder.should_receive(:frankly_map).with(anything, 'setText:', 'the new text').ordered
|
25
|
+
placeholder.should_receive(:frankly_map).with(anything, 'endEditing:', true).ordered
|
26
|
+
|
27
|
+
placeholder.set_text 'the new text'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'knows if text exists anywhere' do
|
31
|
+
placeholder.should_receive(:element_exists).with("view marked:'to find anywhere'")
|
32
|
+
placeholder.has_text? 'to find anywhere'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'knows whether we are editable or not' do
|
36
|
+
placeholder.should_receive(:frankly_map).with(anything, 'isEnabled').and_return([true])
|
37
|
+
placeholder.should be_enabled
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Furter::Accessors::View do
|
4
|
+
let(:view) { Furter::Accessors::View.new(:label => 'id') }
|
5
|
+
let(:selector) { view.send(:selector) }
|
6
|
+
|
7
|
+
context 'locating views' do
|
8
|
+
it 'can be found by accessibility label' do
|
9
|
+
by_label = Furter::Accessors::View.new(:label => 'id')
|
10
|
+
by_label.send(:selector).should eq("view marked:'id'")
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can use a custom type' do
|
14
|
+
by_type = Furter::Accessors::View.new(:type => 'UICustomType', :label => 'id')
|
15
|
+
by_type.send(:selector).should eq("view:'UICustomType' marked:'id'")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can pass extra information as well' do
|
19
|
+
with_extras = Furter::Accessors::View.new(:label => 'id', :extra => 'label')
|
20
|
+
with_extras.send(:selector).should eq("view marked:'id' label")
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can be found by text' do
|
24
|
+
by_text = Furter::Accessors::View.new(:text => "Some Text")
|
25
|
+
by_text.send(:selector).should eq("view text:'Some Text'")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'can be clicked' do
|
30
|
+
view.should_receive(:wait_for_and_touch).with(selector)
|
31
|
+
view.click
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'knows if it is visible' do
|
35
|
+
view.should_receive(:element_is_not_hidden).with(selector).and_return(true)
|
36
|
+
view.should be_visible
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'knows if it is enabled' do
|
40
|
+
view.should_receive(:frankly_map).with(selector, 'isEnabled').and_return([true])
|
41
|
+
view.should be_enabled
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class SomePage
|
4
|
+
include Furter
|
5
|
+
|
6
|
+
text(:text_field, :placeholder => 'text')
|
7
|
+
label(:label_field, :label => 'id')
|
8
|
+
view(:generic_view, :label => 'id')
|
9
|
+
button(:button_field, :label => 'id')
|
10
|
+
switch(:switch_field, :label => 'id')
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Furter::Accessors do
|
14
|
+
let(:screen) { SomePage.new }
|
15
|
+
|
16
|
+
context 'text' do
|
17
|
+
let(:text) { double('Text accessor') }
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
Furter::Accessors::Text.should_receive(:new).with(:placeholder => 'text').and_return(text)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can be returned' do
|
24
|
+
text.should_receive(:get_text)
|
25
|
+
screen.text_field
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can be set' do
|
29
|
+
text.should_receive(:set_text).with('new text')
|
30
|
+
screen.text_field = 'new text'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has editability' do
|
34
|
+
text.should_receive(:editable?).and_return(false)
|
35
|
+
screen.should_not be_text_field_editable
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can dish out a view' do
|
39
|
+
screen.text_field_view.should be(text)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'label' do
|
44
|
+
let(:label) { double('Label control') }
|
45
|
+
|
46
|
+
before(:each) do
|
47
|
+
Furter::Accessors::Label.should_receive(:new).with(:label => 'id').and_return(label)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'knows its value' do
|
51
|
+
label.should_receive(:text).and_return('expected text')
|
52
|
+
screen.label_field.should eq('expected text')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'can dish out a view' do
|
56
|
+
screen.label_field_view.should be(label)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'view' do
|
62
|
+
let(:view) { double('View accessor') }
|
63
|
+
|
64
|
+
before(:each) do
|
65
|
+
Furter::Accessors::View.should_receive(:new).with(:label => 'id').and_return(view)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'can be clicked' do
|
69
|
+
view.should_receive(:click)
|
70
|
+
screen.generic_view
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'can return the raw view' do
|
74
|
+
screen.generic_view_view.should be(view)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'button' do
|
79
|
+
let(:button) { double('Button accessor') }
|
80
|
+
|
81
|
+
before(:each) do
|
82
|
+
Furter::Accessors::Button.should_receive(:new).with(:label => 'id').and_return(button)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'can be clicked' do
|
86
|
+
button.should_receive(:click)
|
87
|
+
screen.button_field
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'can return the raw view' do
|
91
|
+
screen.button_field_view.should be(button)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'switch' do
|
96
|
+
let(:switch) { double('Switch accessor') }
|
97
|
+
|
98
|
+
before(:each) do
|
99
|
+
Furter::Accessors::Switch.should_receive(:new).with(:label => 'id').and_return(switch)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'is aware of its state' do
|
103
|
+
switch.should_receive(:on?).and_return(true)
|
104
|
+
screen.switch_field.should be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'can be turned on or off' do
|
108
|
+
switch.should_receive(:set).with(false)
|
109
|
+
screen.switch_field = false
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'can return the raw view' do
|
113
|
+
screen.switch_field_view.should be(switch)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|