I am trying to mock a library using jest.
Function "requestAuthorization" is just a swift function. The library 'my-health-library' uses Native Modules to access the "requestAuthorization" method.
Test file:
import MyHealthLibrary, { type } from 'my-health-library';
jest.mock('my-health-library', () => {
return {
MyHealthLibrary: {
__esModule: true,
default: {
requestAuthorization: jest.fn(() => Promise.resolve('the response')),
type: type
}
};
}
});
it('Testing to see if Library works', () => {
let read = [type.heartRate]
let write = [type.heartRate]
expect(MyHealthLibrary.requestAuthorization(read, write)).toBe(true)
})
This test keeps failing with "The module factory of jest.mock() is not allowed to reference any out-of-scope variables.".
I could mock my function "requestAuthorization" but how do i mock the type???
The "type" is just an enum
export enum type {
bodyMassIndex = 'HKQuantityTypeIdentifierBodyMassIndex',
heartRate = 'HKQuantityTypeIdentifierHeartRate',
bodyTemperature = 'HKQuantityTypeIdentifierBodyTemperature',
bloodPressureSystolic = 'HKQuantityTypeIdentifierBloodPressureSystolic',
bloodPressureDiastolic = 'HKQuantityTypeIdentifierBloodPressureDiastolic',
bloodGlucose = 'HKQuantityTypeIdentifierBloodGlucose'
}
According to the doc you can jest.requireActual('my-health-library'):
jest.mock('my-health-library', () => {
const { type } = jest.requireActual('my-health-library');
return {
MyHealthLibrary: {
__esModule: true,
default: {
requestAuthorization: jest.fn(() => Promise.resolve('the response')),
type
}
};
}
});
EDIT: however, given the way you import the module (import MHL, { type } from 'm-h-l') I think the export should be:
return {
__esModule: true,
default: {
// the default export, i.e. the MyHealthLibrary object of your import statement
requestAuthorization: jest.fn(() => Promise.resolve('the response'))
},
type // out of the default export, to import it as desconstructed { type }
};
Related
In my Vue 2.7.14 app I'm using Vuelidate 2.0.0. I'm trying to migrate a test from Jest to Vitest, but the v$ object is not initialised correctly in the latter case. The component has a checkbox bound to formData.accepted
validations () {
return {
formData: {
accepted: {
required,
sameAs: sameAs(true)
}
}
}
}
Vuelidate is initialised within the component as per the docs
setup () {
return {
v$: useVuelidate({ $autoDirty: true })
}
},
When I run the following test under Jest, it passes
it('click save button', async () => {
const wrapper = mount(MyComponent)
expect(wrapper.vm.v$.formData.accepted.$invalid).toBeTruthy()
await wrapper.find('[data-cy="accept-checkbox"]').trigger('click')
expect(wrapper.vm.v$.formData.accepted.$invalid).toBeFalsy()
})
However, if I run the same test using Vitest it fails because wrapper.vm.v$.formData is undefined because v$ is initialised to:
v$ {
"$dirty": false,
"$path": "__root",
"$model": null,
"$error": false,
"$errors": [],
"$invalid": false,
"$anyDirty": false,
"$pending": false,
"$silentErrors": [],
"$validationGroups": {}
}
By contrast, when the Jest test is run, $silentErrors is not empty, and the following property path is (obviously) valid
v$.formData.accepted.$invalid
What should I do to ensure that v$ is initialised correctly when the test is run with Vitest?
First try:
You need to assign a const to the wrapper:
it('click save button', async () => {
const wrapper = mount(MyComponent)
const { $v } = wrapper.vm;
expect(wrapper.vm.v$.formData.accepted.$invalid).toBeTruthy()
await wrapper.find('[data-cy="accept-checkbox"]').trigger('click')
expect(wrapper.vm.v$.formData.accepted.$invalid).toBeFalsy()
})
Further try:
I think you are also missing the import of validationMixin in your test file to use the $v object:
import { validationMixin } from 'vuelidate';
After that change your component to this:
export default {
mixins: [validationMixin],
setup () {
return {
v$: useVuelidate()
}
}
Glad about feedback if it worked and if not we'll get there. :)
To initialise Vuelidate when testing a Vue component with Vitest you can use the following approach:
1. Add the following line to your test file:
import Vuelidate from 'vuelidate';
2. Use Vuelidate in your test like so:
it('click save button', async () => {
const wrapper = mount(MyComponent)
const v = Vuelidate.instance()
expect(v.formData.accepted.$invalid).toBeTruthy()
await wrapper.find('[data-cy="accept-checkbox"]').trigger('click')
expect(v.formData.accepted.$invalid).toBeFalsy()
})
3. Make sure that Vuelidate is initialised correctly within your Vue component like so:
v$: useVuelidate({
$autoDirty: true
})
I want to mock window.location.search.
config.test.js
import config from './config'
import { MULTIPLE_VIDEOS } from './Constants/flagKeys'
describe('', () => {
const flag = { [MULTIPLE_VIDEOS]: true }
global.window = Object.create(window)
Object.defineProperty(window, 'location', {
value: {}
})
afterAll(() => {
global.window = null
})
it('Mark query string flag as true', () => {
global.window.location.search = `?${MULTIPLE_VIDEOS}=true`
expect(config.flags).toEqual(flag)
})
})
config.js
export default { flags: getFlagsFromQueryString() }
function getFlagsFromQueryString () {
const queryString = qs.parse(window.location.search.slice(1))
const flags = {}
Object.entries(queryString).forEach(([name, value]) => {
flags[name] = value.toLowerCase() === 'true'
})
return flags
}
Though I set search value in location object before calling config.flags, I can't access it inside the function, It always returns empty string.
I want window.location.search.slice(1) to return ?multipleVideos=true inside getFlagsFromQueryString function instead of empty string, since I changed the value in the test file.
One thing I noticed, when I export the function and call in the test file it works.
For this type of complex uses. I would recommend you to create a Window service/util class and expose methods from there. easy to test and mock.
Sample:
// Window.js
class Window {
constructor(configs) {}
navigate(href) {
window.location.href = href;
}
}
export default new Window({});
Now you can easily mock Window.js. It is similar to DI pattern.
Lets say I have an object with constants
const FETCH_DATA = {
REQUESTED: 'FETCH_DATA_REQUESTED',
}
And now I want to use them in another object
const fetchData = {
requested() {
return {
type: FETCH_DATA.REQUESTED
};
},
}
But I don't want to retype the whole constant name each time.
Is it possible to do destructure? Something like this, only this doesn't work.
const fetchData = {
{REQUESTED}: FETCH_DATA,
requested() {
return {
type: REQUESTED
}
}
}
I can't put it outside the object due to REQUESTED being too general for the global scope as I have multiple REQUESTED constants.
You can create a module (= file) containing your constants and then export them as constants:
FetchDataContants.ts
export const REQUESTED = 'REQUESTED';
export const MYCONST = 'just an example';
then in another fileā¦
import { REQUESTED, MYCONST } from './FetchDataConstants';
requested() {
return { type: REQUESTED }
}
or import all like so...
import * as c from './FetchDataConstants';
requested() {
return { type: c.REQUESTED }
}
When using a named export to return an object literal composed of functions, is it possible to pass a parameter to one of those functions?
For example, let's say the function below returns conditional results depending on if user's an admin:
// gridConfig.js
function getColumnDefs(isAdmin = false) {
// conditionally return columns
return {
orders: [ ... ],
...
}
}
export const config = {
columnDefs: getColumnDefs(),
rowDefs: getRowDefs(),
...
};
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config;
grid.columnDefs = columnDefs['orders'];
...
}
If I add the parameter to the function call inside the export, it says the param isn't defined. Adding a parameter to the export alias gives syntax errors. Even if it allowed this, I'm not clear where I'd pass my param inside main.js.
Is there some way of passing a parameter when structuring an export in this manner?
Maybe keeping it simple can be useful :)
export const config = (isAdmin) => ({
columnDefs: getColumnDefs(isAdmin),
rowDefs: getRowDefs(),
...
});
// Import
import { config } from '[...]'; // Placeholder path of import
const myConfigFalse = config(false);
const myConfigTrue = config(true);
export const config = admin => ({
columnDefs: getColumnDefs(admin),
rowDefs: getRowDefs(),
});
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config(admin);//get the admin variable set before this line
grid.columnDefs = columnDefs['orders'];
...
}
I know that all javascript is valid typescript but I'd like to start converting my javascript to typescript conventions. I'm battling this one snippet of JS:
My standard Javascript that works
if (MyCompany === undefined) {
var MyCompany = {};
}
MyCompany.Uploader = MyCompany.Uploader || {};
MyCompany.Uploader.Core = function (config) {
'use strict';
function build() {
console.log("building");
}
return {
build: build
};
};
var config = {this: "that};
MyCompany.Uploader.Core(config).build(); // outputs building to console
I've been messing with multiple approaches and I feel like I not close enough.
My failed attempt at converting to Typescript
namespace MyCompany.Uploader {
export var Core = (config:any) => {
function build() {
console.log("building");
}
};
}
let configobj = {here:"there"};
MyCompany.Uploader.Core(configobj).build();
This simply doesn't work. I can't seem to access the build function. I'm sure this is a rookie mistake.
The error I get: Property build does not exist on type void
That's because you did not add an important part of your javascript code into the typescript version, and that's the return object which contains the reference for the build function, it should be:
namespace MyCompany.Uploader {
export var Core = (config: any) {
function build() {
console.log("building");
}
return {
build: build
}
};
}
let configobj = { here: "there" };
MyCompany.Uploader.Core(configobj).build();
You can also define interfaces for the config and the return object:
namespace MyCompany.Uploader {
export interface Config {
here: string;
}
export interface Builder {
build: () => void;
}
export var Core = (config: Config): Builder => {
function build() {
console.log(config.here);
}
return {
build: build
}
};
}
let configobj = { here: "there" };
MyCompany.Uploader.Core(configobj).build();