Phil
Phil

Reputation: 504

Jasmine beforeEach not waiting for done callback

I'm having an issue with Jasmine (+Karma + Webpack)

I narrowed the test down to the beforeEach statement not waiting for the done() callback to be executed before running the it block.

EDIT: Navjot Ahuja pointed out that jasmine-node has an issue with setTimeout in beforeEach so I've changed it to use promises, slightly different to his suggestion (as his works, but this example doesn't)

Here's some things I've found which are odd:

This is the test:

describe('Test', function () {
    let flag = false;

    describe('simple test -', function () {
        beforeEach(function (done) {
            console.log('SETTING TIMEOUT');

            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    flag = true;
                    done();
                }
            };
            xhttp.open('GET', 'https://api.github.com/zen', true);
            xhttp.send();
        });
        it('should wait for done and set flag correctly', function () {
            console.log('INSIDE IT STATEMENT');
            expect(flag).toBe(true);
        });
    });
});

This is karma.conf.js

// Karma configuration
const webpackTestConfig = require('./build/tests.webpack.config.js');

module.exports = function (config) {
    const webpackConfig = webpackTestConfig;

    config.set({
        client: {
            args: [
                `--ver=${config.ver}`,
                `--region=${config.region}`,
                `--env=${config.env}`
            ],
            captureConsole: true
        },

        basePath: '',
        frameworks: ['jasmine'],
        files: [
            'tests/integration/integrationTests.js'
        ],

        reporters: ['spec', 'junit'],
        junitReporter: {
            useBrowserName: false,
            outputFile: 'reports/js/unit-components/results/ITESTS-junit.xml'
        },

        coverageReporter: {
            reporters: [
                {type: 'text-summary', dir: 'reports/js/unit-components/coverage/text-summary/', subdir: '.'},
                {type: 'lcov', dir: 'reports/js/unit-components/coverage/lcov/', subdir: '.'},
                {type: 'html', dir: 'reports/js/unit-components/coverage/html', subdir: '.'},
                {type: 'cobertura', dir: 'reports/js/unit-components/coverage/cobertura', subdir: '.'}
            ]
        },

        preprocessors: {
            'tests/integration/integrationTests.js': ['webpack']
        },
        webpack: webpackConfig,
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        captureTimeout: 60000,
        browserNoActivityTimeout: 600000,
        singleRun: false
    });
};

This is the test.webpack.config.js

/*global __dirname*/
const webpackConfigBase = require('./webpack.config.js');
const path = require('path');
const _ = require('underscore');
const rootDir = path.resolve(__dirname, '..');

module.exports = () => {
    const config = webpackConfigBase();

    _.extend(config.resolve.alias, {
        'integrationTests': path.join(rootDir, 'tests/integration'),
        'testUtils': path.join(rootDir, 'tests/util'),
        'testFixtures': path.join(rootDir, 'tests/fixture')
    });

    return config;
};

And this is the referenced base webpack config:

/* globals __dirname */
const path = require('path');
const rootDir = path.resolve(__dirname, '..');

function matchModule(resource, moduleName) {
    return resource && resource.indexOf(moduleName) >= 0;
}

module.exports = () => ({
    context: path.join(rootDir, 'www/latest/'),
    entry: './app/api.js',
    resolve: {
        modules: [
            path.join(rootDir, 'www/latest/app'),
            path.join(rootDir, 'node_modules')
        ],
        alias: {
            'vendor': path.join(rootDir, 'www/latest/vendor')
            // other paths here but removed
        }
    },
    output: {
        path: path.join(rootDir, 'www/latest/dist/'),
        publicPath: 'CENSOREDPATH',
        filename: 'api.js',
        devtoolModuleFilenameTemplate: 'applicationname://[resource-path]',
        devtoolFallbackModuleFilenameTemplate: 'applicationname://[resource-path]?[hash]'
    },
    module: {
        rules: [
            {
                enforce: 'pre',
                test: /\.js$/,
                loader: 'eslint-loader'
            },
            {
                test: /\.js$/,
                exclude: function (resource) {
                    const es6Vendors = [
                        'vendor/censored/src',
                        'vendor/babel'
                    ];
                    const isNodeModule = matchModule(resource, 'node_modules');
                    const isVendor = matchModule(resource, 'vendor');
                    const isEs6Vendor = (es6Vendors.filter(vendor => matchModule(resource, vendor)).length >= 1);

                    if (isNodeModule || (isVendor && !isEs6Vendor)) {
                        return true;
                    }

                    return false;
                },
                loader: 'babel-loader'
            }
        ]
    },
    externals: {
        'react/addons': true,
        'react/lib/ExecutionEnvironment': true,
        'react/lib/ReactContext': true
    },
    node: {
        setImmediate: false,
        clearImmediate: false
    }
});

