Mock 'ora' with Jest to see if it was called - javascript

I'm using jest and trying to Mock the ora package's spinner.start to see if it was called. I was trying the following as I think they have done it in another post here
My code (lala.js):
import ora from 'ora';
const spinner = ora({ indent: 2 });
export const lala = () => {
spinner.start('dfsdfsdf');
return 'hey';
};
export default lala();
test file:
import { lala } from './lala';
import ora from 'ora';
jest.mock('ora', () => () => {
const start = jest.fn();
const result = { start };
return result;
});
describe('lala', () => {
it.only('calls start', () => {
lala();
const spinner = ora();
expect(spinner.start).toHaveBeenCalled();
});
});
The error i'm getting from Jest is that it wasn't called as show in the following code block:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
I have tried so many things and importing/mocking this many ways but can't seem to get it to work. Eventually, I want to do toHaveBeenCalledWith('dfsdfsdf') but I can't even mock this correctly yet. Any help would be appreciated. I am using the following versions of Jest and Ora:
"jest": "24.8.0",
"ora": "3.2.0",
Thanks in advance.

I managed to get a working solution. I solved this by creating a helper function which initialises the ora object and is used in lala.js.
import ora from 'ora';
export const spinner = ora({ indent: 2 });
that way I can import the object in my test file and spy on start and then use the spy in my expectation like so:
import { spinner } from './utils/ora-helper';
export const lala = () => {
spinner.start('dfsdfsdf');
return 'hey';
};
export default lala();
import { lala } from './lala';
import { spinner } from './utils/ora-helper';
describe('lala', () => {
it.only('calls start', () => {
const spinnerSpy = jest.spyOn(spinner, 'start');
lala();
expect(spinnerSpy).toHaveBeenCalled(); // works like a charm
});
});

Related

Unit Test Tauri / VannilaJS / Mocha Chaï : ReferenceError: navigator is not defined

It's the first application i try to make with unit tests.
Stack : Vannilla JS, Tauri, Mocha/Chaï
I try do unit test on my app but i have some difficulties :
No problem when what i test don't use the Tauri API ben when i want unit test some code with import of Tauri API i got an error :
ReferenceError: navigator is not defined
I had search on the official Tauri docs, here on Stack Overflow and on Google and i found no awser.
Thanks in advance for the help and sorry for my poor english.
fileToTest.js
import {readFile} from "../helper.js";
import {state} from "../model.mjs";
export async function importFile(selectedFiles) {
const fileContent= await readFile(selectedFiles);
state.dataTmp.push(fileContent);
}
export async function analyseData() {
state.dataTmp.forEach(function(lines) {
const tmp = lines.split('\n');
if ( tmp[tmp.length - 1 ] === '' ) {
tmp.pop();
}
tmp.forEach(function (line) {
state.data.push(line)
});
});
state.dataTmp= [];
}
helper.js
import {fs} from "#tauri-apps/api";
export async function readFile(selectFile) {
let data = [];
if (Array.isArray(selectFile)) {
for (const file of selectFile) {
let contenuFichier = await fs.readTextFile(file);
data.push(contenuFichier);
}
}
return data
}
fileToTest.test.js
import {analyseBandeGEST} from "fileToTest.js";
import {state} from "model.mjs";
import {assert} from "chai";
it('Test my function', function () {
state.dataTmp= [
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
""
]
analyseData();
assert.equal(state.dataTmp.length, 0);
assert.equal(state.data.length, 2);
});

How to spyOn an exported standalone function using javascript jest?

It is a very simple scenario but I've struggled to find an answer for it.
helpers.ts:
export function foo() {
bar();
}
export function bar() {
// do something
}
helpers.spec.ts:
import { foo, bar } from "./helpers";
describe("tests", () => {
it("example test", () => {
const barSpy = // how can i set this up?
foo();
expect(barSpy).toHaveBeenCalled();
});
});
I can't do const spy = jest.spyOn(baz, 'bar'); because I don't have a module/class to put in place of "baz". It is just an exported function.
Edit:
Jest mock inner function has been suggested as a duplicate but unfortunately it doesn't help with my scenario.
Solutions in that question:
Move to separate module: I cannot do this for my scenario. If I am testing every function in my application, this would result in me creating 10s of new files which is not ideal. (To clarify, I think this solution would work but I cannot use it for my scenario. I am already mocking a separate file function successfully in this test file.)
Import the module into itself:
helpers.spec.ts:
import * as helpers from "./helpers";
describe("tests", () => {
it("example test", () => {
const barSpy = jest.spyOn(helpers, 'bar');
foo();
expect(barSpy).toHaveBeenCalled();
});
});
results in:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
This is the closed solution:
export function bar() {
// do something
}
export function foo() {
exports.bar(); // <-- have to change to exports.bar() instead of bar()
// or this.bar(); would also work.
}
import * as utils from './utils';
describe('tests', () => {
it('example test', () => {
const barSpy = jest.spyOn(utils, 'bar');
utils.foo();
expect(barSpy).toHaveBeenCalled();
});
});
Or take a look this duplicated question

