How override moment now time with wrapper module - javascript

I am using moment and need to stub current time to always return 1/1/18 for example. So when I call moment() or moment.now() I should receive a moment object with the time set to 1/1/18.
Now I could override moment.now but then anything else that uses moment will have this stub due to module caching, which is not what I want.
So I have wrote a momentWrapper that copies moment's protoypes and defines a new constructor. The problem is when calling moment() it doesn't use the new defined prototype for now, as I'm simply calling moment's constructor. I need to copy moment and change it's now protoype in my new consturctor I'm just not sure how to do that.
This is what I have so far:
/**
* Wraps moment so that alternate time can be set as the 'now' time.
* This idea was grabbed from fs-extra https://github.com/jprichardson/node-fs-extra
* #module lib/momentWrapper
*/
var moment = require('moment');
function momentWrapper() {
return moment.apply(this, arguments);
}
assign(momentWrapper, moment);
momentWrapper.prototype = Object.create(moment.prototype);
momentWrapper.prototype.now = newNow;
momentWrapper.prototype.constructor = momentWrapper;
function newNow() {
return +new Date('1995-12-17T03:24:00');
}
/**
* Loops through given arguments and assigns them to a object
* Credit: fs-extra https://github.com/jprichardson/node-fs-extra/blob/fab376ec2bd4d4420806644c1b37a7ac487d17ec/lib/util/assign.js
* #function assign
* #return {Object} Mutated object with copied keys
*/
function assign () {
const args = [].slice.call(arguments).filter(i => i);
const dest = args.shift();
args.forEach(src => {
Object.keys(src).forEach(key => {
if (key === 'now') {
dest[key] = newNow;
}
else {
dest[key] = src[key];
}
});
});
return dest;
}
module.exports = momentWrapper;
Testing file:
var momentWrapper = require('./momentWrapper');
var moment = require('moment');
console.log(momentWrapper()); // Not working
console.log(momentWrapper('12/25/18', 'MM/DD/YY')); // Working
console.log(momentWrapper(momentWrapper.now()).format()) // Working
console.log('----------------------------------------');
console.log(moment()); // Working not being overridden
console.log(moment(moment.now()).format()) // // Working not being overridden
Output:
moment("2018-01-27T15:56:50.928")
moment("2018-12-25T00:00:00.000")
1995-12-17T03:24:00-05:00
----------------------------------------
moment("2018-01-27T15:56:50.938")
2018-01-27T15:56:50-05:00

Depending on your use case, rather than using a wrapper; you could temporarily replace the now function with a custom stub and when you're finished using it, just put the original one back.
var moment = require("moment");
// prepare before testing
moment.originalNow = moment.now;
moment.now = () => +new Date('1995-12-17T03:24:00');
console.log([
moment().format(),
moment(moment.now()).format(),
moment('12/25/18', 'MM/DD/YY').format()
]);
// clean after testing
moment.now = moment.originalNow;
moment.originalNow = undefined;
console.log([
moment().format(),
moment(moment.now()).format()
]);

Its so obvious that i havent seen it. You dont even need to play with inheritance, just clone the moment object and rewrite the now property:
const orig = require("moment");
const moment = (...args) => args.length === 0 ? moment.now() : orig(...args);
Object.assign(moment, orig);
moment.now = () => orig('1995-12-17T03:24:00');
export moment;

Related

How to mock javascipt new Date to return different date from browser console

