the Website of Timothy McCabe Follower of Christ; Student of Epistemology, Apologetics, and Theology
Home Good News Proofs Questions Presentations Software More

How I Implemented JavaScript Unit Tests in my Cordova App

I had a lot of trouble finding clear, documented recommendations for implementing unit tests against the custom JavaScript code I'd written in my Cordova application, which implements several Cordova plugins. As a result of the plugin usage, testing in a standard browser made no sense, as the plugins' JavaScript libraries would not be available. So, if I tested in a browser, either I could not test any functions that implemented those plugins, or else I would have to mock far more than I found reasonable.

I wound up modifying my Cordova application to run Jasmine standalone 5.1.1 unit tests right inside the app.

I also made a config setting that I have to change before a production build to make sure my users don't see my unit tests when they open the app, though I won't be explaining that part here. You could always make an Easter Egg instead if you wanted, like tapping the logo seven times (or something) takes you to the unit tests.

Anyway, here's what I did and how I did it. Hopefully someone will find it useful. Note that this isn't a Cordova tutorial. I'm assuming you already have a Cordova app up-and-running. It's also not a Jasmine tutorial. I'm assuming you can write unit tests already or that you will figure out how elsewhere.

A.I. agrees that human reason necessitates God
Artificial Intelligence chatbot Claude agrees that the existence of human reason can only be explained by the existence of an omnipotent, omniscient, rational God.

First, for our purposes here, in your Cordova app's index.html file, wherever you have your other HTML (represented below by the "Hello world" paragraph), make a link or button that will load the unit tests. Generally, you'll want your scripts loading after your HTML.

<body>
    <div id="htmlWrapper">
        <p>Hello world!</p>
        <a href="#unittests" id="btnOpenUnitTests">
            Unit tests
        </a>
    </div>
 
    <script src="cordova.js"></script>
    <script src="js/index.js"></script>
</body>

Next, download Jasmine standalone 5.1.1 and unzip it.

I personally set up my www directory structure like this, and I'll tell you what the relevant files do.

www/
- css/
- img/
- js/
  - index.js
  - source-code.js
  - source-code2.js
- unit/
  - jasmine-5-1-1/
    - boot0.js
    - boot1.js
    - jasmine.css
    - jasmine.js
    - jasmine-html.js
  - spec/
    - spec-tests.js
    - spec-tests2.js
  - specrunner.js
- index.html

The js/index.js file launches everything. I'm not actually unit testing it at all. But, it's just launching things, generally by loading other scripts and/or calling functions in them.

The vast majority of the app's custom JS code would go into files like js/source-code.js.

The actual unit tests for js/source-code.js go into js/unit/spec/spec-tests.js.

The Jasmine testing framework (basically all the individual files in the unzipped lib/ folder that you downloaded) is in the unit/jasmine-5-1-1/ directory.

How can evil exist if God exists?
If God is good, he wouldn't want evil. If he is omnipotent, he would be able to stop it. If he is omniscient, he would know how to stop it. So how can there be a good God when evil exists?

Cordova recommends using just one HTML page and loading everything into it via JavaScript. So, linking to a different HTML file to run the specs didn't fit the paradigm. Instead, I wanted to load the unit testing framework into my existing HTML page. Therefore, the unit/specrunner.js is a custom file I created based on Jasmine's SpecRunner.html file (found in the root of the unzipped folder you downloaded). That's what makes this all work.

But first, let's tie our btnOpenUnitTests hyperlink to our unit tests. In index.js, add this code. Notice that I have some ancillary functions for loading CSS and JS files. That's how I like to do it. You can do it your way if you prefer something different.

function insertCssFile(path, onSuccess, onError)
{
    try
    {
        var css = document.createElement("link");
        css.rel = "stylesheet";
        css.type = "text/css";
        css.href = path;
        css.onload = function()
        {
            if (typeof onSuccess != 'undefined'
                && !!onSuccess)
            {
                onSuccess();
            }
        }
        css.onerror = function()
        {
            if (typeof onError != 'undefined'
                && !!onError)
            {
                onError(path, 'FAILURE loading CSS');
            }
        }
        document.body.prepend(css);
    }
    catch(e)
    {
        console.log(e);
    }
}

function insertJsFile(path, onSuccess, onError)
{
    try
    {
        var js = document.createElement("script");
        js.type = "text/javascript";
        js.src = path;
        js.onload = function()
        {
            if (typeof onSuccess != 'undefined'
                && !!onSuccess)
            {
                onSuccess();
            }
        }
        js.onerror = function()
        {
            if (typeof onError != 'undefined'
                && !!onError)
            {
                onError(path, 'FAILURE loading JS');
            }
        }
        document.body.append(js);
    }
    catch(e)
    {
        console.log(e);
    }
}