=========================================================================

This is the output from the console:

Version: webpack 3.6.0
Time: 39ms
webpack: Compiled successfully.
    webpack: Compiling...
06 10 2017 07:30:01.789:WARN [karma]: No captured browser, open http://localhost:9876/
    Hash: 020b93689ec1147a0c3b
Version: webpack 3.6.0
Time: 70ms
Asset     Size  Chunks             Chunk Names
tests/integration/integrationTests.js  3.96 kB       0  [emitted]  tests/integration/integrationTests.js
    [0] ./tests/integration/integrationTests.js 238 bytes {0} [built]
    [1] ./tests/integration models\/item.spec.js$ 192 bytes {0} [built]
    [2] ./tests/integration/specs/models/item.spec.js 612 bytes {0} [optional] [built]
webpack: Compiled successfully.
06 10 2017 07:30:01.799:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
    06 10 2017 07:30:01.800:INFO [launcher]: Launching browser Chrome with unlimited concurrency
06 10 2017 07:30:01.812:INFO [launcher]: Starting browser Chrome
06 10 2017 07:30:02.238:INFO [Chrome 61.0.3163 (Linux 0.0.0)]: Connected on socket mL2CqoCdzxFggtuIAAAA with id 72267743
Chrome 61.0.3163 (Linux 0.0.0) LOG LOG: '=================', './specs/models/item.spec.js', '================='
LOG LOG: 'SETTING TIMEOUT'
LOG LOG: 'INSIDE IT STATEMENT'

Test
simple test -
Test
simple test -
✗ should wait for done and set flag correctly
Expected false to be true.
    at UserContext.<anonymous> (tests/integration/integrationTests.js:121:30)

✗ should wait for done and set flag correctly
Expected false to be true.
    at UserContext.<anonymous> (tests/integration/integrationTests.js:121:30)


Chrome 61.0.3163 (Linux 0.0.0): Executed 2 of 1 (2 FAILED) ERROR (0.007 secs / 0.003 secs)

The notable errors from the aforementioned console errors are:

  1. Uncaught TypeError: Cannot read property 'spies' of undefined
  2. Uncaught Error: Tried to complete the wrong suite

And the full output:

================= ./specs/models/item.spec.js =================
integrationTests.js:120 asdfasdf

debug.js:15 FAILED Test simple test - Test simple test - should wait for done and set flag correctly

debug.js:15 FAILED Test simple test - should wait for done and set flag correctly

2debug.js:6 Skipped 0 tests

