27 January 2010

It should have no missing translations!

I'm a big fan of rspec and of rails' I18n, and I don't like having to study yml translation files over and over to make sure every key has a translation in every language; so I wrote it_should_have_no_missing_translations to test my templates for missing translations.

Previously, I needed this for every template:

  it "should have no missing translations in fr" do
    I18n.locale = "fr"
    do_render
    response.should_not have_tag("span.translation_missing")
  end

  it "should have no missing translations in en" do
    I18n.locale = "en"
    do_render
    response.should_not have_tag("span.translation_missing")
  end

Where do_render knows how to render the template I'm testing. If you're like me, and I presume you are, you're thinking the duplication up there is a bit annoying and someone should do something about it. Well, here you go:

  it_should_have_no_missing_translations

You like? Obviously, it_should_have_no_missing_translations needs a bit of context, like an implementation of do_render, and any other setup you need. Here's the implementation, under the WTFPL. Copy it into your specs or make a helper out of it that you include in your spec, or publish it in a gem and become famous.

  def it_should_have_no_missing_translations
    INSTALLED_LANGUAGES.each do |lang|
      it "should not have translations missing in #{lang}" do
        I18n.locale = lang
        do_render
        response.should_not have_missing_translations
      end
    end
  end

have_missing_translations is defined thus:

  INSTALLED_LANGUAGES = [:en, :fr] unless defined?(INSTALLED_LANGUAGES)

  class TranslationsMissing
    def initialize(scope)
      @scope = scope
    end

    def matches? response
      if response.is_a? String
        root_node = HTML::Document.new(response, false, false).root
      else
        root_node = HTML::Document.new(response.body, false, false).root
      end
      m = @scope.css_select root_node, ".translation_missing"
      @missing_translations = []
      m.each do |mt|
        @missing_translations << mt.children.first
      end
      m.size > 0
    end

    def failure_message
      "expected that response would contain a translation_missing element, but it didn't"
    end

    def negative_failure_message
      "expected that response would contain no missing translations, but it contained these \n#{@missing_translations.join("\n")}"
    end
  end

  def have_missing_translations
    TranslationsMissing.new self
  end

No comments:

Post a Comment