function insertJsFiles(pathsAry, onSuccess, onError)
{
    // Pass in an array of JS files and
    // they will be loaded in the
    // consecutive order specified in the array
    var tmpAry = [];
    var firstPth = pathsAry[0];
    for (var i=0, len=pathsAry.length; i<len; i++)
    {
        var pth = pathsAry[i];
        if (i != 0)
        {
            tmpAry.push(pth);
        }
    }
    if (tmpAry.length > 0)
    {
        insertJsFile(firstPth, function()
        {
            insertJsFiles(tmpAry, onSuccess, onError);
        });
    }
    else
    {
        insertJsFile(firstPth, function()
        {
            if (typeof onSuccess != 'undefined'
                && !!onSuccess)
            {
                onSuccess();
            }
        });
    }
}

$('#btnOpenUnitTests').unbind('click.test').bind(
'click.test',
function(e)
{
    e.preventDefault();
    // Set up test runner and run all unit tests
    insertJsFile('unit/specrunner.js');
});
What is God like?
Why should I care what anyone else thinks, including any kind of god? Even if god's bigger and stronger than me, that doesn't mean he's right...

Now let's look at the specrunner.js file. Notice that it calls the same CSS and JS loading functions that we put into the index.js file. Since that file was already loaded, and we have a one-page app, they should still be available.

function loadJasmine(onSuccess)
{
    // Add Jasmine (standalone) unit testing framework
    // https://jasmine.github.io/pages/getting_started.html
    // https://github.com/jasmine/jasmine/releases
    insertCssFile('unit/jasmine-5-1-1/jasmine.css');
    insertJsFiles(
    [
        'unit/jasmine-5-1-1/jasmine.js',
        'unit/jasmine-5-1-1/jasmine-html.js',
        'unit/jasmine-5-1-1/boot0.js'
    ],
    function()
    {
        // Jasmine is now set up.
        // Insert source and spec files here...
        if (typeof onSuccess != 'undefined'
            && !!onSuccess)
        {
            onSuccess(function()
            {
                // Lastly, run Jasmine
                insertJsFile('unit/jasmine-5-1-1/boot1.js');
            });
        }
    });
}

// Wipe old stuffs we don't need no more
$('#htmlWrapper').empty();

// Load our unit testing framework
// and all of our source and spec files
loadJasmine(function(onComplete)
{
    // Add source and spec files here...
    insertJsFiles(
    [
        'js/source-code.js',
        'js/source-code2.js'
    ], function()
    {
        insertJsFiles(
        [
            'unit/spec/spec-tests.js',
            'unit/spec/spec-tests2.js'
        ], function()
        {
            if (typeof onComplete != 'undefined'
                &&!!onComplete)
            {
                onComplete();
            }
        });
    });
});

That should be it. My actual code was a bit different, and the code that I've shared here per-se is not tested. If it doesn't seem to work right, please let me know.

Gilbert Guttlebocker, Defender of Dragons

Gilbert Guttlebocker, Defender of Dragons

Riveting, yet absurd; romantic, yet innocent; Gilbert Guttlebocker, Defender of Dragons is a little Roald Dahl, a little Harry Potter, and a little Chronicles of Narnia, all rolled into one. Timothy McCabe collaborates with the great Benedict Ballyhoot to bring you the novel of the century!

 

World Religions and Cults (volume 2)

In Printed Form

Along with numerous other authors including Don Landis, Bodie Hodge and Roger Patterson, Timothy McCabe contributes analyses of various world religions and cults in this volume from Master Books.

Other Writings

"Would you accept that, had you been born in Saudi Arabia, you would more than likely be defending the Quran and Allah with the same vehemence that you now defend Christ?"

Absolutely not: if I were born in Saudi Arabia, reality would be incoherent. The only things that can happen are the things God has willed to cause to happen. If these things did not happen, it would only be because God willed not to cause them to happen. God only causes that which He prefers to cause, so for Him to have caused other than what He has caused, He would have to prefer other than what He prefers. However, He is who He is, and He cannot deny Himself (Exodus 3:14; 2 Timothy 2:13).
Continue reading...

"Does evolution (and with that, the evils that have occurred in the name of natural selection, prior to the fall of man) disprove the existence of God?"

In short, no. There is absolutely no evidence that evolution, in the sense in which this question is referring to it, has ever happened. Evidence of the lack of evidence includes the "theory" of Punctuated Equilibrium, developed by evolutionary paleontologists Niles Eldredge and Stephen Jay Gould. One of the celebrated functions of this theory is its demonstration of why, if evolution were actually true, there would be no fossil evidence for it.
Continue reading...

"If creation happened about 5000 years ago, then how come a supernova was seen in 1987, but it was located 170,000 lightyears away?"

The study of light and its speed is a complex undertaking that physicists have not even nearly completed. Does light travel at different fixed speeds in either direction of a round trip? Has its speed always been what it appears to be today? Can anything material travel faster than it? No physicist alive seems to know the answers to these questions for certain. But God does.
Continue reading...

All articles