Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test cleanup: deprecate.test.ts #1489

Merged
merged 8 commits into from
Nov 8, 2023
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

226 changes: 199 additions & 27 deletions test/deprecate.test.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,229 @@
/* eslint-disable import/no-named-as-default-member */
import assert from 'node:assert';
import chalk from 'chalk';
import sinon from 'sinon';
import sinon, { type SinonSpy } from 'sinon';
import deprecate from '../src/util/deprecate.js';

type SimpleObject = {
foo: number;
functionInObj: (someValue: number) => number;
};

describe('deprecate()', () => {
let fakeConsoleLog: SinonSpy;
beforeEach(() => {
sinon.spy(console, 'log');
fakeConsoleLog = sinon.fake();
sinon.replace(console, 'log', fakeConsoleLog);
});

afterEach(() => {
console.log.restore();
sinon.restore();
});

it('log a message', () => {
const func = sinon.spy();
const wrapped = deprecate('foo', func);
sinon.assert.notCalled(console.log);
wrapped('bar', 2);
sinon.assert.calledWith(console.log, chalk.yellow('(!) ') + 'foo');
sinon.assert.calledWith(func, 'bar', 2);
describe('deprecate a function', () => {
let deprecatedLogSpy: SinonSpy;
beforeEach(() => {
deprecatedLogSpy = sinon.fake();
sinon.replace(deprecate, 'log', deprecatedLogSpy);
});

afterEach(() => {
sinon.restore();
});

it('the original function is still called', () => {
const originalFunction = sinon.spy();
const deprecatedFunction = deprecate('this is function deprecated', originalFunction);

deprecatedFunction('baz', 3);
assert.ok(
originalFunction.calledWith('baz', 3),
`original function called with (${originalFunction.lastCall.args[0]}, ${originalFunction.lastCall.args[1]})`,
);
});

it('a call to deprecate.log(msg) is added', () => {
const originalFunction = sinon.spy();
const deprecatedFunction = deprecate('this is function deprecated', originalFunction);

originalFunction('bar', 2);
assert.ok(originalFunction.calledWith('bar', 2), 'original function not called with ("bar", 2)');
assert.ok(deprecatedLogSpy.notCalled);

deprecatedFunction('baz', 3);
assert.ok(deprecatedLogSpy.calledWith('this is function deprecated'));
});
});

describe('.log', () => {
it('logs the message in yellow, starting with "(!) "', () => {
deprecate.log('this is the message');
assert.ok(fakeConsoleLog.calledWith(chalk.yellow('(!) ') + 'this is the message'));
});
});

describe('.object()', () => {
it('wrap an object and log a message', () => {
const dummy = {
let deprecatedLogSpy: SinonSpy;
beforeEach(() => {
deprecatedLogSpy = sinon.fake();
sinon.replace(deprecate, 'log', deprecatedLogSpy);
});

afterEach(() => {
sinon.restore();
});

it('deprecates all functions/methods in the object', () => {
type SomeOtherObject = SimpleObject & {
anotherFunction: (someValue: string) => string;
};
const originalObject: SomeOtherObject = {
foo: 1,
func: sinon.spy(),
functionInObj(someValue: number): number {
return someValue - 1;
},
anotherFunction(someValue: string): string {
return `${someValue} and ${someValue}`;
},
};
const wrapped = deprecate.object('<%= name %> is deprecated', dummy);

// Keep values
assert.equal(wrapped.foo, 1);
// TODO When deprecate.js is changed to .ts, DeprecatedObject will be defined in deprecate.ts as something like type DeprecatedObject<O> = O;
const deprecatedObject = deprecate.object('<%= name %> is deprecated', originalObject);
// @ts-expect-error The method functionInObj() does exist on deprecatedObject. This should be a DeprecatedObject<SimpleObject>. When deprecate.js is changed to .ts, this can be implemented and no error will occur here.
deprecatedObject.functionInObj(42);
assert.ok(
deprecatedLogSpy.calledWith('functionInObj is deprecated'),
`last call with args: ${deprecatedLogSpy.lastCall.args[0]}`,
);

// Wrap methods
wrapped.func(2);
sinon.assert.calledWith(dummy.func, 2);
sinon.assert.calledWith(console.log, chalk.yellow('(!) ') + 'func is deprecated');
// @ts-expect-error The method anotherFunction() does exist on deprecatedObject. This should be a DeprecatedObject<SimpleObject>. When deprecate.js is changed to .ts, this can be implemented and no error will occur here.
deprecatedObject.anotherFunction('something');
assert.ok(
deprecatedLogSpy.calledWith('anotherFunction is deprecated'),
`last call with args: ${deprecatedLogSpy.lastCall.args[0]}`,
);
});

it('properties that are not functions are not changed', () => {
const originalObject: SimpleObject = {
foo: 1,
functionInObj(someValue: number): number {
return someValue - 1;
},
};

const deprecatedObject = deprecate.object('The function "<%= name %>" is deprecated', originalObject);
// @ts-expect-error The property foo does exist on deprecatedObject. This should be a DeprecatedObject<SimpleObject>. When deprecate.js is changed to .ts, this can be implemented and no error will occur here.
const fooValue = deprecatedObject.foo;
assert.equal(fooValue, 1);
assert.ok(deprecatedLogSpy.notCalled);
});

it('property getters and setters are not changed', () => {
type ObjectWithGettersSetters = SimpleObject & {
get bar(): number;
set bar(someValue: number);
};

const originalObject: ObjectWithGettersSetters = {
foo: 10,
functionInObj(someValue: number): number {
return someValue - 1;
},
get bar(): number {
return this.foo * 2;
},
set bar(someValue: number) {
this.foo = someValue / 2;
},
};

const deprecatedObject = deprecate.object('The function "<%= name %>" is deprecated', originalObject);
// @ts-expect-error The getter bar does exist on the object. This should be a DeprecatedObject<SimpleObject>. When deprecate.js is changed to .ts, this can be implemented and no error will occur here.
deprecatedObject.bar;
// @ts-expect-error The setter bar does exist on the object. This should be a DeprecatedObject<SimpleObject>. When deprecate.js is changed to .ts, this can be implemented and no error will occur here.
deprecatedObject.bar = 7;

assert.ok(deprecatedLogSpy.notCalled);
});

it('deprecation message can be a template', () => {
const originalObject: SimpleObject = {
foo: 1,
functionInObj(someValue: number): number {
return someValue - 1;
},
};

const deprecatedObject = deprecate.object('The function "<%= name %>" is deprecated', originalObject);

// @ts-expect-error The method functionInObj() does exist on deprecatedObject. This should be a DeprecatedObject<SimpleObject>. When deprecate.js is changed to .ts, this can be implemented and no error will occur here.
deprecatedObject.functionInObj(42);

assert.ok(
deprecatedLogSpy.calledWith('The function "functionInObj" is deprecated'),
`last call with args: ${deprecatedLogSpy.lastCall.args[0]}`,
);
});
});

describe('.property()', () => {
it('wrap a property getter and log a message', () => {
const dummy = {
let deprecatedLogSpy: SinonSpy;
beforeEach(() => {
deprecatedLogSpy = sinon.fake();
sinon.replace(deprecate, 'log', deprecatedLogSpy);
});

afterEach(() => {
sinon.restore();
});

it('the deprecated message shows when a property is accessed', () => {
const originalObject = {
foo: 1,
};
deprecate.property('foo is deprecated', dummy, 'foo');
deprecate.property('foo property is deprecated', originalObject, 'foo');

// Value is not affected; it remains the same
assert.equal(originalObject.foo, 1);

assert.ok(
deprecatedLogSpy.calledWith('foo property is deprecated'),
`deprecatedLogSpy called with (${deprecatedLogSpy.lastCall.args[0]})`,
);
});

it('property getters and setters are deprecated', () => {
type ObjectWithGettersSetters = SimpleObject & {
get bar(): number;
set bar(someValue: number);
};

const originalObject: ObjectWithGettersSetters = {
foo: 10,
functionInObj(someValue: number): number {
return someValue - 1;
},
get bar(): number {
return this.foo * 2;
},
set bar(someValue: number) {
this.foo = someValue / 2;
},
};

// Keep values
assert.equal(dummy.foo, 1);
deprecate.property('bar is deprecated', originalObject, 'bar');
originalObject.bar;
assert.ok(
deprecatedLogSpy.calledWith('bar is deprecated'),
`deprecatedLogSpy called with (${deprecatedLogSpy.lastCall.args[0]})`,
);

// Wrap methods
sinon.assert.calledWith(console.log, chalk.yellow('(!) ') + 'foo is deprecated');
originalObject.bar = 7;
assert.ok(
deprecatedLogSpy.calledWith('bar is deprecated'),
`deprecatedLogSpy called with (${deprecatedLogSpy.lastCall.args[0]})`,
);
});
});
});
Loading