Question

I have a page with text in different HTML elements and I want a quick way of verifying the text.

Using jasmine and jasmine-query to load HTML and test DOM.

For example, I want to verify text in this HTML

<table id="users">
    <thead>
        <tr>
            <td>user</td>
            <td>permissions</td>
            <td>last seen</td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Darren</td>
            <td>testuser,customer</td>
            <td>today</td>
        </tr>
        <tr>
            <td>Hillary</td>
            <td>administrator</td>
            <td>yesterday</td>
        </tr>
    </tbody>
</table>

And let's say I want to verify each row in my table contains the correct text. Jasmine test file:

    it('should find correct text in table', function () {
        expect( $('#users').find('tbody tr').first() ).toContainText('Darren testuser,customer today');
        expect( $('#users').find('tbody tr').last() ).toContainText('Hillary administrator yesterday');

    });

I will get this failure:

Testexample:: should find correct text in table: failed
  Expected '<tr>
                <td>Darren</td>
                <td>testuser,customer</td>
                <td>today</td>
            </tr>' to contain text 'Darren testuser,customer today'. (1)
  Expected '<tr>
                <td>Hillary</td>
                <td>administrator</td>
                <td>yesterday</td>
            </tr>' to contain text 'Hillary administrator yesterday'. (2)
1 spec in 0.283s.
>> 2 failures

The other experiment was to use jQuery.text() to extract, then I still have an error because of all the whitespace:

    it('should find correct text in table with jQuery.text()', function () {
        expect( $('#users').find('tbody tr').first().text() ).toContain('Darren testuser,customer today');
        expect( $('#users').find('tbody tr').last().text()  ).toContain('Hillary administrator yesterday');

    });

Gives this failure:

Testexample::  should find correct text in table with jQuery.text(): failed
  Expected '
                Darren
                testuser,customer
                today
            ' to contain 'Darren testuser,customer today'. (1)
  Expected '
                Hillary
                administrator
                yesterday
            ' to contain 'Hillary administrator yesterday'. (2)
1 spec in 0.291s.
>> 2 failures

Capybara (for ruby) has a way to normalize text so that I can always see a reasonable text representation of my HTML. How would I normalize whitespace in an easy way so that I can make verifications like this?

(I don't expect answers like 'you should not test across html elements'... since this is the premise for the question. Actually I like making assertions across several elements: It's readable, short, and give a quick view if stuff is working. Also really necessary when I am testing from outside-in)

Was it helpful?

Solution 2

Thanks to Tobias' answer I ended up improving on that and wrote my own custom matcher. Now I can write:

        expect( $('#users').find('tbody tr').first() ).toHaveNormalizedText('Darren testuser,customer today');

Matcher code -- added in the beforeEach() block of my describe() scenario:

        this.addMatchers({
            toHaveNormalizedText: function(expected) {
                var actualText = $.trim(this.actual.text()).replace(/\s+/g, ' ');
                var result = actualText === expected;
                if( result) return result;
                //rest is only if it fails
                var notText = this.isNot ? " not" : "";
                var charcodes = [];
                for(var i=0; i<actualText.length; i++){
                    charcodes.push(actualText.charCodeAt(i));
                    if(i>23) break;
                }
                this.message = function () {return 'Expected "' + actualText + notText + '" to match "'+expected+'"\n\nFirst 25 charcodes:\n'+charcodes;};
                return result;
            }
        });

A few notes about this. Here is the actual replacement:

$.trim(this.actual.text()).replace(/\s+/g, ' ');

$.trim() was important for my html structure which did include whitespace in the beginning of the text() string.

this.message = function () {return 'Expected "' + actualText + notText + '" to match "'+expected+'"\n\nFirst 25 charcodes:\n'+charcodes;};

this.message is jasmine's custom error message. This string outputs strings like this on error:

failed
  Expected "Darren testuser,customer todaiiy" to match "Darren testuser,customer today"

First 25 charcodes:
68,97,114,114,101,110,32,116,101,115,116,117,115,101,114,44,99,117,115,116,111,109,101,114,32 (1)

I decided to add the charcodes because at one point I had a problem with charcodes 10 (newline) and 32 (spaces).

OTHER TIPS

I often add something like this to my projects:

String.prototype.normalizeSpace = function() {
  return this.replace(/\s\s+/g, ' ');
}

So after .text() you could just add a call to .normalizeSpace()

For Jasmine v2, I wrote this:

jasmine.addMatchers({
  toHaveTrimText: function() {
    return {
      compare: function(actual, expected) {
        var actualText = $(actual).text().trim();
        var result = {
          pass: actualText === expected,
          message: 'Expected "' + actualText + '" to match "' + expected + '"'
        }
        return result;
      }
    }
  }
});

I'm also using https://github.com/velesin/jasmine-jquery.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top