There is a website I use which shows different content on different dates. In the JavaScript, it uses new Date() to determine the current date, which it uses to determine which content to show.
If I would like to view content from a different date, I can change my system time. However, this is tedious and interferes with other applications. I am trying to figure out if there is some code I can run in the browser's javascipt console that will mock out new Date() to return the date of my choosing
I see there are some questions that discuss creating a spy on Date with jest, but I do not see a way to mock this in my browser console
It's possible to replace the Date function with your own function that provides the results you want, but doing it before the page uses it will be tricky unless you write a browser extension.
The fundamental bit is (see comments):
// Save the original `Date` function
const OriginalDate = Date;
// Replace it with our own
Date = function Date(...args) {
// Called via `new`?
if (!new.target) {
// No, just pass the call on
return OriginalDate(...args);
}
// Determine what constructor to call
const ctor = new.target === Date ? OriginalDate : new.target;
// Called via `new`
if (args.length !== 0) {
// Date constructor arguments were provided, just pass through
return Reflect.construct(ctor, args);
}
// It's a `new Date()` call, mock the date we want; in this
// example, Jan 1st 2000:
return Reflect.construct(ctor, [2000, 0, 1]);
};
// Make our replacement look like the original (which has `length = 7`)
// You can't assign to `length`, but you can redefine it
Object.defineProperty(Date, "length", {
value: OriginalDate.length,
configurable: true
});
// Save the original `Date` function
const OriginalDate = Date;
// Replace it with our own
Date = function Date(...args) {
// Called via `new`?
if (!new.target) {
// No, just pass the call on
return OriginalDate(...args);
}
// Determine what constructor to call
const ctor = new.target === Date ? OriginalDate : new.target;
// Called via `new`
if (args.length !== 0) {
// Date constructor arguments were provided, just pass through
return Reflect.construct(ctor, args);
}
// It's a `new Date()` call, mock the date we want; in this
// example, Jan 1st 2000:
};
// Make our replacement look like the original (which has `length = 7`)
// You can't assign to `length`, but you can redefine it
Object.defineProperty(Date, "length", {
value: OriginalDate.length,
configurable: true
});
console.log("new Date()", new Date());
console.log("new Date(2021, 7, 3)", new Date(2021, 7, 3));
thank you #t-j-crowder 🙏 for the best solution I have found so far... I made some customisation to it so Date.now works as well.
Also one advise, put breakpoint at the very beginning of your script, run the code in console and resume execution for best date consistency 😌
my modification:
// Save the original `Date` function
const OriginalDate = Date;
const fakeDateArgs = [2022, 5, 3]; // beware month is 0 based
let fakeDate;
// Replace it with our own
Date = function Date(...args) {
// Called via `new`?
if (!new.target) {
// No, just pass the call on
return OriginalDate(...args);
}
// Determine what constructor to call
const ctor = new.target === Date ? OriginalDate : new.target;
// Called via `new`
if (args.length !== 0) {
// Date constructor arguments were provided, just pass through
return Reflect.construct(ctor, args);
}
// It's a `new Date()` call, mock the date we want; in this
fakeDate = Reflect.construct(ctor, fakeDateArgs);
return fakeDate;
};
// Make our replacement look like the original (which has `length = 7`)
// You can't assign to `length`, but you can redefine it
Object.defineProperty(Date, "length", {
value: OriginalDate.length,
configurable: true
});
Object.defineProperty(Date, "now", {
value: () => fakeDate.getTime(),
configurable: true
});
You can use this to modify the content before it is loaded:
https://developer.chrome.com/docs/extensions/reference/webRequest/
There is this extension which I haven't used that may be able to do it:
https://chrome.google.com/webstore/detail/page-manipulator/mdhellggnoabbnnchkeniomkpghbekko?hl=en

How do I test a function that calls another function in jest?

