These are the specs for the RubyMotion MacBacon UI layer. They will be in the main repo in the near future.
Created
July 5, 2012 16:01
-
-
Save alloy/3054536 to your computer and use it in GitHub Desktop.
RubyMotion MacBacon UI layer spec.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe "Bacon::Functional::API, concerning location helpers" do | |
extend Bacon::Functional::API | |
def inset | |
Bacon::Functional::API::LOCATION_TO_POINT_INSET | |
end | |
it "returns the points corresponding to the view" do | |
view = UIView.alloc.initWithFrame(CGRectMake(100, 100, 100, 100)) | |
_location_to_point(view, :top_left).should == CGPointMake(100+inset, 100+inset) | |
_location_to_point(view, :top).should == CGPointMake(150, 100+inset) | |
_location_to_point(view, :top_right).should == CGPointMake(200-inset, 100+inset) | |
_location_to_point(view, :right).should == CGPointMake(200-inset, 150) | |
_location_to_point(view, :bottom_right).should == CGPointMake(200-inset, 200-inset) | |
_location_to_point(view, :bottom).should == CGPointMake(150, 200-inset) | |
_location_to_point(view, :bottom_left).should == CGPointMake(100+inset, 200-inset) | |
_location_to_point(view, :left).should == CGPointMake(100+inset, 150) | |
end | |
it "returns the opposite of a location" do | |
_location_opposite(:top_left).should == :bottom_right | |
_location_opposite(:top).should == :bottom | |
_location_opposite(:top_right).should == :bottom_left | |
_location_opposite(:right).should == :left | |
_location_opposite(:bottom_right).should == :top_left | |
_location_opposite(:bottom).should == :top | |
_location_opposite(:bottom_left).should == :top_right | |
_location_opposite(:left).should == :right | |
end | |
end | |
class ContainerView < UIView | |
end | |
class SimpleViewController < UIViewController | |
attr_reader :purpleView, :blueView, :redView | |
def loadView | |
frame = UIScreen.mainScreen.applicationFrame | |
frame.origin = CGPointZero | |
self.view = ContainerView.alloc.initWithFrame(frame) | |
view.accessibilityLabel = 'Container view' | |
@purpleView = UIView.alloc.initWithFrame(CGRectMake(100, 100, 100, 100)) | |
@purpleView.backgroundColor = UIColor.purpleColor | |
@purpleView.accessibilityLabel = 'Purple view' | |
view.addSubview(@purpleView) | |
@blueView = UIView.alloc.initWithFrame(CGRectMake(25, 25, 50, 50)) | |
@blueView.backgroundColor = UIColor.blueColor | |
@blueView.accessibilityLabel = 'Blue view' | |
@purpleView.performSelector('addSubview:', withObject:@blueView, afterDelay:0.5) | |
@redView = UIView.alloc.initWithFrame(CGRectMake(110, 25, 75, 75)) | |
@redView.backgroundColor = UIColor.redColor | |
@redView.accessibilityLabel = 'Red view' | |
view.addSubview(@redView) | |
end | |
end | |
describe "UIView extensions" do | |
tests SimpleViewController | |
it "returns the first subview with the specified accessibility label" do | |
window.viewByName('Container view', 1).should == controller.view | |
controller.view.viewByName('Purple view', 1).should == controller.purpleView | |
controller.blueView.viewByName('Purple view', 1).should == nil | |
end | |
it "keeps trying to find a view by accessibility label during the timeout" do | |
# This button shows up 0.5 second after the other views | |
window.viewByName('Blue view', 0.1).should == nil | |
window.viewByName('Blue view', 0.6).should == controller.blueView | |
end | |
it "looks through all superviews, until it finds a matching class" do | |
controller.purpleView.up(UIButton, 0.1).should == nil | |
controller.purpleView.up(ContainerView, 0.6).should == controller.view | |
end | |
it "keeps trying to look through all superviews, until it finds a matching class, during the timeout" do | |
controller.blueView.up(ContainerView, 0.1).should == nil | |
controller.blueView.up(ContainerView, 0.6).should == controller.view | |
end | |
it "returns all views of a specific class and sorts them top-left to bottom-right" do | |
window.viewsByClass(UIButton, 0.1).should == [] | |
window.viewsByClass(ContainerView, 0.1).should == [controller.view] | |
window.viewsByClass(UIView, 0.1).should == [ | |
controller.view, | |
controller.redView, | |
controller.purpleView | |
] | |
end | |
it "keeps trying to find a view by class during the timeout" do | |
controller.purpleView.viewsByClass(UIView, 0.1).should == [] | |
controller.purpleView.viewsByClass(UIView, 0.6).should == [controller.blueView] | |
end | |
end | |
describe "Bacon::Functional::API, concerning device events" do | |
tests SimpleViewController | |
it "finds a view by its accessibility label" do | |
view('Purple view').should == controller.purpleView | |
view('Blue view').should == controller.blueView | |
end | |
it "finds views by class" do | |
views(ContainerView).should == [controller.view] | |
end | |
it "returns a view immediately if given instead of an accessibility label" do | |
view = controller.purpleView | |
# This will raise if the #view helper would not actually return the view immediately | |
def window.viewByName(accessibilityLabel) | |
raise 'Oh noes!' | |
end | |
view(view).should == view | |
end | |
it "raises if no view by label could be found after the `timeout` passes" do | |
start = Time.now.to_i | |
e = catch_bacon_error { view('Does not exist') } | |
Time.now.to_i.should >= (start + 3) | |
e.message.should == "Unable to find a view with label `Does not exist'" | |
end | |
it "raises if no views by class could be found after the `timeout` passes" do | |
start = Time.now.to_i | |
e = catch_bacon_error { views(UITableView) } | |
Time.now.to_i.should >= (start + 3) | |
e.message.should == "Unable to find any view of class `UITableView'" | |
end | |
def catch_bacon_error | |
e = nil | |
begin | |
yield | |
rescue Bacon::Error => e | |
end | |
e.should.not == nil | |
e.count_as.should == :error | |
e | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SmallControlsViewController < UIViewController | |
attr_reader :tappableView, :switch, :lateKumbayaButton | |
def loadView | |
frame = UIScreen.mainScreen.applicationFrame | |
frame.origin = CGPointZero | |
self.view = UIView.alloc.initWithFrame(frame) | |
#view.userInteractionEnabled = true | |
view.accessibilityLabel = 'Container view' | |
buttonMargin, buttonWidth, buttonHeight = 20, 150, 34 | |
buttonY = buttonMargin | |
button = UIButton.buttonWithType(UIButtonTypeRoundedRect) | |
button.setTitle("Kumbaya!", forState:UIControlStateNormal) | |
button.frame = CGRectMake((frame.size.width - buttonWidth) / 2, buttonY, buttonWidth, buttonHeight) | |
button.addTarget(self, action:'buttonTapped=:', forControlEvents:UIControlEventTouchUpInside) | |
view.addSubview(button) | |
buttonY += buttonHeight + buttonMargin | |
@lateKumbayaButton = UIButton.buttonWithType(UIButtonTypeRoundedRect) | |
@lateKumbayaButton.setTitle("Late Kumbaya!", forState:UIControlStateNormal) | |
@lateKumbayaButton.frame = CGRectMake((frame.size.width - buttonWidth) / 2, buttonY, buttonWidth, buttonHeight) | |
@lateKumbayaButton.addTarget(self, action:'buttonTapped=:', forControlEvents:UIControlEventTouchUpInside) | |
# Delay adding the view so we can test that the `view` method retries until it's found | |
view.performSelector('addSubview:', withObject:@lateKumbayaButton, afterDelay:1) | |
switchY = buttonY + buttonHeight + buttonMargin | |
@switch = UISwitch.alloc.initWithFrame(CGRectMake((frame.size.width - buttonWidth) / 2, switchY, buttonWidth, buttonHeight)) | |
@switch.accessibilityLabel = 'Switch control' | |
@switch.on = false | |
view.addSubview(@switch) | |
# Nested tap gesture recognizers | |
tappableViewContainerY = switchY + buttonHeight + buttonMargin | |
container = UIView.alloc.initWithFrame(CGRectMake(10, tappableViewContainerY, buttonWidth + 50, buttonHeight + 50)) | |
container.backgroundColor = UIColor.greenColor | |
view.addSubview(container) | |
tappableViewY = buttonMargin | |
@tappableView = UILabel.alloc.initWithFrame(CGRectMake(10, tappableViewY, buttonWidth, buttonHeight)) | |
@tappableView.userInteractionEnabled = true | |
@tappableView.backgroundColor = UIColor.blueColor | |
@tappableView.accessibilityLabel = 'Tappable view' | |
@tappableView.textAlignment = UITextAlignmentCenter | |
@tappableView.text = 'Taps: 0' | |
container.addSubview(@tappableView) | |
previous_recognizer = nil | |
3.downto(1) do |taps| | |
2.downto(1) do |touches| | |
recognizer = UITapGestureRecognizer.alloc.initWithTarget(self, action:'handleTap:') | |
recognizer.numberOfTapsRequired = taps | |
recognizer.numberOfTouchesRequired = touches | |
@tappableView.addGestureRecognizer(recognizer) | |
recognizer.requireGestureRecognizerToFail(previous_recognizer) if previous_recognizer | |
previous_recognizer = recognizer | |
end | |
end | |
end | |
attr_accessor :buttonTapped | |
def buttonTapped?; @buttonTapped; end | |
attr_reader :tapRecognizer | |
attr_reader :tappedLocationInWindow | |
def handleTap(recognizer) | |
@tappableView.text = "Taps: #{recognizer.numberOfTapsRequired}" | |
@tapRecognizer = recognizer | |
@tappedLocationInWindow = recognizer.locationInView(nil) | |
end | |
end | |
describe "Bacon::Functional::API, concerning one-shot gestures" do | |
tests SmallControlsViewController | |
it "flicks a switch" do | |
flick "Switch control", :from => :left | |
controller.switch.isOn.should == true | |
flick "Switch control", :to => :left | |
controller.switch.isOn.should == false | |
end | |
it "by default taps at the center of a view" do | |
highlight_touches! | |
view = tap("Tappable view") | |
controller.tappedLocationInWindow.should == view.superview.convertPoint(view.center, toView:nil) | |
end | |
it "taps buttons" do | |
tap "Kumbaya!" | |
controller.buttonTapped.currentTitle.should == "Kumbaya!" | |
# this one actually shows up a second later | |
tap "Late Kumbaya!" | |
controller.buttonTapped.currentTitle.should == "Late Kumbaya!" | |
end | |
it "recognizes a single tap" do | |
tap "Tappable view" | |
controller.tapRecognizer.numberOfTapsRequired.should == 1 | |
controller.tapRecognizer.numberOfTouchesRequired.should == 1 | |
tap "Tappable view", :touches => 2 | |
controller.tapRecognizer.numberOfTapsRequired.should == 1 | |
controller.tapRecognizer.numberOfTouchesRequired.should == 2 | |
end | |
it "recognizes a double tap" do | |
tap "Tappable view", :times => 2 | |
controller.tapRecognizer.numberOfTapsRequired.should == 2 | |
controller.tapRecognizer.numberOfTouchesRequired.should == 1 | |
tap "Tappable view", :times => 2, :touches => 2 | |
controller.tapRecognizer.numberOfTapsRequired.should == 2 | |
controller.tapRecognizer.numberOfTouchesRequired.should == 2 | |
end | |
it "recognizes a triple tap" do | |
tap "Tappable view", :times => 3 | |
controller.tapRecognizer.numberOfTapsRequired.should == 3 | |
controller.tapRecognizer.numberOfTouchesRequired.should == 1 | |
tap "Tappable view", :times => 3, :touches => 2 | |
controller.tapRecognizer.numberOfTapsRequired.should == 3 | |
controller.tapRecognizer.numberOfTouchesRequired.should == 2 | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ScrollViewController < UIViewController | |
attr_reader :scrollView, :scrollViewRotation, :imageView | |
def loadView | |
@scrollViewRotation = 0 | |
frame = UIScreen.mainScreen.applicationFrame | |
frame.origin = CGPointZero | |
self.view = UIView.alloc.initWithFrame(frame) | |
@scrollView = UIScrollView.alloc.initWithFrame(view.bounds) | |
@scrollView.backgroundColor = UIColor.redColor | |
@scrollView.delegate = self | |
@scrollView.maximumZoomScale = 3 | |
view.addSubview(@scrollView) | |
@imageView = UIImageView.alloc.initWithImage(UIImage.imageNamed('We Need You.jpg')) | |
@imageView.frame = [[0, 0], @imageView.image.size] | |
@scrollView.accessibilityLabel = 'Scroll view' | |
@scrollView.contentSize = @imageView.image.size | |
@scrollView.addSubview(@imageView) | |
recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(self, action:'handleRotation:') | |
@scrollView.addGestureRecognizer(recognizer) | |
end | |
def viewForZoomingInScrollView(sv) | |
@imageView | |
end | |
def handleRotation(recognizer) | |
@scrollViewRotation = recognizer.rotation | |
@scrollView.transform = CGAffineTransformMakeRotation(recognizer.rotation) | |
end | |
end | |
describe "Bacon::Functional::API, concerning continuous gestures" do | |
tests ScrollViewController | |
it "creates 'pinch open' and 'pinch close' gesture events" do | |
before = controller.scrollView.zoomScale | |
pinch_open 'Scroll view' | |
controller.scrollView.zoomScale.should > before | |
before = controller.scrollView.zoomScale | |
pinch_close 'Scroll view' | |
controller.scrollView.zoomScale.should < before | |
end | |
it "creates rotate gesture events" do | |
before = controller.scrollViewRotation | |
rotate 'Scroll view', :degrees => 90 | |
controller.scrollViewRotation.should > before | |
end | |
before do | |
pinch_open 'Scroll view' | |
end | |
it "drags from point A to point B" do | |
before = controller.scrollView.contentOffset | |
drag 'Scroll view', :from => CGPointMake(310, 100), :to => CGPointMake(5, 150) | |
controller.scrollView.contentOffset.x.should > before.x | |
controller.scrollView.contentOffset.y.should < before.y | |
end | |
it "drags along the specified list of points" do | |
view = controller.scrollView | |
before = view.contentOffset | |
drag 'Scroll view', :points => linear_interpolate(_location_to_point(view, :bottom_right), _location_to_point(view, :left)) | |
view.contentOffset.x.should > before.x | |
view.contentOffset.y.should > before.y | |
end | |
it "drags with multiple fingers" do | |
controller.scrollView.panGestureRecognizer.minimumNumberOfTouches = 3 | |
controller.scrollView.panGestureRecognizer.maximumNumberOfTouches = 3 | |
before = controller.scrollView.contentOffset | |
drag 'Scroll view', :from => :right, :touches => 3 | |
controller.scrollView.contentOffset.x.should > before.x | |
controller.scrollView.contentOffset.y.should == before.y | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DeviceSpecController < UIViewController | |
def loadView | |
frame = UIScreen.mainScreen.applicationFrame | |
frame.origin = CGPointZero | |
self.view = UIImageView.alloc.initWithFrame(frame) | |
view.image = UIImage.imageNamed('We Need You.jpg') | |
end | |
def shouldAutorotateToInterfaceOrientation(orientation) | |
true | |
end | |
# This is all for `shake` support | |
attr_accessor :shaked | |
def shaked? | |
@shaked | |
end | |
def viewWillAppear(animated) | |
super | |
becomeFirstResponder | |
end | |
def viewDidDisappear(animated) | |
super | |
resignFirstResponder | |
end | |
def canBecomeFirstResponder | |
true | |
end | |
def motionEnded(motion, withEvent:event) | |
@shaked = motion == UIEventSubtypeMotionShake | |
end | |
# Accelerometer support | |
def enableAccelerometer=(flag) | |
UIAccelerometer.sharedAccelerometer.delegate = (flag == true ? self : nil) | |
end | |
attr_reader :accelerationData | |
def accelerometer(accelerometer, didAccelerate:acceleration) | |
@accelerationData = acceleration | |
end | |
end | |
describe "Bacon::Functional::API, concerning device events" do | |
tests DeviceSpecController | |
after do | |
rotate_device :to => :portrait | |
end | |
it "changes device orientation" do | |
rotate_device :to => :landscape, :button => :right | |
controller.interfaceOrientation.should == UIInterfaceOrientationLandscapeRight | |
rotate_device :to => :landscape, :button => :left | |
controller.interfaceOrientation.should == UIInterfaceOrientationLandscapeLeft | |
rotate_device :to => :portrait, :button => :bottom | |
controller.interfaceOrientation.should == UIInterfaceOrientationPortrait | |
rotate_device :to => :portrait, :button => :top | |
controller.interfaceOrientation.should == UIInterfaceOrientationPortraitUpsideDown | |
end | |
it "has default orientations for portrait and landscape for when the :button option is omitted" do | |
rotate_device :to => :landscape | |
controller.interfaceOrientation.should == UIInterfaceOrientationLandscapeLeft | |
rotate_device :to => :portrait | |
controller.interfaceOrientation.should == UIInterfaceOrientationPortrait | |
end | |
it "creates a shake motion gesture (for undo support, for instance)" do | |
shake | |
controller.should.be.shaked | |
end | |
it "sends accelerometer events" do | |
with_accelerometer do | |
accelerate :x => 0.5, :y => 0.5, :z => 0.5 | |
end | |
controller.accelerationData.x.should == 0.5 | |
controller.accelerationData.y.should == 0.5 | |
controller.accelerationData.z.should == 0.5 | |
end | |
def with_accelerometer | |
controller.enableAccelerometer = true | |
yield | |
ensure | |
controller.enableAccelerometer = false | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment