Why branch is not covered with rest parameters in Jest? - javascript

I'm currently trying to cover 100% of my function but I'm facing a branch not covered and it don't understand why it's not covered and even how to fix and cover it.
test coverage result here
It tried many different tests but it didn't changed anything. I don't if the problems comes from me or Jest ?
My function
export const removeProductInList = (state: IItems, action: IBasketRemoveProductAction) => {
const {
payload: { uuid },
} = action;
const { [uuid]: itemToRemove, ...restOfItems } = state;
return restOfItems;
};
My tests
product1 = { id: 1 };
product2 = { id: 2 };
mockUuid1 = 'IdTest';
mockUuid2 = 'IdTest2';
mockItem1 = { product: product1, quantity: 1 };
mockItem2 = { product: product2, quantity: 1 };
mockListItems = {
[mockUuid1]: mockItem1,
[mockUuid2]: mockItem2,
};
it('should handle PRODUCT__REMOVE', () => {
expect(removeProductInList({ [mockUuid1]: mockItem1 }, removeProduct(mockUuid1))).toEqual({});
expect(removeProductInList(mockListItems, removeProduct(mockUuid1))).toEqual({ [mockUuid2]: mockItem2 });
expect(removeProductInList({}, removeProduct('acac'))).toEqual({});
});
I expect to cover all the function with my actual test.
My version of jest is 23.4.1, just in case.

You just have to specify the target esnext in your tsConfig file in ts-jest.
// jest.config.js
module.exports = {
...
'ts-jest': {
....
tsConfig: './tsconfig.json'),
},
// tsconfig.json
{
...,
"target": "esnext",
}

Try to test the removeProductList with extra properties
expect(removeProductInList({ [mockUuid1]: mockItem1, extraProp: 'someValue' }, removeProduct(mockUuid1))).toEqual({});

In my case, I followed Nicolas' answer and edited babel.config.json as below and it worked. Thank you, Nicolas.
{
"presets": ["#babel/preset-env"],
"targets": {"esmodules": true} <-- added this line
}

Related

How can i test an observable that has pipe and map?

I'm assigning values to this variables inside ngOnInit:
this.simStatsList$ = this.sideMenuService.getSimStatsList();
this.currentStation$ = this.simStatsList$.pipe(
map(station => station.find((station: ISimStats) => station.stationName === this.authService.userStation)),
) as Observable<ISimStats>;
This is my test:
it('should select userStation as currentStation', () => {
component.currentStation$.subscribe((response) => {
expect(response).toEqual(
{ stationName: 'test', stats: { open: 0, down: 0, preflight: 0 } }
);
});
});
It passes but is not covering the map function from rxjs. Also im providing sideMenuService and AuthService as mocked values and this is my mock. I'm missing something but i don't know what is it.
export const mockSideMenuService = {
getSimStatsList: () =>
of([
{ stationName: 'test', stats: { open: 0, down: 0, preflight: 0 } },
{ stationName: 'test1', stats: { open: 1, down: 1, preflight: 1 } }
] as ISimStats[])
}
export const mockAuthService = {
userStation: 'test'
}
Could you help me to cover the whole code?
After #will alexander comment i did some change and it worked:
First, pass the function to the sideMenuService and recieve needed data as parameters:
side-menu.service.ts
getCurrentSimStats(
simStatsList$: Observable<ISimStats[]>,
currentStation: string): Observable<ISimStats> {
return simStatsList$.pipe(
map((station) => station.find((station: ISimStats) => station.stationName === currentStation))) as Observable<ISimStats>;
}
Then my component test coverage passed as 100% but the sideMenuService wasn't so i wrote this small test on service spec file:
side-menu.service.spec.ts
it('should getCurrentStation', () =>{
service.getCurrentSimStats(of(mockSideMenuService.mockSimStatsResponse), 'test').subscribe((res) => {
expect(res).toEqual(mockSideMenuService.mockCurrentSimStatsResponse);
});
})
After this, everything worked and tests are passing!

How to initialize variables referencing each other?

I'm using TypeScript with validate-value which uses ajv under the hood. I want to create validation schemas for filters similiar to MongoDB operators.
Each schema should have its own file but since it's possible to nest one filter into another I'm creating a circular reference before initializing the schemas. So I'm getting the error
ReferenceError: Cannot access 'filterSchema' before initialization
How to reproduce the problem with plain Node and JS:
package.json
{
"type": "module",
"scripts": {
"dev": "node main.js"
}
}
main.js
import { filterSchema } from "./filterSchemas/filterSchema.js";
console.log(filterSchema);
.filterSchemas/filterSchema.js
import { logicalOperatorSchema } from "./logicalOperatorSchema.js";
const filterSchema = {
type: "object",
oneOf: [logicalOperatorSchema],
};
export { filterSchema };
.filterSchemas/logicalOperatorSchema.js
import { notSchema } from "./notSchema.js";
const logicalOperatorSchema = {
type: "object",
oneOf: [notSchema],
};
export { logicalOperatorSchema };
.filterSchemas/notSchema.js
import { filterSchema } from "./filterSchema.js";
const notSchema = {
type: "object",
properties: {
$not: filterSchema,
},
required: ["$not"],
additionalProperties: false,
};
export { notSchema };
Do you have any suggestions how to solve this problem?
I also tried to put all schemas into a single file but that didn't solve the actual problem.
schemas.js
const notSchema = {
type: "object",
properties: {
$not: filterSchema,
},
required: ["$not"],
additionalProperties: false,
};
const logicalOperatorSchema = {
type: "object",
oneOf: [notSchema],
};
const filterSchema = {
type: "object",
oneOf: [logicalOperatorSchema],
};
export { filterSchema }
And I also do think that solving this problem solves the other one
const foo = {
x: bar
};
const bar = {
y: foo
};
console.log(foo);
You can do it like this, but it is not going to be useful.
var foo = {
x: bar
}
var bar = {
y: foo
}
foo.x = bar
bar.y = foo
console.log(foo)
console.log(foo.x.y.x.y)

