I find myself writing this at the start of pretty much all of my unit tests in mocha:
it('should do something', (done) => {
Vue.config.errorHandler = done;
// do something aynchronous
});
By default, Vue catches all errors itself and logs them to the console, so mocha can't see them. This code makes sure that thrown errors fail the tests.
Is there a way with mocha to do this without having to start every single async test with this line of code? If I have to write / use a plugin, that's fine.
Try:
Vue.config.errorHandler = function (err, vm, info) {
throw err
}
in your test entry.
Related
According to documentation, this hook should only execute once per suite run (once every cypress run call), but this seems to be executed before every single spec. Is this a bug? Or am I doing something wrong?
Cypress.Cookies.defaults({
preserve: ['cookie']
})
before(() => {
cy.log("Should only run once")
})
});
The before() hook runs once per spec file, not once per cypress run.
It looks like you want the Before Run API
on('before:run', (details) => {
/* ... */
})
I have the following test for a custom Cypress command of mine:
it('can navigate to a url', () => {
const history = createHistory();
cy.window().then(win => ((win as any).__chh__history__ = history));
cy.spy(history, 'push');
history.listen((location, action) => {
expect(action).to.equal('PUSH');
expect(location.pathname).to.equal(PATH);
expect(location.state).to.equal('foobar');
});
cy.navigate(PATH, 'foobar');
cy.wait(0).then(() => {
expect(history.push).to.have.been.called;
});
});
The command works fine, but if I remove the cy.wait(0), only doing the expect, then the test fails.
I assume it's because the cy.navigate command (added via Cypress.Commands.add) is queued, like other Cypress commands, meaning the expect actually runs before any of the Cypress commands have even started?
If so, that's fine and as expected of course, but is there a way in Cypress tests to queue things, without using cy.wait(0)?
For example when I'm checking elements, I can use cy.get(something).should($el => expect(...)), but in this case, there's nothing to "should on"... I could of course cy.get('body') or whatever, but I don't really want to get anything in this case. 🤔
The docs show use of a naked expect() after the spy has been established,
cy.spy(util, 'addListeners')
App.start()
expect(util.addListeners).to.be.called
but I think this is misleading, since expect() runs immediately but intervening Cypress commands may have inherent delays or reties, and can't be guaranteed to complete before the expect is executed.
Two other ways to test this are:
Ensure the expect is only run after your custom command
cy.navigate(PATH, 'foobar').then(() => {
expect(history.push).to.have.been.called;
})
Put the expectation into the Cypress command queue
cy.navigate(PATH, 'foobar');
cy.wrap(history).its('push').should('have.been.called')
I would like to be able to access all JS warnings and errors from the Browser Console during test runs. With the "-e" (skip JS errors) flag disabled, the test stops at the first error, so clearly it is looking for them. With this flag enabled, I would like to be able to see which errors (and ideally warnings) fired during test runs.
I've tried using the ClientFunction and window.onerror methods. I've also tried the -r 'reports' flag -- I only see TestCafe errors, not JS errors from the page under test. I've tried "Accessing Console Messages" from https://devexpress.github.io/testcafe/documentation/test-api/accessing-console-messages.html but this only gives messaging thrown by console.log, etc ("Note that this method returns only messages posted via the console.error, console.warn, console.log and console.info methods. Messages output by the browser (like when an unhandled exception occurs on the page) will not be returned.")
const installErrorHandler = ClientFunction(() => {
window.onerror = error => {
console.log("ERROR::::::");
console.log(error);
};
});
Over in the Clojure space, when using the Etaoin webdriver implementation (on Chrome or Phantom.js), at any point simply performing a
(get-logs driver)
returns
{:level :warning,
:message "1,2,3,4 anonymous (:1)",
:timestamp 1511449388366,
:source nil,
:datetime #inst "2017-11-23T15:03:08.366-00:00"}
....
....
Including any 'unhandled exceptions'.
Can I not do this in TestCafe?
Probably your example with window.onerror does not work because it executes later that error occurs. I suggest you extract the error handler into a separate *.js file and inject it using the new testcafe feature: Inject Scripts into Tested Pages.
Look at the following example:
script to inject (log-errors.js):
window.unhandledErrors = [];
window.onerror = (message, source, lineno, colno, error) => {
window.unhandledErrors.push({ message, source, lineno, colno, error });
}
test:
import { Selector } from 'testcafe';
fixture('Log errors')
.page('http://example.com')
.afterEach(async t => {
const errors = await t.eval(() => window.unhandledErrors);
console.log(errors);
});
test('test', async t => {
//...
});
Run tests by the following command:
testcafe chrome test.js -e --cs=./scripts/log-errors.js
Note that Script Injection feature is available since 1.4.0 version.
Since start(), stop() will be removed in Qunit 2.0, what is the alternative for async setups and teardowns via the beforeEach, afterEach methods? For instance, if I want the beforeEach to wait for a promise to be finished?
QUnit basically wants people to stop using the global methods (not just start() and stop(), but also test(), expect(), etc). So, as of version 1.16.0, you should always use either the global namespace (QUnit) or the assert API argument passed into the test() functions. This includes the new async control:
QUnit.test( "testing async action", function( assert ) { // <-- note the `assert` argument here
var done = assert.async(); // tell QUnit we're doing async actions and
// hold onto the function it returns for later
setTimeout(function() { // do some async stuff
assert.ok( true, "This happened 100 ms later!" );
done(); // using the function returned from `assert.async()` we
// tell QUnit we're don with async actions
}, 100);
});
If you are familiar with the old start() and stop() way of doing things, you should see that this is extremely similar, but more compartmentalized and extensible.
Because the async() method call is on the assert argument into the test, it cannot be used in the beforeEach() function. If you have an example of how you were doing that before, please post it and we can try to figure out how to git it into the new way.
UPDATE
My mistake previously, the assert object is being passed into the beforeEach and afterEach callbacks on modules, so you should be able to do the same logic that you would do for a test:
QUnit.module('set of tests', {
beforeEach: function(assert) {
var done = assert.async();
doSomethingAsync(function() {
done(); // tell QUnit you're good to go.
});
}
});
(tested in QUnit 1.17.1)
Seeing that nobody has answered the beforeEach/afterEach part: a test suite is supposed to run as soon as the page loads. When that is not immediately possible, then resort to configuring QUnit:
QUnit.config.autostart = false;
and continue with setting up your test suite (initializing tests, feeding them to QUnit, asynchronously waiting for some components to load, be it AJAX or anything else), your site, and finally, when it's ready, just run:
QUnit.start();
QUnit's docsite has it covered.
Ember Qunit, has once exists beforeEach/setup, afterEach/teardown co-exist for a little while.
See PR: https://github.com/emberjs/ember-qunit/pull/125
I try to create e2e tests with karma and jasmine with yeoman. In my karma-e2e.conf.js I add jasmine:
files = [
JASMINE,
JASMINE_ADAPTER,
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'test/e2e/**/*.js'
];
A need async testing so I need to use runs, waits, waitsFor (https://github.com/pivotal/jasmine/wiki/Asynchronous-specs)
But if I try to use it:
it('test', function () {
runs(function () {
...
});
});
Scenatio test runner returns this:
TypeError: Cannot call method 'runs' of null
at runs (http://localhost:8080/adapter/lib/jasmine.js:562:32)
at Object.<anonymous> (http://localhost:8080/base/test/e2e/eduUser.js:42:3)
at Object.angular.scenario.SpecRunner.run (http://localhost:8080/adapter/lib/angular-scenario.js:27057:15)
at Object.run (http://localhost:8080/adapter/lib/angular-scenario.js:10169:18)
I don't know where the problem is. Can you help me please?
Angular e2e tests with Karma don't and can't use the JASMINE adapter. Instead you have the ANGULAR_SCENARIO_ADAPTER which has a similar feel to writing Jasmine tests.
All commands in the adapter's API are asynchronous anyway. For example element('#nav-items').count() doesn't return a number, it returns a Future object. Future objects are placed in a queue and executed asynchronously as the runner progresses. To quote the API docs:
expect(future).{matcher}:
[...] All API statements return a future object, which get a value assigned after they are executed.
If you need to run your own asynchronous test code, you can extend the adapter's DSL, this is easier than it might sound. The idea is that you return your own Future which can be evaluated by a matcher such as toBe(). There are some examples on how to do this in the e2e-tests.js Gist from Vojta. Just remember to call done(null, myRetrunValue); when your test code is successful (myRetrunValue is the value evaluated by your matcher). Or done('Your own error message'); if you want the test to fail.
UPDATE: In response to question below. To simulate a login, first add a function called login to the dsl:
angular.scenario.dsl('login', function() {
return function(selector) {
// #param {DOMWindow} appWindow The window object of the iframe (the application)
// #param {jQuery} $document jQuery wrapped document of the application
// #param {function(error, value)} done Callback that should be called when done
// (will basically call the next item in the queuue)
return this.addFutureAction('Logging in', function(appWindow, $document, done) {
// You can do normal jQuery/jqLite stuff here on $document, just call done() when your asynchronous tasks have completed
// Create some kind of listener to handle when your login is complete
$document.one('loginComplete', function(e){
done(null, true);
}).one('loginError', function(e){
done('Login error', false);
});
// Simulate the button click
var loginButton = $document.find(selector || 'button.login');
loginButton.click();
})
};
});
And then call:
beforeEach( function()
{
expect( login('button.login') ).toBeTruthy();
});