Using: Node, Prisma and Jest.
I have a value that is acting like a Javascript date in everyway other than the instanceof Date
check.
Here is the code that I've used to check if it's a date.
const deletedUser = await app.prisma.user.findUnique({
where: { id: 1 },
});
console.log(deletedUser);
console.log(typeof deletedUser.deleted_at); // object
console.log(deletedUser.deleted_at.constructor); // [Function: Date]
console.log(deletedUser.deleted_at.constructor.name); // Date
console.log(deletedUser.deleted_at instanceof Date); // false
console.log(deletedUser.deleted_at); // 2022-08-15T21:50:34.344Z
console.log(Object.prototype.toString.call(deletedUser.deleted_at) === '[object Date]'); // true
console.log(Object.prototype.toString.call(deletedUser.deleted_at)); // [object Date]
console.log(new Date(deletedUser.deleted_at)); // 2022-08-15T21:50:34.344Z
console.log(deletedUser.deleted_at.prototype); // undefined
Object.keys(deletedUser.deleted_at).forEach(prop => console.log(prop))
console.log(Object.keys(deletedUser.deleted_at)); // []
console.log(deletedUser.deleted_at.__proto__); // {}
console.log(isNaN(deletedUser.deleted_at)); // false
console.log(deletedUser.deleted_at.valueOf()); // Mon Aug 15 2022 21:56:54 GMT+0000 (Coordinated Universal Time)
console.log(Date(deletedUser.deleted_at.constructor.prototype));
I've looked at the following resources to research this question.
- How to check whether an object is a date?
- https://jestjs.io/docs/expect#expectobjectcontainingobject
- Get the name of an object's type
- Detecting an "invalid date" Date instance in JavaScript
- Why are myarray instanceof Array and myarray.constructor === Array both false when myarray is in a frame?
- http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
- https://groups.google.com/g/comp.lang.javascript/c/XTWYCOwC96I/m/70rNoQ3L-xoJ
- How to know string value is an instance of Date in Javascript?
Using these resources I used the above checks to inspect whether the deleted_at
is a Date
and they all pass.
This ans. https://stackoverflow.com/a/643827/9530790 from the first question above mentions that...
you can use the instanceof operator, i.e. But it will return true for invalid dates too, e.g. new Date('random_string') is also instance of Date
date instanceof Date
This will fail if objects are passed across frame boundaries.
A work-around for this is to check the object's class via
Object.prototype.toString.call(date) === '[object Date]'
My understanding is that frames have to do with iframes and separate windows on a browser. I'm using node, so I'm not sure if that has to do with separate frames. Also as mentioned in the ans. if it is separate frames then Object.prototype.toString.call(date) === '[object Date]'
should be false
but by my case it's true
.
In jest when I test like the following...
expect(deletedUser).toMatchObject(
expect.objectContaining({
deleted_by_id: expect.any(Number),
deleted_at: expect.any(Date),
}),
);
The expect.any(Date)
call fails. It's possible that under the hood jest calls instanceOf
. I couldn't find this mentioned in the jest docs.
What's interesting to note is that when I set deleted_at before doing the expect call above, lke this deletedUser.deleted_at
= new Date` then the test passes. It's just that when it comes out of the database from prisma it fails.
Jest prints out that the test failed with deleted_at looking like this "deleted_at": Date {}
. While when I set deleted_at before to new Date then it passes and prints like this "deleted_at": 2022-08-15T21:56:54.402Z
. However when it fails and jest prints Date {}
, if I log it with console.log
it prints the date normaly "deleted_at": 2022-08-15T21:56:54.402Z
I recently changed around the setup of the tests and I imagine that has to do with it. But who is causing this failure? And what is the reason? Who is causing instanceof Date
to be false and why?
What I changed is that in my jest.config.js I create a global app to use in the tests and I attach the prisma client so I can get it via app.prisma
const app = require('./tests/app');
const prisma = require('./prisma/client');
const request = require('supertest');
app.prisma = prisma;
app.testRequest = request(app);
module.exports = {
testEnvironment: 'node',
globals: {
app,
},
};
I did this so I only have to use one app across test suites this greatly speeds up our tests, cutting down the time from about 130s to 40s. I can come up with a workaround to get this test to pass but I'm concerned that this may be indicative of a more significant issue that I've stumbled upon.
I'm wondering if node, prisma or jest is creating a separate context between one Date constructor and another. Similar to passing objects across frame boundaries. However, I can't confirm this and as mentioned above then the Object.prototype.toString.call(date) === '[object Date]'
check should be false
.
Update
I've noticed that a similar issue is occurring regarding a function that we're attaching to the Array.prototype
property. When trying to access it on the test it doesn't exist.
From this behavior, I'm leaning toward the idea that jest is creating some sort of new context before each test, and things that get added in jest.config.js won't necessarily exist during each test.
The jest docs https://jestjs.io/docs/configuration#globals-object mention something about data not persisting between tests when using globals. Even though some things are persisted, it's not clear what is and what isn't and it can't be relied upon.