diff --git a/.travis.yml b/.travis.yml index 4ce8b7e..b7f6bfd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ rvm: - 1.9.3 script: - rake generate -- rake deploy +- rake deploy:preview env: global: - secure: ! 'EShtaDZJGtAPbrx7yzNEaXw+pLvOfyMklloEgLa9mvRtniYGXF9iQjcUYSvq diff --git a/CNAME b/CNAME deleted file mode 100644 index 2b601ee..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -testingwithfrank.com diff --git a/Gemfile b/Gemfile index bbc4d79..00b96bb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,6 @@ source 'http://rubygems.org' +gem "RedCloth" gem "rake" gem "jekyll", "0.11.0" gem "liquid", "~>2.2.2" -gem "microstatic", "~> 0.1.0" +gem "microstatic", "~> 0.3.0" diff --git a/Gemfile.lock b/Gemfile.lock index 2b37a85..c36a5a5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,17 +1,26 @@ GEM remote: http://rubygems.org/ specs: + RedCloth (4.2.9) albino (1.3.3) posix-spawn (>= 0.3.6) - aws-s3 (0.6.3) - builder - mime-types - xml-simple builder (3.2.0) classifier (1.3.3) fast-stemmer (>= 1.0.0) directory_watcher (1.4.1) + excon (0.25.3) fast-stemmer (1.0.1) + fog (1.14.0) + builder + excon (~> 0.25.0) + formatador (~> 0.2.0) + mime-types + multi_json (~> 1.0) + net-scp (~> 1.1) + net-ssh (>= 2.1.3) + nokogiri (~> 1.5) + ruby-hmac + formatador (0.2.4) jekyll (0.11.0) albino (>= 1.3.2) classifier (>= 1.3.1) @@ -23,15 +32,27 @@ GEM liquid (2.2.2) maruku (0.6.0) syntax (>= 1.0.0) + microstatic (0.3.0) + fog (>= 1) mime-types (1.23) + mini_portile (0.5.1) + multi_json (1.7.7) + net-scp (1.1.2) + net-ssh (>= 2.6.5) + net-ssh (2.6.8) + nokogiri (1.6.0) + mini_portile (~> 0.5.0) posix-spawn (0.3.6) + rake (10.0.4) + ruby-hmac (0.4.0) syntax (1.0.0) - xml-simple (1.1.2) PLATFORMS ruby DEPENDENCIES - aws-s3 (~> 0.6.3) + RedCloth jekyll (= 0.11.0) liquid (~> 2.2.2) + microstatic (~> 0.3.0) + rake diff --git a/Rakefile b/Rakefile index d46718f..812c275 100644 --- a/Rakefile +++ b/Rakefile @@ -1,8 +1,7 @@ -require 'aws/s3' require_relative 'tasks/deploy_tasks.rb' desc "use jekyll to generate the static site into the `public` dir" task :generate do - sh "jekyll public" + sh "jekyll" end diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..1b0137c --- /dev/null +++ b/_config.yml @@ -0,0 +1,2 @@ +destination: public +exclude: ["public","Gemfile","Gemfile.lock","CNAME","Rakefile","tasks"] diff --git a/_layouts/default.html b/_layouts/default.html index b1ba32a..7e2a46f 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -3,11 +3,9 @@ Testing With Frank — {{ page.title }} - + + + @@ -31,10 +29,9 @@
    @@ -47,6 +44,7 @@
  1. View Selectors
  2. User Contributed Steps
  3. Testing on a Physical Device
  4. +
  5. Testing Mac Apps
  • More Information diff --git a/device.md b/device.md index 1e48e60..ac2693c 100644 --- a/device.md +++ b/device.md @@ -27,19 +27,19 @@ You can use xcodebuild on the command line to do this. If you have multiple tar Fruitstrap will install the app to the device and launch it using gdb. Make sure your provisioning includes your device or it will fail on the verification step; and that you have proper code-signing certs in your Xcode. Delete your previous build first to verify that it actually loaded. Preparation: -symlink: /Users/[user_dir_name]/Library/Developer/Xcode/iOS DeviceSupport/6.1.x (xxx)/DeveloperDiskImage.dmg from +symlink: /Users/user_dir_name/Library/Developer/Xcode/iOS DeviceSupport/6.1.x (xxx)/DeveloperDiskImage.dmg from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/6.1 (10B141)/DeveloperDiskImage.dmg -symlink: /Users/[user_dir_name]/Library/Developer/Xcode/iOS DeviceSupport/6.0.x (xxx)/DeveloperDiskImage.dmg from +symlink: /Users/user_dir_name/Library/Developer/Xcode/iOS DeviceSupport/6.0.x (xxx)/DeveloperDiskImage.dmg from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/6.0/DeveloperDiskImage.dmg -symlink: /Users/[user_dir_name]/Library/Developer/Xcode/iOS DeviceSupport/5.1.x (xxx)/DeveloperDiskImage.dmg from +symlink: /Users/user_dir_name/Library/Developer/Xcode/iOS DeviceSupport/5.1.x (xxx)/DeveloperDiskImage.dmg from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/5.1/DeveloperDiskImage.dmg -symlink: /Users/[user_dir_name]/Library/Developer/Xcode/iOS DeviceSupport/5.0.x (xxx)/DeveloperDiskImage.dmg from +symlink: /Users/user_dir_name/Library/Developer/Xcode/iOS DeviceSupport/5.0.x (xxx)/DeveloperDiskImage.dmg from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/5.0/DeveloperDiskImage.dmg -symlink: /Users/[user_dir_name]/Library/Developer/Xcode/iOS DeviceSupport/4.3.x (xxx)/DeveloperDiskImage.dmg from +symlink: /Users/user_dir_name/Library/Developer/Xcode/iOS DeviceSupport/4.3.x (xxx)/DeveloperDiskImage.dmg from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3/DeveloperDiskImage.dmg ## Get my fork of fruitstrap (I had to patch the default path for gdb) diff --git a/faq.md b/faq.md index dc176c6..14c8d24 100644 --- a/faq.md +++ b/faq.md @@ -95,3 +95,14 @@ Specifically, you can add a 'run script' build step to your Xcode target's build That generates a support/bundle_path.rb file ever time you build which sets APP_BUNDLE_PATH to the location of the app bundle which XCode just built. Cucumber will by default automagically import anything in that support dir, so it will load that file and make the valid_APP_BUNDLE_PATH value for Frank to use. + +## I'm using CocoaPods and my build is failing + +This shows up with one of 2 errors - a `nothing to build`, or more often something like + +`ld: library not found for -lPods` and `clang: error: linker command failed with exit code 1 (use -v to see invocation)` + +The fix is to make sure the Pods are getting built, using `--configuration ONLY_ACTIVE_ARCH=NO` + +e.g. `frank build --workspace MyProject.xcworkspace --scheme MyProject --configuration ONLY_ACTIVE_ARCH=NO` + diff --git a/images/screenshots/ib-accessibility-identitiy.png b/images/screenshots/ib-accessibility-identitiy.png new file mode 100644 index 0000000..5d9b2d0 Binary files /dev/null and b/images/screenshots/ib-accessibility-identitiy.png differ diff --git a/index.md b/index.md index e1b613b..6bd3ea3 100644 --- a/index.md +++ b/index.md @@ -1,6 +1,6 @@ --- layout: default -title: Painless iOS Testing With Cucumber +title: Painless iOS and Mac Testing With Cucumber --- **Frank** allows you to write structured text test/acceptance diff --git a/mac.md b/mac.md new file mode 100644 index 0000000..8b70b89 --- /dev/null +++ b/mac.md @@ -0,0 +1,71 @@ +--- +layout: default +title: Testing Mac Apps +--- + +Frank can be used to write tests for Mac apps as well as iOS apps. Writing tests for Mac apps is similar to writing tests for iOS apps, but there are important differences. + +## Requirements + +Frank can be used to test 64-bit Mac apps on OS X 10.7 and 10.8. Due to the NDA covering the pre-release builds of 10.9, testing on 10.9 is not currently supported. If you need to run tests on 10.9 before the official release, see [this thread](https://groups.google.com/forum/#!topic/frank-discuss/hugP4qelN7s). + +The testing machine must have access for assistive devices turned on, as described in [Getting Started](getting_started.html). + +## Setup + +The steps to Frankify a Mac app are nearly identical to the [steps to Frankify an iOS app](http://blog.thepete.net/blog/2012/06/24/writing-your-first-frank-test/). The one difference is that you must pass a `--mac` flag to `frank build`. The `frank build` command normally compiles the app for running in a simulator. As Mac apps do not run in a simulator, the `--mac` flag will prevent Frank from trying to build your app for a simulator. + +## Labeling Views + +Frank identifies UIViews based on their `accessibilityLabel` property. However, `accessibilityLabel` does not exist on the Mac. Therefore, Frank defines a `FEX_accessibilityLabel` property on NSObject. By default, this property is equal to the object's accessibility description, which can be set in Interface Builder. + +![Accessibility Identity in Interface Builder](images/screenshots/ib-accessibility-identitiy.png) + +NSMenuItems and NSControls without accessibility descriptions will set their `FEX_accessibilityLabel` to their `title`. You can override `-FEX_accessibilityLabel` to provide a different label for your own objects. + +## View Frames + +Frank uses the `accessibilityLabel` property to get a view's size and position, which also does not exist on the Mac. Therefore, Frank on the Mac adds a `FEX_accessibilityFrame` property to NSObject, which is created by combining the object's `NSAccessibilityPositionAttribute` and `NSAccessibilitySizeAttribute`. If your object does not have these accessibility attributes, you will need to override `-FEX_accessibilityFrame`. + +If you override `-FEX_accessibilityFrame`, it should return a rect assuming that the origin point (0, 0) is at the top-left, rather than bottom-left of the screen. Although Cocoa's origin point is in the bottom-left, the accessibility API and the CGEvent API (which is used to create mouse events) assume the origin is in the top-left. + +## View Hierarchy + +Frank on iOS builds a hierarchy of view objects by starting with the application's `keyWindow`, adding its `subviews`, and their `subviews`, and so on, recursively. The Mac, however, has a variety of objects which do not inherit from NSView, such as NSWindow, NSMenuItem, and various view cells. + +The root of the view hierarchy on the Mac is the shared NSApplication object. Its children are the application's windows, its menu bar, and its contextual menus. + +In order to build this view hierarchy, Frank adds two properties to NSObject on the Mac, `FEX_parent` and `FEX_children`. When implementing a custom view class with cells, you will need to wrap those cells in objects which implement `FEX_accessibilityLabel` and `FEX_accessibilityFrame`. + +When overriding `FEX_children`, take care to only add children which are visible to the user. Views present in a scroll view, but which are outside of the view's visible bounds, should not be included. Some views, like view-based NSTableViews, do not create subviews for objects which are not visible, so your views should do the same for consistency. + +## The `bring_to_front` and `simulate_click` Functions + +Because the Mac can run multiple, overlapping apps, Frank needs a way to bring your application and its individual windows to the foreground. The `bring_to_front` function can be passed an NSApplication, an NSWindow, or an NSView. When passed an NSView, it will call `bring_to_front` on that view's NSWindow. + +{% highlight ruby %} + bring_to_front "view:'NSApplication' marked:'TestApp'" + bring_to_front "view:'NSWindow' marked:'Untitled 1'" +{% endhighlight %} + +Most mouse functions should only be called on objects that are visible in the frontmost window. However, `simulate_click` is an exception to this rule. This function does not create any mouse events, but by default performs `NSAccessibilityPressAction` on the object. NSMenuItem and other classes which do not support `NSAccessibilityPressAction` implement `-FEX_simulateClick` to perform an action when `simulate_click` is called on them. See the documentation for more details. + +While `simulate_click` can be safely called on all objects in the background, not all objects will respond to actions while in the background. Using frank console to call simulate_click on an object is a good way to test whether that object will respond while in the background. + +## The `click`, `double_click` and `drag_with_initial_delay` Functions + +The remaining mouse functions simulate actual mouse events. While running tests that use these functions, it is important to not move the mouse. This means that while the tests are running, you cannot use your computer to do other things. For this reason, you may want to run your tests on another machine or in a virtual machine. + +## The Keyboard Functions + +The keyboard functions `type_into_keyboard` and `type_shortcut` can be called while the application is in the background. The keyboard events are limited to your app. However, menu shortcuts will not work when the app is in the background. If you want a menu item to perform its action when the app is in the background, use `simulate_click`. + +## Contextual Menus + +Contextual menus can be opened using calling the `show_menu` function on an object that spawns contextual menus. Contextual menus do not fit well into the view hierarchy, and so Frank considers them to be the children of the shared NSApplication instance, instead of the right-clicked object. In addition, contextual menus items are only visible to Frank while their menu is open. + +## Functions That Don't Work on the Mac + +Most functions that work on iOS also work on the Mac. However, there are a few that do not. Most notably, the touch functions, the `fill_in` function, and the functions that deal with animation are currently iOS-only. + +On iOS, all views are backed by CALayers. This is not always true on OS X, and OS X has multiple ways to animate a view. If you need to test whether your views are animating, you'll need to implement a test appropriate for how you are animating your views. diff --git a/tasks/deploy_tasks.rb b/tasks/deploy_tasks.rb index 93f3c3c..009d3da 100644 --- a/tasks/deploy_tasks.rb +++ b/tasks/deploy_tasks.rb @@ -1,7 +1,7 @@ -require 'microstatic' +require 'microstatic/rake' PREVIEW_AWS_BUCKET = "preview.testingwithfrank.com" -PRODUCTION_AWS_BUCKET = "preview.testingwithfrank.com" # NOT CONFIDENT QUITE YET +PRODUCTION_AWS_BUCKET = "www.testingwithfrank.com" def aws_creds { @@ -16,8 +16,19 @@ def deploy_to_bucket(bucket) deployer.upload end -desc "deploy to production" -task :deploy do - deploy_to_bucket(PRODUCTION_AWS_BUCKET) -end +namespace :deploy do + source_dir = File.expand_path("../../public",__FILE__) + + desc "deploy to production" + Microstatic::Rake.s3_deploy_task( :prod ) do |task| + task.source_dir = source_dir + task.bucket_name = PRODUCTION_AWS_BUCKET + end + + desc "deploy to preview" + Microstatic::Rake.s3_deploy_task( :preview ) do |task| + task.source_dir = source_dir + task.bucket_name = PREVIEW_AWS_BUCKET + end +end