View complete objects using addProbe

Trying to debug observers and bindings in a SproutCore application? First, call addProbe on any interesting keys:

MyApp.MyView = SC.View.extend({
  init: function () {
    sc_super();
    this.addProbe('layout');
    this.addProbe('frame');
  }
});

Then, open up sproutcore/frameworks/runtime/mixins/observable, and change this:

SC.logChange = function logChange(target, key, value) {
  console.log("CHANGE: %@[%@] => %@".fmt(target, key, target.get(key))) ;
};

To this:

SC.logChange = function logChange(target, key, value) {
  console.log("CHANGE: %@[%@] =>".fmt(target, key), target.get(key));
};

Now, open up the debugging console in your browser. Whenever the specified properties change, your will see a fully-expandable JavaScript object on the console.

Using SC.ControlTestPane? Does your app GUI show up instead of your tests?

If you’re using SC.ControlTestPane to write unit tests for your views (more on this later, perhaps), and you see your main application GUI appearing where your tests belong, then you may have a syntax error in your JavaScript. Check your console for error messages.

Page through an SC.ListView with the spacebar

Would you like to page down through an SC.ListView with the spacebar? Try this:

MyApp.CustomScrollView = SC.ScrollView.extend({
  
  contentView: SC.ListView.design({
    // ...bindings, etc., go here...

    keyDown: function(evt) {
      if (SC.PRINTABLE_KEYS[evt.charCode] === ' ') {
        var scroll_view = this.getPath('parentView.parentView');
        scroll_view.scrollBy(0, scroll_view.get('frame').height - 50);
        return YES;
      } else {
        return NO;
      }
    }
  })
});

This will page down one screen (minus 50 pixels) each time you hit the spacebar.

Does SproutCore take 30 seconds to reload under sc-server? Use thin.

For some reason, reloading a SproutCore app under sc-server may take 20 or 30 seconds. If this happens to you, try:

sudo gem install thin

This should get reload times down to a couple of seconds.

Tom Dale: How to apply a style to the current input field

Tom Dale explains how to apply a style to the current input field:

.sc-text-field-view.focus input { 
        color: red; 
} 

Testing SproutCore with Cucumber + Selenium

This isn’t really a complete tutorial; just a few tricks to make the process a lot easier.

First, add the following code to features/support/env.rb, replacing existing bits as necessary:

Webrat.configure do |config|
  config.mode = :selenium
  config.application_environment = Rails.env

  # Same as script/server, so SproutCore can proxy to the same port in both
  # development and test modes.
  config.application_port = 3000 

  # This is used for both the initial browser startup, and the maximum
  # timeout for commands like 'open'.  It needs to be very high, because we
  # may need to wait for a SproutCore recompile at some point.
  config.selenium_browser_startup_timeout = 40
end

require 'features/environments/selenium_locator_fix'

World(Webrat::Selenium::Methods)
World(Webrat::Selenium::Matchers)

def expect_element xpath
  selenium.wait_for_element("xpath=#{xpath}", :timeout_in_seconds => 5)
end

def press_key(str)
  chr = str[0]
  chr.integer?.should == true # Breaks for Ruby 1.9?
  key_arg = sprintf("\\%02d", chr)

  # This works in all current browsers except Safari < 4.
  # http://stackoverflow.com/questions/497094/how-do-i-find-out-which-javascript-element-has-focus
  selenium.key_press("dom=document.activeElement", key_arg)
end

Here’s the locators fix:

# Make field_labeled work correctly with Selenium.  This is based on the
# discussion at
# https://webrat.lighthouseapp.com/projects/10503/tickets/255-field_labeled-locator-missing-in-selenium-mode
# but it has been changed in a number of ways to actually make it work again.

module Webrat::Locators
  def field_labeled(label, *field_types)
    Webrat::Locators::FieldLabeledLocator.new(self, Webrat::XML.document(response_body), label, *field_types).locate!
  end
end

class Webrat::SeleniumSession
  def dom
    @dom ||= Webrat::XML.document(response_body)
  end

  def current_dom
    dom
  end

  def elements
    @elements ||= {}
  end
end

Now, we need to add layerId properties to some of our globally-known views, so we have a chance of finding them:

// Needed so Cucumber can find this element.  Note that this ID
// actually applies to a wrapper element containing the actual input
// element, so it's still pretty tricky to get at.
layerId: 'searchBox'

Here are some sample step definitions:

When /^I run a search for "([^\"]*)"$/ do |query|
  # SproutCore assigns random element IDs, and places those IDs on wrapper
  # elements instead of the actual input elements.  So we override the
  # layerId property on our testable views, and write some messy XPath to
  # extract the actual element we want.
  locator = "xpath=id('searchBox')//input"
  selenium.wait_for_element(locator, 5)
  selenium.type(locator, "#{query}")
  press_key("\r")
end

When /^I press tab$/ do
  press_key("\t")
end

When /^I press return$/ do
  press_key("\r")
end