integrationTests.js:120 asdfasdf
debug.js:21 Uncaught Expected false to be true.
    at UserContext.<anonymous> (http://karma.vg.censored.com/base/tests/integration/integrationTests.js:121:26)

    (anonymous) @ debug.js:21
setTimeout (async)
(anonymous) @ debug.js:20
window.__karma__.result @ debug.js:23
KarmaReporter.specDone @ adapter.js:243
dispatch @ jasmine.js:4366
(anonymous) @ jasmine.js:4337
specResultCallback @ jasmine.js:1175
complete @ jasmine.js:530
(anonymous) @ jasmine.js:4231
channel.port1.onmessage @ jasmine.js:1774
debug.js:21 Uncaught Expected false to be true.
    at UserContext.<anonymous> (http://karma.vg.censored.com/base/tests/integration/integrationTests.js:121:26)
    (anonymous) @ debug.js:21
setTimeout (async)
(anonymous) @ debug.js:20
window.__karma__.result @ debug.js:23
KarmaReporter.specDone @ adapter.js:243
dispatch @ jasmine.js:4366
(anonymous) @ jasmine.js:4337
specResultCallback @ jasmine.js:1175
complete @ jasmine.js:530
(anonymous) @ jasmine.js:4231
channel.port1.onmessage @ jasmine.js:1774


jasmine.js:1024 Uncaught TypeError: Cannot read property 'spies' of undefined
at currentSpies (jasmine.js:1024)
at SpyRegistry.clearSpies (jasmine.js:4848)
at clearResourcesForRunnable (jasmine.js:818)
at Spec.specResultCallback [as resultCallback] (jasmine.js:1173)
at QueueRunner.complete [as onComplete] (jasmine.js:530)
at jasmine.js:4231
at MessagePort.channel.port1.onmessage (jasmine.js:1774)
currentSpies @ jasmine.js:1024
SpyRegistry.clearSpies @ jasmine.js:4848
clearResourcesForRunnable @ jasmine.js:818
specResultCallback @ jasmine.js:1173
complete @ jasmine.js:530
(anonymous) @ jasmine.js:4231
channel.port1.onmessage @ jasmine.js:1774


jasmine.js:1024 Uncaught TypeError: Cannot read property 'spies' of undefined
at currentSpies (jasmine.js:1024)
at SpyRegistry.clearSpies (jasmine.js:4848)
at clearResourcesForRunnable (jasmine.js:818)
at nodeComplete (jasmine.js:955)
at QueueRunner.onComplete (jasmine.js:5327)
at jasmine.js:4231
at MessagePort.channel.port1.onmessage (jasmine.js:1774)
currentSpies @ jasmine.js:1024
SpyRegistry.clearSpies @ jasmine.js:4848
clearResourcesForRunnable @ jasmine.js:818
nodeComplete @ jasmine.js:955
onComplete @ jasmine.js:5327
(anonymous) @ jasmine.js:4231
channel.port1.onmessage @ jasmine.js:1774


jasmine.js:951 Uncaught Error: Tried to complete the wrong suite
at nodeComplete (jasmine.js:951)
at QueueRunner.onComplete (jasmine.js:5327)
at jasmine.js:4231
at MessagePort.channel.port1.onmessage (jasmine.js:1774)
nodeComplete @ jasmine.js:951
onComplete @ jasmine.js:5327
(anonymous) @ jasmine.js:4231
channel.port1.onmessage @ jasmine.js:1774


jasmine.js:1024 Uncaught TypeError: Cannot read property 'spies' of undefined
at currentSpies (jasmine.js:1024)
at SpyRegistry.clearSpies (jasmine.js:4848)
at clearResourcesForRunnable (jasmine.js:818)
at QueueRunner.onComplete (jasmine.js:984)
at jasmine.js:4231
at MessagePort.channel.port1.onmessage (jasmine.js:1774)
currentSpies @ jasmine.js:1024
SpyRegistry.clearSpies @ jasmine.js:4848
clearResourcesForRunnable @ jasmine.js:818
(anonymous) @ jasmine.js:984
(anonymous) @ jasmine.js:4231
channel.port1.onmessage @ jasmine.js:1774


jasmine.js:2373 Uncaught Uncaught TypeError: Cannot read property 'spies' of undefined

I have spent many hours on this now and have no clue, can anyone advise please?

Upvotes: 2

Views: 1807

Answers (2)

Wojciech K
Wojciech K

Reputation: 1061

If you have found this question by searching for Uncaught Error: Tried to complete the wrong suite:

  1. See if there are any other errors in your code. Do this by setting singleRun: true at the top level of karma configuration and take a look at the console in the browser.

  2. See if maybe you have accidentally imported jasmine twice. This may happen because:

  • in addition to adding jasmine to frameworks in karma config, you have added it to files
  • one of the plugins you are using is already loading jasmine, so you are needlessly specifying jasmine under frameworks. Example of such a plugin: @metahub/karma-jasmine-jquery

Upvotes: 1

Navjot Ahuja
Navjot Ahuja

Reputation: 1171

I guess timeouts are not supported right now in jasmine-node(Issue: https://github.com/mhevery/jasmine-node/issues/344)

You can use promises. Like this:

describe('Test', function() {
  let flag = false;
  let beforeEachPromise;
  describe('simple test -', function() {
    beforeEach(function(done) {
      console.log('SETTING TIMEOUT');
      beforeEachPromise = new Promise(function(resolve, reject) {
        setTimeout(function() {
          console.log('TIMEOUT CALLBACK TRIGGERED');
          flag = true;
          done();
        }, 2000);
      })
    });
    it('should wait for done and set flag correctly', function() {
      beforeEachPromise.then(function() {
        expect(flag).toBe(true);
      })
    });
  });
});

Upvotes: 1

Related Questions