furter 0.0.3

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.
Files changed (82) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +2 -0
  4. data/Changelog +3 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +25 -0
  8. data/Rakefile +30 -0
  9. data/app/FurterApp/Frank/features/my_first.feature +12 -0
  10. data/app/FurterApp/Frank/features/step_definitions/launch_steps.rb +20 -0
  11. data/app/FurterApp/Frank/features/support/env.rb +8 -0
  12. data/app/FurterApp/Frank/frankify.xcconfig +6 -0
  13. data/app/FurterApp/Frank/libCocoaAsyncSocket.a +0 -0
  14. data/app/FurterApp/Frank/libCocoaAsyncSocketMac.a +0 -0
  15. data/app/FurterApp/Frank/libCocoaHTTPServer.a +0 -0
  16. data/app/FurterApp/Frank/libCocoaHTTPServerMac.a +0 -0
  17. data/app/FurterApp/Frank/libCocoaLumberjack.a +0 -0
  18. data/app/FurterApp/Frank/libCocoaLumberjackMac.a +0 -0
  19. data/app/FurterApp/Frank/libFrank.a +0 -0
  20. data/app/FurterApp/Frank/libFrankMac.a +0 -0
  21. data/app/FurterApp/Frank/libShelley.a +0 -0
  22. data/app/FurterApp/Frank/libShelleyMac.a +0 -0
  23. data/app/FurterApp/FurterApp.xcodeproj/project.pbxproj +807 -0
  24. data/app/FurterApp/FurterApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  25. data/app/FurterApp/FurterApp.xcodeproj/project.xcworkspace/xcuserdata/lwilson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  26. data/app/FurterApp/FurterApp.xcodeproj/xcuserdata/lwilson.xcuserdatad/xcschemes/FurterApp.xcscheme +86 -0
  27. data/app/FurterApp/FurterApp.xcodeproj/xcuserdata/lwilson.xcuserdatad/xcschemes/xcschememanagement.plist +22 -0
  28. data/app/FurterApp/FurterApp/AppDelegate.h +22 -0
  29. data/app/FurterApp/FurterApp/AppDelegate.m +161 -0
  30. data/app/FurterApp/FurterApp/Default-568h@2x.png +0 -0
  31. data/app/FurterApp/FurterApp/Default.png +0 -0
  32. data/app/FurterApp/FurterApp/Default@2x.png +0 -0
  33. data/app/FurterApp/FurterApp/DetailViewController.h +23 -0
  34. data/app/FurterApp/FurterApp/DetailViewController.m +80 -0
  35. data/app/FurterApp/FurterApp/FurterApp-Info.plist +59 -0
  36. data/app/FurterApp/FurterApp/FurterApp-Prefix.pch +15 -0
  37. data/app/FurterApp/FurterApp/FurterApp.xcdatamodeld/.xccurrentversion +8 -0
  38. data/app/FurterApp/FurterApp/FurterApp.xcdatamodeld/FurterApp.xcdatamodel/contents +9 -0
  39. data/app/FurterApp/FurterApp/MasterViewController.h +22 -0
  40. data/app/FurterApp/FurterApp/MasterViewController.m +235 -0
  41. data/app/FurterApp/FurterApp/en.lproj/InfoPlist.strings +2 -0
  42. data/app/FurterApp/FurterApp/en.lproj/MainStoryboard_iPad.storyboard +260 -0
  43. data/app/FurterApp/FurterApp/en.lproj/MainStoryboard_iPhone.storyboard +129 -0
  44. data/app/FurterApp/FurterApp/main.m +18 -0
  45. data/cucumber.yml +2 -0
  46. data/features/button.feature +12 -0
  47. data/features/label.feature +10 -0
  48. data/features/navigation.feature +5 -0
  49. data/features/step_definitions/button_steps.rb +7 -0
  50. data/features/step_definitions/label_steps.rb +8 -0
  51. data/features/step_definitions/navigation_steps.rb +7 -0
  52. data/features/step_definitions/switches_steps.rb +12 -0
  53. data/features/step_definitions/text_steps.rb +25 -0
  54. data/features/support/core_ext/string.rb +9 -0
  55. data/features/support/env.rb +25 -0
  56. data/features/support/hooks.rb +3 -0
  57. data/features/support/screens/detail.rb +15 -0
  58. data/features/support/screens/elsewhere_page.rb +7 -0
  59. data/features/switches.feature +7 -0
  60. data/features/text.feature +15 -0
  61. data/furter.gemspec +26 -0
  62. data/lib/furter.rb +53 -0
  63. data/lib/furter/accessors.rb +87 -0
  64. data/lib/furter/accessors/alert_button.rb +18 -0
  65. data/lib/furter/accessors/button.rb +17 -0
  66. data/lib/furter/accessors/label.rb +13 -0
  67. data/lib/furter/accessors/map_pin.rb +18 -0
  68. data/lib/furter/accessors/switch.rb +20 -0
  69. data/lib/furter/accessors/table.rb +15 -0
  70. data/lib/furter/accessors/table_item.rb +10 -0
  71. data/lib/furter/accessors/text.rb +27 -0
  72. data/lib/furter/accessors/view.rb +46 -0
  73. data/lib/furter/navigation.rb +25 -0
  74. data/lib/furter/version.rb +3 -0
  75. data/spec/lib/furter/accessors/button_spec.rb +23 -0
  76. data/spec/lib/furter/accessors/label_spec.rb +24 -0
  77. data/spec/lib/furter/accessors/switch_spec.rb +28 -0
  78. data/spec/lib/furter/accessors/text_spec.rb +39 -0
  79. data/spec/lib/furter/accessors/view_spec.rb +43 -0
  80. data/spec/lib/furter/accessors_spec.rb +116 -0
  81. data/spec/spec_helper.rb +18 -0
  82. 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,17 @@
1
+ module Furter
2
+ module Accessors
3
+ class Button < View
4
+ private
5
+ def view_class
6
+ case
7
+ when @locator[:type]
8
+ "view:'#{@locator[:type]}'"
9
+ when @locator[:text]
10
+ "button label"
11
+ else
12
+ "button"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module Furter
2
+ module Accessors
3
+ class Label < View
4
+ def view_class
5
+ 'label'
6
+ end
7
+
8
+ def text
9
+ frankly_map(selector, 'text')[0]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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,10 @@
1
+ module Furter
2
+ module Accessors
3
+ class TableItem < Button
4
+ private
5
+ def selector
6
+ "label marked:'#{@locator[:text]}' parent tableViewCell"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -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,3 @@
1
+ module Furter
2
+ VERSION = "0.0.3"
3
+ 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