1af30891b7e22f41acf79a1e103b6a18?s=70x70

We’ve mentioned our love of page objects in feature testing. Setting up the environment page objects, our pattern is to implement method missing to handle methods of type _page. When a step requests login_page, method missing picks up the request, lazy loads a LoginPage into a instance variable on demand. That @login_page instance is then available to any subsequent step in the spec through the login_page method.

module ProjectWorld
  def method_missing(method, *args, &block)
    if method =~ /_page$/
      variable_name = "@#{method}"
      ivar = instance_variable_get(variable_name)

      unless ivar
        page = method.to_s.classify.constantize.new
        ivar = instance_variable_set(variable_name, page)
      end
      ivar
    else
      super
    end
  end
end
World(ProjectWorld)

This is possible because any instance variable that is set in the World of a Cucumber feature spec is globally available to any other step of that spec. This applies not only to page objects, but to any data models that are created or factory’d up into instance variables. We found ourselves doing things like this a lot in our steps.

Given(/^there is an active promotion with title "(.*?)"$/) do |title|
  @active_promotion = FactoryGirl.create(:active_promotion, title: title)
end

Given(/^the active promotion has a message "(.*?)"$/) do |text|
  FactoryGirl.create(:message, promotion: @active_promotion, text: text)
end

The assumption in the second step makes makes me uncomfortable, i.e. some previous step has set up @active_promotion. Keep in mind these two steps could have been defined in completely different files, and still work. Global accessibility of instance variables in Cucumber seems wrong, but it can be rationalized I suppose. You can be relatively assured that each spec is a self contained little program with a relatively small scope.

We can avoid this in our data objects though by catching calls to them and lazy loading, similar to what we do with our page objects. Since we’ve given in to the idea of global state in our specs, why not apply this same pattern to our short-lived data models? Chris Nelson and I decided to give it a try on our last project.

It turns out that factory_girl makes this super simple. factory_girl makes it easy to represent a model in it’s various states by just setting it up and referring to it by name. For example it’s easy and preferable in factory_girl to define representations for a promotion like this:

FactoryGirl.create(:active_promotion)
FactoryGirl.create(:pending_promotion)

rather than:

FactoryGirl.create(:promotion, status: "active")
FactoryGirl.create(:promotion, status: "pending")

This makes it easy define simple accessors in our Project world that return very specific, consistent data objects. Our module iterates through each defined factory, defining a lazy-loading accessor for each:

module ProjectWorld
  def method_missing(method, *args, &block)
    ...
  end

  FactoryGirl.factories.map(&:name).each do |factory|
    define_method(factory) do
      variable_name = "@#{factory}"
      ivar = instance_variable_get(variable_name)

      unless ivar
        object = FactoryGirl.create(factory)
        ivar = instance_variable_set(variable_name, object)
      end
      ivar
    end
  end
end

We can now effectively change the above steps to this:

Given(/^there is an active promotion with title "(.*?)"$/) do |title|   
end

Given(/^the active promotion has a message "(.*?)"$/) do |text|
  FactoryGirl.create(:message, promotion: active_promotion, text: text
end

The active_promotion method in the second step has been defined in our module. It will look for an instance_variable @active_promotion, and return it if it is defined. Otherwise it will set that @active_promoiton to a newly instantiated active_promotion factory. This effectively makes the first step, which was responsible for setting up the @active_promotion, a NOOP.

This pattern allows us to access models in the same way in every step, having confidence that it will never be nil.

6657ed53edd49aa0c9d32817780d9501?s=70x70

The content scale effect has become a fairly popular design pattern across the web and mobile apps recently. This is the effect where the main content section of the UI is pushed, pulled, scaled, or rotated in some way to reveal additional information. In most cases it is used show and hide some sort of navigation or menu. Today I’d like to explore this effect in a slightly different context – image overlays.


View Demo

It is important to note that we will be using CSS3 transforms and transitions to achieve these effects so older browsers might not render them correctly.

In this example we’ll scale our image back while fading in the overlay and give the text inside a slight upward motion while fading it in. First, lets start with out basic thumbnail component structure:

<ul>
  <li class="thumbnail">
    <div class="thumbnail__overlay">
      <h3 class="title"> </h3>
      <p class="caption"> </p>
      <a class="button"> Read more <span class="button__arrow"> &#8594 </span>
    </div>
    <div class="thumbnail__image">
      <img src="...">
    </div>
  </li>
  <li class="thumbnail">
    ...
  </li>
  <li class="thumbnail">
    ...
  </li>
</ul>

Next we can add initial styles:

.thumbnail {
  position: relative;
  overflow: hidden;
}

.thumbnail__image {
  transform-origin: bottom center;
  transition: transform 0.5s;
}

.thumbnail__overlay {
  position: absolute;
  width: 100%;
  height: 100%;
  padding: 25px;
  background-color: rgba(87,192,232,0.85);
  opacity: 0;
  transform: translateY(100%);
  transition: transform 0.5s, opacity 0.5s;
}

.thumbnail__overlay .title,
.thumbnail__overlay .caption,
.thumbnail__overlay .button {
  opacity: 0;
  transform: translateY(20px);
  transition: transform 0.5s, opacity 0.5s;
  transition-delay: 0.3s;
}

And then layer on our transition styles:

.thumbnail:hover .thumbnail__image {
  transform: scale(0.9);
}

.thumbnail:hover .thumbnail__overlay {
  transform: translateY(0%);
}

.thumbnail:hover .thumbnail__overlay .title,
.thumbnail:hover .thumbnail__overlay .caption,
.thumbnail:hover .thumbnail__overlay .button {
  opacity: 1;
  transform: translateY(0%);
}

Please check the demo for some more examples. I hope find this little experiment enjoyable and inspiring!