How to use an async function as the second parameter to jest.mock?

I need to use jest.mock together with async, await and import() so that I can use a function from another file inside the mocked module. Otherwise I must copy and paste a few hundreds of slocs or over 1000 slocs, or probably it is not even possible.
An example
This does work well:
jest.mock('./myLin.jsx', () => {
return {
abc: 967,
}
});
Everywhere I use abc later it has 967 as its value, which is different than the original one.
This does not work:
jest.mock('./myLin.jsx', async () => {
return {
abc: 967,
}
});
abc seems to not be available.
Actual issue
I need async to be able to do this:
jest.mock('~/config', async () => {
const { blockTagDeserializer } = await import(
'../editor/deserialize' // or 'volto-slate/editor/deserialize'
);
// … here return an object which contains a call to
// blockTagDeserializer declared above; if I can't do this
// I cannot use blockTagDeserializer since it is outside of
// the scope of this function
}
Actual results
I get errors like:
TypeError: Cannot destructure property 'slate' of '((cov_1viq84mfum.s[13]++) , _config.settings)' as it is undefined.
where _config, I think, is the ~/config module object and slate is a property that should be available on _config.settings.
Expected results
No error, blockTagDeserializer works in the mocked module and the unit test is passed.
The unit test code
The code below is a newer not-working code based on this file on GitHub.
import React from 'react';
import renderer from 'react-test-renderer';
import WysiwygWidget from './WysiwygWidget';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';
const mockStore = configureStore();
global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
global.__CLIENT__ = false; // eslint-disable-line no-underscore-dangle
jest.mock('~/config', async () => {
const { blockTagDeserializer } = await import(
'../editor/deserialize' // or 'volto-slate/editor/deserialize'
);
const createEmptyParagraph = () => {
return {
type: 'p',
children: [{ text: '' }],
};
};
return {
settings: {
supportedLanguages: [],
slate: {
elements: {
default: ({ attributes, children }) => (
<p {...attributes}>{children}</p>
),
strong: ({ children }) => {
return <strong>{children}</strong>;
},
},
leafs: {},
defaultBlockType: 'p',
textblockExtensions: [],
extensions: [
(editor) => {
editor.htmlTagsToSlate = {
STRONG: blockTagDeserializer('strong'),
};
return editor;
},
],
defaultValue: () => {
return [createEmptyParagraph()];
},
},
},
};
});
window.getSelection = () => ({});
test('renders a WysiwygWidget component', () => {
const store = mockStore({
intl: {
locale: 'en',
messages: {},
},
});
const component = renderer.create(
<Provider store={store}>
<WysiwygWidget
id="qwertyu"
title="My Widget"
description="My little description."
required={true}
value={{ data: 'abc <strong>def</strong>' }}
onChange={(id, data) => {
// console.log('changed', data.data);
// setHtml(data.data);
}}
/>
</Provider>,
);
const json = component.toJSON();
expect(json).toMatchSnapshot();
});
What I've tried
The code snippets above show partially what I have tried.
I searched the web for 'jest mock async await import' and did not found something relevant.
The question
If jest.mock is not made to work with async, what else can I do to make my unit test work?
Update 1
In the last snippet of code above, the line
STRONG: blockTagDeserializer('strong'),
uses blockTagDeserializer defined here which uses deserializeChildren, createEmptyParagraph (which is imported from another module), normalizeBlockNodes (which is imported from another module) and jsx (which is imported from another module) functions, which use deserialize which uses isWhitespace which is imported from another module and typeDeserialize which uses jsx and deserializeChildren.
Without using await import(...) syntax how can I fully mock the module so that my unit test works?
If you want to dig into our code, please note that the volto-slate/ prefix in the import statements is for the src/ folder in the repo.
Thank you.
I'd advise not doing any "heavy" stuff (whatever that means) in a callback of jest.mock, – it is designed only for mocking values.
Given your specific example, I'd just put whatever the output of blockTagDeserializer('strong') right inside the config:
jest.mock('~/config', () => {
// ...
extensions: [
(editor) => {
editor.htmlTagsToSlate = {
STRONG: ({ children }) => <strong>{children}</strong>, // or whatever this function actually returns for 'strong'
};
return editor;
},
],
// ...
});
This doesn't require anything asynchronous to be done.
If you need this setup to be present in a lot of files, extracting it in a setup file seems to be the next best thing.
I found a solution. I have a ref callback that sets the htmlTagsToSlate property of the editor in the actual code of the module, conditioned by global.__JEST__ which is defined as true in Jest command line usage:
import { htmlTagsToSlate } from 'volto-slate/editor/config';
[...]
testingEditorRef={(val) => {
ref.current = val;
if (val && global.__JEST__) {
val.htmlTagsToSlate = { ...htmlTagsToSlate };
}
}}
Now the jest.mock call for ~/config is simple, there is no need to do an import in it.
I also use this function:
const handleEditorRef = (editor, ref) => {
if (typeof ref === 'function') {
ref(editor);
} else if (typeof ref === 'object') {
ref.current = editor;
}
return editor;
};

Jest: How to mock custom module that is exported from an index.js file?

This is the structure of my project (create-react-app):
Contents of /src/api/searchAPI.js:
import client from './client';
async function searchMulti(query, options = {}) {
options.query = query;
return await client.get('/search/multi', options);
}
export default {
searchMulti
};
Contents of /src/api/index.js:
import movieAPI from './movieAPI';
import personAPI from './personAPI';
import searchAPI from './searchAPI';
import configurationAPI from './configurationAPI';
export { movieAPI, personAPI, searchAPI, configurationAPI };
QuickSearch component imports searchAPI ands uses it to fetch some data over the web.
Now, I need to test (with react-testing-library) the QuickSearch component.
So, I would like to mock the api module (exported in /src/api/index.js) in order to use a mock function instead of searchAPI.searchMulti( ).
If I put below code in /src/componentns/__tests__/QuickSearch.js, it works just fine:
...
import { searchAPI } from '../../
...
...
jest.mock('../../api', () => {
return {
searchAPI: {
searchMulti: jest.fn().mockResolvedValue({ results: [] })
}
};
});
...
it('some test', () => {
searchAPI.searchMulti.mockResolvedValueOnce({ results: [] });
const { queryByTitle, getByPlaceholderText } = renderWithRouter(
<QuickSearch />
);
const input = getByPlaceholderText(/Search for a movie or person/i);
expect(searchAPI.searchMulti).not.toHaveBeenCalled();
act(() => {
fireEvent.change(input, { target: { value: 'Aladdin' } });
});
expect(searchAPI.searchMulti).toHaveBeenCalledTimes(1);
});
My problem is that I don't want to mock api in every test file that needs it. Instead, I would like to put api in a __mocks__ folder so that other tests can use it you, too.
How can I do that?

Jasmine Data Provider is not working (jasmine_data_provider_1.using is not a function)

I am trying to achieve Data Driven testing in my project by using jasmine data providers.
I have a data.ts file like below
export const hardshipTestData = {
scenarios: {
scenario1: {
isHome: 'Yes'
},
scenario2: {
isHome: 'No'
}
}
};
I am using above data in spec file
import { using } from 'jasmine-data-provider';
import { hardshipTestData } from '../../data/testdata';
using(hardshipTestData.scenarios, function (data, description) {
it('testing data providers', () => {
console.log(data.isHome);
});
});
My issue here is when I am trying to write data. intelligence is not even giving the option isHome. When I enforce it and run the test I am getting the following error
TestSuite encountered a declaration exception
configuration-parser.js:48
- TypeError: jasmine_data_provider_1.using is not a function
any help is appreciated
You need to change import type. Try to replace:
import { using } from 'jasmine-data-provider';
with:
const using = require('jasmine-data-provider');
Also, keep in mind that firstly should be describe block:
describe('example test', () => {
using(hardshipTestData.scenarios, (data) => {
it('should calc with operator -', () => {
console.log(data.isHome);
});
});
});
Adding to Oleksii answer, his answer is for typescript.
but If you want to use in plain javascript use below:
Add below in your code:
var using = require('jasmine-data-provider');
Example:
var jasminedatasetobj = require("./jasmineDataDrivenData");
var using = require('jasmine-data-provider');
using(jasminedatasetobj.datadrive, function (data, description) {
it('Open NonAngular js website Alerts', async() => {
await browser.get("https://qaclickacademy.github.io/protocommerce/");
element(by.name("name")).sendKeys(data.name);
});
});
You might need to give full path of Jasmine data provider for plain javascripts to avoid module not found error.
var jsondataobj = require('../../../../config/Jsoninput.json');//define the data source location
var using = require('C:/Users/sam/AppData/Roaming/npm/node_modules/jasmine-data-provider');
describe("Test Jasmine Data provider",function(){
you need to declare the variable for "jasmine-data-provider" , because import can use to import the properties/classes.
instead of using variable you can give any name to the varible (I tried use "post" instead of "using" and it is still working as expected)
your code should be like
import { hardshipTestData } from "../Test";
const using = require("jasmine-data-provider");
describe("Login TestCases", () => {
using(hardshipTestData.scenarios, (alldata: any, alldesc: any) => {
it("login with different credentials", async () => {
console.log(data.isHome);
})
})
})
this will resolve you problem.

Categories

Resources