In JavaScript, I can define a constructor function which can be called with or without new:
function MyCl
Here's how I've solved this in jest for testing groups of immutable models. The makeHash function isn't doing anything special, just a utility creating short, random strings from uuid().
The 'magic' for me was declaring type as new (...args: any[]) => any allowing it to be 'newed' as let model = new set.type(...Object.values(set.args));. So, less about getting around new and more about working in 'newable' forms.
// models/oauth.ts
export class OAuthEntity {
constructor(public readonly id: T) {}
[key: string]: any;
}
export class OAuthClient extends OAuthEntity {
/**
* An OAuth Client
* @param id A unique string identifying the client.
* @param redirectUris Redirect URIs allowed for the client. Required for the authorization_code grant.
* @param grants Grant types allowed for the client.
* @param accessTokenLifetime Client-specific lifetime of generated access tokens in seconds.
* @param refreshTokenLifetime Client-specific lifetime of generated refresh tokens in seconds
* @param userId The user ID for client credential grants
*/
constructor(
public readonly id: string = '',
public readonly redirectUris: string[] = [],
public readonly grants: string[] = [],
public readonly accessTokenLifetime: number = 0,
public readonly refreshTokenLifetime: number = 0,
public readonly userId?: string,
public readonly privateKey?: string
) {
super(id);
}
}
// models/oauth.test.ts
import { makeHash, makePin } from '@vespucci/utils';
import { OAuthEntity, OAuthClient } from '@vespucci/admin/server/models/oauth';
type ModelData = { type: new (...args: any[]) => any; args: { [key: string]: any }; defs?: { [key: string]: any } };
describe('Model Tests', () => {
const dataSet: ModelData[] = [
{ type: OAuthEntity, args: { id: makeHash() } },
{
type: OAuthClient,
args: {
id: makeHash(),
redirectUris: [makeHash()],
grants: [makeHash()],
accessTokenLifetime: makePin(2),
refreshTokenLifetime: makePin(2),
userId: makeHash(),
privateKey: makeHash(),
},
},
{
type: OAuthClient,
args: {},
defs: {
id: '',
redirectUris: [],
grants: [],
accessTokenLifetime: 0,
refreshTokenLifetime: 0,
},
},
];
dataSet.forEach((set) => {
it(`Creates ${set.type.name} With ${Object.keys(set.args).length} Args As Expected`, () => {
let model!: any;
const checkKeys = Object.keys(set.args).concat(Object.keys(set.defs || {}).filter((k) => !(k in set.args)));
const checkValues: any = checkKeys
.map((key) => ({ [key]: set.args[key] || set.defs?.[key] }))
.reduce((p, c) => ({ ...p, ...c }), {});
expect(() => {
model = new set.type(...Object.values(set.args));
}).not.toThrow();
expect(model).toBeDefined();
checkKeys.forEach((key) => expect(model[key]).toEqual(checkValues[key]));
});
});
});
The end result, for me, is: