DOM tests in Jasmine

This continues some notes about working on sf-active-js, moving it to a contemporary Javascript development environment.

Here’s a Jasmine spec to instantiate an HTML5 widget and test a few of the method calls. These method calls cause changes in the DOM, and these tests verify that the changes happened.

describe("comment", function() {
    var comment;
    beforeEach(function() {
        var elements = $('<div id="disclose"></div><div id="edit"></div>');
        $('body').append(elements);
        comment = Comment('#edit', '#disclose');
    });
    afterEach(function() {
        $('#disclose').remove();
        $('#edit').remove();
    });
    it("Comment should add three inputs and one textarea.", function() {
        expect($('input').length).toEqual(3);
        expect($('textarea').length).toEqual(1);
    });
    it("should hide the form", function() {
        comment.hide();
        expect($('input:visible').length).toEqual(0);
    });
    it("should fill and clear the form", function() {
        comment.setSubject('boo');
        expect($('input:eq(0)').val()).toEqual('boo');
        comment.setAuthor('foo');
        expect($('input:eq(1)').val()).toEqual('foo');
        comment.setText('bar');
        expect($('textarea:eq(0)').val()).toEqual('bar');
        comment.clear();
        expect($('input:eq(0)').val()).toEqual('');
        expect($('input:eq(1)').val()).toEqual('');
        expect($('textarea:eq(0)').val()).toEqual('');
    });
    it("should hide the disclosure", function() {
        comment.disableCommentDiscloser();
        expect($('#disclose:visible').length).toEqual(0);
    });
    it("should show the disclosure", function() {
        comment.enableCommentDiscloser();
        expect($('#disclose:visible').length).toEqual(1);
    });
});

The good news is that the new modularization and object orientation for the code is going to work. It’s testable. It’s encapsulated. It’s leading to better, less confusing code.

On the other hand, I feel these tests have dubious value. They just test the most basic DOM manipulations, which have to be created visually in the first place.

The code is interesting, because it uses :eq() and :visible, which I rarely ever use.

I could see it being useful when there’s an input of a collection of data, and you test to see if all the data was displayed. Then, you’re testing the logic to ingest and display the data, which is more complex than plain-old-DOM-manipulation, which this library does.

Also, I skipped over the hardest test which was a call named post() that posts the form data to a server.

I skipped it because this would require making a mock server and then intercepting the $.ajax() method.

Here are some tools that help do that – i haven’t used them yet.

https://github.com/jasmine/jasmi…

https://github.com/jakerella/jqu…

To be more realistic, you want to control both ends and test it end-to-end.

Integrating PHP into the testing

As a first step toward that, I had to rearrange the project again, to integrate PHP. The servers associated with the application are stored in a directory called ws/, and are written in PHP. They occupy the same namespace as the client-side files.

The modules used were: grunt-php, grunt-browserify, and grunt-contrib-watch

The grunt-php instructions had the basic spec. The main difference is that I used a “test” job instead of “dist”, and am testing against the uncompressed code, and don’t need any of the minified files.

The Jasmine layout needed to be changed, however. I created a directory called src/jasmine/ that would be accessible via grunt’s php server. All the Jasmine files and folders were moved there: SpecRunner.html, lib, and spec.

SpecRunner was edited so it would point to the right files, and tests were run.

At this point, I had access to both the Jasmine tests, and the PHP services.

On the PHP side, however, I couldn’t do anything because I didn’t have a dev environment for the server side work. I’d need to build that up next.

On the client side, I still couldn’t run network tests because I needed to abstract out the URLs. Instead of calling ‘/js/ws/post.php’, I’d have to make a function like ws_url(‘post’) that would return the correct URL with the correct path.

So, some other parts of the code need to be changed to make network communications testable.

(There was already a related function called getProxyUrl(url) that created a couple problems. It would convert local requests to remote requests via a local proxy, thus avoiding the cross-domain request problem, but today, with CORS, this is somewhat unnecessary. It’s better to add CORS functionality to the servers. It also gets in the way of testing, because you really want to run requests to local servers; right now, running the app locally reaches out to the production server.)

Here are some of the sources:

Gruntfile.js:

        php: {
            test: {
                options: {
                    hostname: '127.0.0.1',
                    port: 9000,
                    base: 'src',
                    keepalive: false,
                    open: false
                }
            }
        },
        browserSync: {
            test: {
                bsFiles: {
                    src: [
                        'src/jasmine/spec/*',
                        'src/js/*'
                    ]
                },
                options: {
                    proxy: '<%= php.test.options.hostname %>:<%= php.test.options.port %>',
                    watchTask: true,
                    notify: true,
                    open:true,
                    logLevel: 'silent',
                    ghostMode: {
                        clicks: true,
                        scroll: true,
                        links: true,
                        forms: true
                    }
                }
            }
        }

Also in the Gruntfile

        watch: {
            test: {
                files: [],
                tasks: []
            }
        },

This causes watch to run its loop, but won’t do anything.

The layout of the Jasmine files became:

  /
    /src
        /js
        /vendor
        /jasmine
            /lib
            /spec
            SpecRunner.html

And you could access the SpecRunner via http://localhost:3000/jasmine/Sp…