Browser history needs a DOM in my tests with jest - unit-testing

When I try to test my sagas I get the following error:
Invariant failed: Browser history needs a DOM
and test suite failed to run
It happens because I use function redirectTo() in my sagas
This function exports from this file:
import createHistory from 'history/createBrowserHistory';
export const history = createHistory();
export const redirectTo = (path: string): void => {
history.push(path);
};
How can I solve this problem? I need emulate DOM?
This error occurs even if I use saga which does not contain redirectTo() function

according to JEST documentation you need to set the testEnvironment variable to jsdom by adding it to jest.config file for all tests or a comment in the .spec file for the single suite
For single file add this comment at the top:
/**
* #jest-environment jsdom
*/
or to set the jsdom environment for all tests add this:
jest.config.js
module.exports = {
testEnvironment: 'jest-environment-jsdom',
// ... other options ...
}

Related

How to skip mocked image and get real image attributes with Jest?

I am writing unit tests and I want to test an img atrributes. However, when I make an assertion, I get mocked img attributes which is under __mocks__>fileMock.js. Because I mock files, I only get mocked file atrributes. How can I skip mocked files in my tests?
describe('Buttons', () => {
test('Clear filters work after filtering tools, workflows and using searchbar', async () => {
render(<HeaderBar {...defaults} />);
const clearFilterButton = screen.getByRole('button', { name: 'Clear Filters' });
const toolsButton = screen.getByRole('button', { name: 'TOOLS' });
const selectedTools = await within(toolsButton).findByRole('img');
expect(selectedTools).toHaveAttribute('src', 'images/tools-selected.png');
});
And test result is :
Buttons › Clear filters work after filtering tools, workflows and using searchbar
expect(element).toHaveAttribute("src", "images/tools-selected.png") // element.getAttribute("src") === "images/tools-selected.png"
Expected the element to have attribute:
src="images/tools-selected.png"
Received:
src="test-file-stub"
39 | const toolsButton = screen.getByRole('button', { name: 'TOOLS' });
40 | const selectedTools = await within(toolsButton).findByRole('img');
> 41 | expect(selectedTools).toHaveAttribute('src', 'images/tools-selected.png');
I need to test real image, and skip mocking that img in my test.
It sounds like you have a manual user module mock defined in a __mock__ folder next to your actual code.
In that case the mock is used in any test file where you call jest.mock('moduleName') unless automock is set to true in which case the mock will always be used.
If you are explicitly mocking the file using jest.mock('moduleName') then simply remove that from the test file where you want to use the actual code instead of the mock.
If you have automock set to true in your Jest config then you can tell Jest to use the original code file in a given test by using jest.unmock('moduleName').

Is there a way to use global jest mocks with Quasar?

I want to mock my i18n object globally.
I'm following a guideline. I'm creating jest.init.ts file inside my jest folder. It contains following rules:
import { config } from "#vue/test-utils"
config.mocks["$t"] = () => ""
But my tests fail with the error:
TypeError: _vm.$t is not a function
I've also tried to import quasar implementation of vue-test utils ('#quasar/quasar-app-extension-testing-unit-jest'), but the result is the same
I found this https://testing.quasar.dev/packages/unit-jest/. Here is a section for mocking i18n.

How to mock global Vue.js variable in JEST test

I have a global property/variable with my app urls:
Vue.prototype.$apiUrls = {
root: 'http://localhost:8080/',
api: 'api/v1/'
// etc.
}
I use it inside my components as axios request:
axios.get(`${this.$apiUrls.root}${this.$apiUrls.api}/users/`)
Now I want to test my component's code, I've mocked axios already, but still I receive an error:
TypeError: Cannot read property '$apiUrls' of undefined
I've tried to define/mock this property inside each test and/or in JEST's setup file, like e.g.
global.$apiUrls = {...}
// or
Vue.prototype.$apiUrls = {...}
// or
Object.defineProperties(Vue.prototype, {$apiUrls: {...}})
I've also tried mocking it to window or this (yeah, thats silly), but with no success - I still receive that error - please help.
There is two ways to achieve this. One is using the Config option, as mentioned by #Aldarund. You can read about it here.
If you are using Jest, I recommend doing this in the jest.init.js file:
import { config } from '#vue/test-utils'
config.mocks['$apiUrls'] = {
'some/endpoint'
}
Then add this to the jest section of your package.json:
"setupFiles": [
"<rootDir>/jest.init.js"
]
Now it is globally mocked. If you want to do this on a per test basis, you can use the mocks mounting option:
const wrapper = shallowMount(Foo, {
mocks: {
$apiUrls: 'some/endpoint'
}
})
Hopefully this helps!
If you are interested I am compiling a collection of simple guides on how to test Vue components here. It's under development, but feel free to ask make an issue if you need help with other related things to testing Vue components.
I don't think the answers above work anymore (in 2020).
Here's what worked for me:
For vue-test-utils 1.x.x (Vue 2)
Create a new file, name it eg. jest.init.js
Give it the following content:
import { config } from "#vue/test-utils";
config.mocks["yourGlobalProperty"] = label => label; //you can replace it with your own mock
Add this to your jest.config.js (actually write "rootDir", don't replace anything with a real path)
module.exports = {
setupFiles: ["<rootDir>/jest.init.js"]
}
These files will be only ran before jest runs unit tests.
Note that I'm importing {config}, not the default export. I don't know why the default didn't work for me. Even the documentation for vue test utils doesn't import the default export anymore
Also make sure you're not trying to import from the old vue-test-utils package. (The new one is #vue/test-utils)
For #vue/test-utils 2.x.x (vue-test-utils-next) (Vue 3)
Follow steps like for 1.x.x above, but in step two, do this instead:
import { config } from "#vue/test-utils"; //2.0.0-beta.5
config.global.mocks = {
yourGlobalProperty: label => label
};
You can do it with vue-test-utils beta 15 and later.
Here docs
And some example would be:
import VueTestUtils from '#vue/test-utils'
VueTestUtils.config.mocks['$apiUrls'] = {
...
}

Jest globalSetup option not working

I'm trying to make a function called loadFixtures available to all Jest tests.
I have the following line within the jest config object inside package.json:
"globalSetup": "<rootDir>/src/test/js/config/setup-globals.js"
setup-globals.js contains:
module.exports = function() {
function loadFixtures(filename) {
console.info('loadFixtures is working');
}
}
Within my tests I have, for example:
beforeEach(() => {
loadFixtures('tooltip-fixture.html');
});
However when I run Jest I get the following for each test:
ReferenceError: loadFixtures is not defined
I verified that the setup-globals.js file is definitely being found and loaded in by Jest before the tests execute.
Can anyone assist in identifying where I've gone wrong here? I've spent pretty much an entire day trying to debug without luck.
You should be using setupFiles and not globalSetup.
// jest config
"setupFiles": [
"<rootDir>/src/test/js/config/setup-globals.js"
]
then src/test/js/config/setup-globals.js:
global.loadFixtures(filename) {
console.info('loadFixtures is working');
}
references: https://medium.com/#justintulk/how-to-mock-an-external-library-in-jest-140ac7b210c2
If you bootstrapped your application using npx create-react-app (CRA), you do not need to add the setupFiles key under your jest key in the package.json file (CRA prevents overriding that key).
what you simply need to do is to add the file setupTests.js in the root of your SRC folder, and populate it with the snippet below:
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({
adapter: new Adapter(),
});
remember you must have earlier installed the right versions of enzyme and enzyme-adapter-react
CRA has been wired to automatically load the setupTests.js file in the src folder if it exists. Hence after adding these, you can then go over to your test and do import {shallow} from enzyme without triggering an error.
if you are not using Create-react-app, all you need to do, in addition to adding the file above to your src folder is to add the key setupFiles into the jest key in your package.json. it should look like this:
"jest": {
"setupFiles": ['<rootDir>/src/setupTests.js'],
}
and you are good to go.
Cheers!
You're defining a function in a different scope. How about you create a separate module and import it directly in your test files. Or if you really want to define it in the global scope, try using the following code in your setup-globals.js file.
module.exports = function() {
global.loadFixtures = function(filename) {
console.info('loadFixtures is working');
}
}

Testing service in Angular returns module is not defined

I am trying to run the default service unit test in my project (Taken from the Angular Seed project on GitHub), but I keep getting the error "module is not defined".
I have read that it could be something to do with the order of the referenced JavaScript files, but I can't seem to get it to work, so hopefully one of you might be able to help.
My configuration for the test looks like this:
basePath = '../';
files = [
'public/javascripts/lib/jquery-1.8.2.js',
'public/javascripts/lib/angular.js',
'public/javascripts/lib/angular-.js',
'public/app.js',
'public/controllers/.js',
'public/directives.js',
'public/filters.js',
'public/services.js',
JASMINE,
JASMINE_ADAPTER,
'public/javascripts/lib/angular-mocks.js',
'test/unit/*.js' ];
autoWatch = true;
browsers = ['Chrome'];
junitReporter = { outputFile: 'test_out/unit.xml', suite: 'unit'
};
The service looks like the following:
angular.module('myApp.services', []).
value('version', '0.1');
The test looks like this:
'use strict';
describe('service', function() {
beforeEach(module('myApp.services'));
describe('version', function() {
it('should return current version', inject(function(version) {
expect(version).toEqual('0.1');
}));
});
});
And the error when running the test through testacular is this:
ReferenceError: module is not defined
You are missing the angular-mocks.js file.
I had the same problem, and I understood why it wasn't working:
The jasmine.js javascript must be referenced BEFORE the angular-mocks.js file.
Indeed, the angular-mocks.js checks if Jasmine is loaded, and only if it is it will add the module function to the window.
Here is an extract of Angular Mocks code:
(Edit after the few comments about 'hacking' I had below: this is just an extract of the code, this is not something you need to write yourself, it's already there!)
window.jasmine && (function(window) {
[...]
window.module = angular.mock.module = function() {
var moduleFns = Array.prototype.slice.call(arguments, 0);
return isSpecRunning() ? workFn() : workFn;
/////////////////////
[...]
};
In a nutshell:
Just reference your jasmine.js before angular-mocks.js and off you go.
The window.module function comes in angular-mocks.js and is a shorthand for angular.mock.module. As mentioned in the docs, the module function only works with Jasmine.
Using Testacular, the following example configuration file will load angular-mocks.js.
/** example testacular.conf.js */
basePath = '../';
files = [
JASMINE,
JASMINE_ADAPTER,
'path/to/angular.js',
'path/to/angular-mocks.js', // for angular.mock.module and inject.
'src/js/**/*.js', // application sources
'test/unit/**/*.spec.js' // specs
];
autoWatch = true;
browsers = ['Chrome'];
And, as suggested elsewhere, you can run Testacular with debug logging to see what scripts are loaded (you can also see the same in the inspector):
testacular --log-level debug start config/testacular.conf.js
The angular.mock.inject docs include a pretty complete example.
We use 'module' without 'angular' in our unit tests and it works fine.
CoffeeScript:
describe 'DiscussionServicesSpec', ->
beforeEach module 'DiscussionServices'
beforeEach inject ... etc.
which compiles to
JavaScript:
describe('DiscussionServices', function() {
beforeEach(module('DiscussionServices'));
beforeEach(inject(function ... etc.
The only time I see something like the error you described is if in the testacular.conf.js file the angular-mocks.js file is not listed in the files section before the specs trying to use 'module'. If I put it after my tests in the 'files' list I get
ReferenceError: Can't find variable: module
(Our tests are being run through PhantomJS)
I had included angular-mocks.js in my karma config, but was still getting the error. It turns out the order is important in the files array. (duh) Just like in the head of an html doc, if a script calls angular before it's defined, and error occurs. So I just had to include my app.js after angular.js and angular-mocks.js.
If you're using Yeoman and its angular-generator, you probably get this error. Especially when you do the Tutorial ( ._.)
I fixed it, by copying the angular-mocks.js file, from the bower_components/angular-mocks dir to the test/mock dir. Of course you have to be sure, that your karma.conf.js file is configured correctly.
Greetings!
I had this same issue when I was doing something like var module = angular.module('my',[]). I needed to make sure it was surrounded by IIFE