I have a function that converts an array of 8 bit integers into a x-bit integer. This function has extensive unit tests in Jest. This function is called convertArrayIntoInt().
I have another functions that relies on convertArrayIntoInt() called getSignature().
/**
* #param {Uint8Array} fileContents The file contents
* #param {number} position The position in the file where the signature is.
* #return {number} the 32 bit signature
*/
function getSignature(fileContents, position) {
let signature = fileContents.slice(position, position + 4);
signature = convertArrayIntoInt(signature);
for(let s in Enums.Signatures) {
if(signature === parseInt(s)) {
return signature;
}
}
return Enums.Signatures.BAD_SIGNATURE;
}
I want to write a test for this function that tests whether Uint8Array.of(0x04, 0x03, 0x4b, 0x50), 0) returns 0x04034b50 or Enums.BAD_SIGNATURE. I heard that with unit testing, you don't want to test a function that relies on the output of another function. How do I go about doing this in Jest?
Update: Looks like I had some issue in my original response when I merged different files in to a single files I expected it to work as before but it didn't so make sure to put function(s) you want to inject in to separate file(s).
As mentioned earlier, unit test should just test functionality of function it self while mocking dependencies.
I moved functions in to separate files, in one file all functions are defined and in another test.
enums.js
export const Enums = {
Signatures: {
SIGNATURE_1: 1,
SIGNATURE_2: 2,
SIGNATURE_3: 3,
BAD_SIGNATURE: -1
}
};
convertArrayIntoInt.js
export function convertArrayIntoInt(array) {
// processing...
return array.length;
}
getSignature.js
import { convertArrayIntoInt } from "./convertArrayIntoInt";
import { Enums } from "./enums";
export const getSignature = (fileContents, position) => {
let signature = convertArrayIntoInt(
fileContents.slice(position, position + 4)
);
for (let s in Enums.Signatures) {
if (signature === parseInt(s, 10)) {
return signature;
}
}
return signature;
};
tests.spec.js
import { getSignature } from "./getSignature";
import { Enums } from "./enums";
import * as convertArrayIntoIntModule from "./convertArrayIntoInt";
it("Function check", () => {
// arrange
const spy = jest.spyOn(convertArrayIntoIntModule, "convertArrayIntoInt");
spy.mockReturnValue(Enums.Signatures.SIGNATURE_1);
// act
const expected = Enums.Signatures.SIGNATURE_1;
const actual = getSignature([1, 2, 3, 4, 5, 6], 1);
// assert
expect(spy).toBeCalledTimes(1);
expect(actual).toEqual(expected);
// clean up
spy.mockRestore();
});
You can take a look at this sandbox that I created to test dummy function similar to yours.
Yes, you have heard it right. A unit test should only test a single unit/function. What you can do is, you can make another unit test to check convertArrayIntoInt() function, and for this function, you can set dummy return values for convertArrayIntoInt() in order to verify other logic is working correctly or not.
You can spyOn convertArrayIntoInt function and mockReturnValue.
https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname

React hooks with Typescript: Expected an assignment or function call and instead saw an expression

I have been a React dev for some time now. At work, we have now introduced Typescript which I am relatively new to.
I have created a hook for using cookies but the function I return is causing an error.
The hook (with some company stuff removed):
function useCookies(key: string) {
const [storedCookie, setStoredCookie] = useState(() => getCookieValue(key));
function setCookie(value: any, daysUntilExpiry?: number): any {
if (value) {
const valueToStore =
value instanceof Function ? value(storedCookie) : value;
setStoredCookie(valueToStore);
let cookieString = `${key}=${valueToStore}`;
if (daysUntilExpiry) {
const date = new Date();
date.setDate(date.getDate() + daysUntilExpiry);
cookieString = `${cookieString}; expires=${date}`;
}
document.cookie = cookieString;
}
}
return [storedCookie, setCookie];
}
const [loggedIn, setAuthState] = useCookies('logged_in');
setAuthState(true, 7);
The setAuthState function call gives me the error Expected an assignment or function call and instead saw an expression. Can anyone point me as to why?!
The return type of useCookies is inferred as an array that is any length and that can contain either strings or functions that set the cookie. Because of this, when you pluck the second item out of the list, TS thinks that this could be either a string or a function.
To fix this, if you're on 3.4+, when you return your value from your function, just mark it with as const as you return it:
return [cookie, setCookie] as const;
EDIT: Per Svensson's suggestion above also fixed it.

Class static getter which returns class in Javascript