Using XState, how can I access name of current state in an action?

I'm playing around learning XState and wanted to include an action in a machine that would just log the current state to console.
Defining a simple example machine like so, how would I go about this? Also note the questions in the comments in the code.
import { createMachine, interpret } from "xstate"
const sm = createMachine({
initial: 'foo',
states: {
foo: {
entry: 'logState', // Can I only reference an action by string?
// Or can I add arguments here somehow?
on: {
TOGGLE: {target: 'bar'}
}
},
bar: {
entry: 'logState',
on: {
TOGGLE: {target: 'foo'}
}
}
}
},
{
actions: {
logState(/* What arguments can go here? */) => {
// What do I do here?
}
}
});
I know that actions are called with context and event as arguments but I don't see a way to get the current state from either of those. Am I missing something here?
For a simple use case like yours, you could try recording the state on transition.
let currentState;
const service = interpret(machine).onTransition(state => {
if (state.value != currentState) {
// TODO: terminate timer if any and start a new one
currentState = state.value;
}
});
Then use the value in your actions.
See more here: https://github.com/statelyai/xstate/discussions/1294
Actions receive three arguments - context, event and meta. meta have property state, which is current state.
import { createMachine } from "xstate";
let metaDemo = createMachine(
{
id: "meta-demo",
initial: "ping",
states: {
ping: {
entry: ["logStateValues"],
after: { TIMEOUT: "pong" },
},
pong: {
entry: ["logStateValues"],
after: { TIMEOUT: "ping" },
},
},
},
{
delays: {
TIMEOUT: 3000,
},
actions: {
logStateValues(ctx, event, meta) {
if (meta.state.matches("ping")) {
console.log("It's PING!");
} else if (meta.state.matches("pong")) {
console.log("And now it's PONG");
} else {
console.log(
`This is not supposed to happen. State is: ${meta.state
.toStrings()
.join(".")}`
);
}
},
},
}
);

config error : Invalid value undefined for HardhatConfig.networks.paths.url

this is my hardhat.config.js file code :
require("#nomiclabs/hardhat-waffle");
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
networks : {
hardhat :{
chainId: 1337,
},
paths :{
artifacts: "./src/artifacts",
},
},
};
and when i compile this using npx hardhat compile
it shows following error :
Error HH8: There's one or more errors in your config file:
Invalid value undefined for HardhatConfig.networks.paths.url - Expected a value of type
string.
To learn more about Hardhat's configuration, please go to https://hardhat.org/config
for your reference i am following this tutorial for building this web3 project : "https://blog.suhailkakar.com/setup-and-build-your-first-web-3-application"
You have to close the network's bracket after the hardhat chain Id :)
Please replace your module.exports object with this
module.exports = {
solidity: "0.8.4",
networks:{
hardhat:{
chainId: 1337,
}
},
paths:{
artifacts: "./src/artifacts"
}
};
P.S: Thanks for pointing this out, I have also updated my article
Required dotenv before using process.env values in hardhat.config.js
require("#nomicfoundation/hardhat-toolbox")
require("dotenv").config()
const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL
const PRIVATE_KEY = process.env.PRIVATE_KEY
const PRIVATE_KEY_USER1 = process.env.PRIVATE_KEY_USER1
/** #type import('hardhat/config').HardhatUserConfig */
module.exports = {
defaultNetwork: "hardhat",
solidity: {
compilers: [
{ version: "0.8.8" },
{ version: "0.6.6" },
{ version: "0.8.16" },
],
},
networks: {
rinkeby: {
url: RINKEBY_RPC_URL,
accounts: [PRIVATE_KEY, PRIVATE_KEY_USER1],
chainId: 4,
},
},
}

How to overcome jest "Cannot access before initialization" problem?

settings.js
export default {
web: {
a: 1
},
mobile: {
b: 2
}
};
getSetting.js
import settings from "./settings";
export const getSetting = platform => {
return settings[platform];
};
getSettings.test.js
import { getSetting } from "./getSetting";
const TEST_SETTINGS = { c: 3 };
jest.mock("./settings", () => {
return {
test: TEST_SETTINGS
};
});
test("getSetting", () => {
expect(getSetting("test")).toEqual(TEST_SETTINGS);
});
Error
ReferenceError: Cannot access 'TEST_SETTINGS' before initialization
I believe this has something to do with hoisting. Is there a way to overcome this issue? Does jest provide any other means to achieve this?
I don't want to do this. This is not good when the mock data is large and used in multiple tests.
jest.mock("./settings", () => {
return {
test: { c: 3 }
};
});
expect(getSetting("test")).toEqual({ c: 3 });
jest.mock is automatically hoisted, this results in evaluating mocked module before TEST_SETTINGS is declared.
Also, this results in ./settings being mocked with test named export, while it's expected to have default export.
It shouldn't use temporary variable, the value is available when it's being imported:
import settings from "./settings";
jest.mock("./settings", () => {
return { default: {
test: { c: 3 }
} };
});
...
expect(getSetting("test")).toBe(settings.test);
Or, use Dynamic Imports import().
E.g.
const TEST_SETTINGS = { c: 3 };
jest.mock('./settings', () => {
return {
test: TEST_SETTINGS,
};
});
test('getSetting', async () => {
const { getSetting } = await import('./getSetting');
expect(getSetting('test')).toEqual(TEST_SETTINGS);
});
test result:
PASS examples/61843762/getSettings.test.js
✓ getSetting (4 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.25 s, estimated 4 s
jestjs version: "jest": "^26.6.3"

Categories