steel
steel

Reputation: 12520

Capybara: Test for Content in CSS PsuedoElements

I'm working on an app where text conditionally appears in a ::before pseudo element's content property and is rendered on the page. After a code change caused this important text to accidentally disappear, I wanted to be able to write tests that would capture that error if it happened again, but there are challenges grabbing the content from pseudo-selectors. I was looking for something like:

#scss
.content-div {
  &.condition-true {
    &:before {
      content: "conditional text";
    }
  }
}

#coffeescript
if @someCondition
  $('content-div').addClass('condition-true')
else
  $('content-div').removeClass('condition-true')

#spec
context "when true" do
  it "should have the conditional text" do
    # do thing that makes it true
    expect( page ).to have_content("conditional text")
  end
end

The solution wasn't so easy, and I thought I'd share here and let others comment, or provide other solutions.

I'm using Capybara 2.3.0 and Poltergeist 1.5.1.

Upvotes: 2

Views: 1017

Answers (2)

Mirror318
Mirror318

Reputation: 12683

I took @steel's answer and shifted it to a ruby function:

# Note this has no waiting logic,
# so the chord needs to already be rendered
def assert_first_chord(chord)
  content = page.evaluate_script <<-SCRIPT
    (function () {
      var element = document.getElementsByClassName('chord')[0];
      var content = window.getComputedStyle(element, ':after').getPropertyValue('content');

      return content;
    })()
  SCRIPT
  assert_equal(chord, content.delete('"'))
end

assert_first_chord('G')

The important thing to note is that this gets the element instantly, there's no capybara magic of waiting until an element appears, so you may want to find the element before using this method to check the contents.

Upvotes: 0

steel
steel

Reputation: 12520

The key was passing a block of code to page.evaluate_script, as well as Javascript's getComputedStyle() function.

content_array = page.evaluate_script <<-SCRIPT.strip.gsub(/\s+/,' ')
  (function () {
    var elementArray = $('.desired-css-selector');
    var contentArray = [];
    for (var i = 0, tot=elementArray.length; i < tot; i++) {
      var content = window.getComputedStyle( elementArray[i], ':before' ).getPropertyValue('content');
      contentArray.push(content);
    }
    return contentArray;
  })()
SCRIPT
content_array.each { |c| c.gsub!(/\A'|'\Z/, '') }

expect( content_array ).to include("conditional text")

UPDATE - SIMPLE EXAMPLE:

I've recently had to do a much simpler version of this:

color = page.evaluate_script <<-SCRIPT
  (function () {
    var element = document.getElementById('hoverme');
    var color = window.getComputedStyle( element, ':hover' ).getPropertyValue('color');

    return color;
   })()
SCRIPT

Upvotes: 3

Related Questions