I have some code:
//Main class
class Tools {
//Subclass implemented by static getter
static get SUPPORTED_UNITS () {
return class {
//Another one
static get Size () {
return class {
//Some data (constants in my case)
};
}
};
}
}
I want to use it like Tools.SUPPORTED_UNITS.Size and I want to receive some data as a result of it.
I use WebStorm for writing JavaScript code, so it shows me this when I tried to use it.
I don't know exactly, maybe I just haven't done some setup to my WebStorm, but.. What I need to do that I can use construction like Class.Subclass.SubClassMethod/SubSubclass.
I need that:
It be not a method
It be static
It be immutable (without Object.freeze or something like that) - so I can't change my Class.Subclass by typing Class.Subclass = foo;
It be indexing by IDE - so I have some help from IDE (like I type Class.Su and she suggest Class.Subclass)
Is it real in ES6 and WebStorm? Or maybe I have to use ES7 and Babel or someting like that?
P.S. That code is working, but I need suggestions from IDE or it will be like a hell.
Following #estus and #Felix Kling suggestion... (likely being misrepresented here)
const TOOLS = {
get SUPPORTED_UNITS() {
return {
get SIZE() {
return {
get Bar() {
return 1;
}
}
}
}
}
};
console.log(TOOLS.SUPPORTED_UNITS.SIZE.Bar) // 1;
TOOLS.SUPPORTED_UNITS.SIZE = "Hello";
// TOOLS.SUPPORTED_UNITS.SIZE = "Hello";
// ^
// TypeError: Cannot set property SIZE of #<Object> which has only a getter
That works but as remarked by #bergi, it creates a new object in every lookup:
console.log(TOOLS.SUPPORTED_UNITS === TOOLS.SUPPORTED_UNITS); // false
Yep...
An alternative still using getters would be encapsulate all those objects in an IIFE:
const TOOLS = (function () {
const SIZE = {
get Bar() { return 1; }
}
const SUPPORTED_UNITS = {
get SIZE() {
return SIZE;
}
}
return {
get SUPPORTED_UNITS() {
return SUPPORTED_UNITS;
}
}
}());
console.log(TOOLS.SUPPORTED_UNITS.SIZE.Bar === TOOLS.SUPPORTED_UNITS.SIZE.Bar); // true
console.log(TOOLS.SUPPORTED_UNITS.SIZE === TOOLS.SUPPORTED_UNITS.SIZE); // true
console.log(TOOLS === TOOLS); // true
TOOLS.SUPPORTED_UNITS = "hello"; // still throws an error
Third update:
The second example solves the issue related to creation of a new object every lookup but it also does not take in consideration that the access to every nested property results in a function call to reevaluate something that is not supposed to change:
//A naive measurement:
for(let counter = 0; counter < 5; counter++) {
let start = Date.now();
for(let i = 0; i < 200000; i++){
let somevar = Tools.SUPPORT_UNITS.SIZE;
}
let end = Date.now();
console.log(`Elapsed using nested getters: ${end - start}ms.`);
}
// Something like:
/** Elapsed using nested getters: 20ms.
* Elapsed using nested getters: 14ms.
* Elapsed using nested getters: 9ms.
* Elapsed using nested getters: 10ms.
* Elapsed using nested getters: 10ms.
*
// While the same as ordinary properties:
* Elapsed ordinary properties: 2ms.
* Elapsed ordinary properties: 5ms.
* Elapsed ordinary properties: 1ms.
* Elapsed ordinary properties: 0ms.
* Elapsed ordinary properties: 0ms.
*/
So it is also better to check alternatives before using this approach if running 200000 times loops is in the foreseeable future.

ECMAScript/Node: is it possible to save an object instance or context to the filesystem?

Is it possible to save an object (instance of a class) to the filesystem including its internal values?
Please do not flag as off-topic. I understand that even if this is somehow achievable, it is not common practice. It doesn't matter either if it is hacky. I am just curious if it can be achieved because it is sunday 8 oclock and I had a long day.
function Node() {
let x = Math.random()
this.data = function() {
return x
}
}
let node1 = new Node()
// save it to the disk somehow as node1
saveNode('node1', node1)
// load it back from the disk into memory somehow, perhaps using eval??
let savedNode = loadNode('node1')
console.log(
savedNode.data(),
node1.data()
) /* these should be the same */
You can stringify an object including its functions by using a 'modifier' function as second argument in JSON.stringify but that won't do the trick here.
Only if the object (or class) cooperates. No, it's not possible in general to access the values hidden behind a closure scope.
You can however do something like
class Node {
constructor(x = Math.random()) {
this.data = () => x;
}
toJSON() {
return {x: this.data()};
}
static fromJSON(obj) {
return new this(obj.x);
}
}
const fs = require("fs");
function save(name, obj) {
fs.writeFileSync(`./${name}.json`, JSON.stringify(obj), "utf-8");
}
function load(constr, name) {
return constr.fromJSON(JSON.parse(fs.readFileSync(`./${name}.json`, "utf-8");
}
let node1 = new Node();
save('node1', node1);
let savedNode = load(Node, 'node1');
console.log(savedNode.data(), node1.data())

Categories

Resources