Skip to content

Commit

Permalink
strict null checks
Browse files Browse the repository at this point in the history
  • Loading branch information
rezoled committed Jul 15, 2024
1 parent 5ee5671 commit 6472f1a
Show file tree
Hide file tree
Showing 16 changed files with 45 additions and 36 deletions.
6 changes: 3 additions & 3 deletions src/MethodStubCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class MethodStubCollection {
return matchingGroup ? matchingGroup.getGroupIndex() : -1;
}

public getFirstMatchingFromGroupAndRemoveIfNotLast(groupIndex: number, args: any[]): MethodStub {
public getFirstMatchingFromGroupAndRemoveIfNotLast(groupIndex: number, args: any[]): MethodStub | null {
const result = this.getFirstMatchingFromGroup(groupIndex, args);
this.removeIfNotLast(groupIndex, args);
return result;
Expand All @@ -30,8 +30,8 @@ export class MethodStubCollection {
}
}

private getFirstMatchingFromGroup(groupIndex: number, args: any[]): MethodStub {
return this.items.find((item: MethodStub) => item.getGroupIndex() === groupIndex && item.isApplicable(args));
private getFirstMatchingFromGroup(groupIndex: number, args: any[]): MethodStub | null {
return this.items.find((item: MethodStub) => item.getGroupIndex() === groupIndex && item.isApplicable(args)) ?? null;
}

private getFirstMatchingIndexFromGroup(groupIndex: number, args: any[]): number {
Expand Down
2 changes: 1 addition & 1 deletion src/MethodStubSetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class MethodStubSetter<T, ResolveType = void, RejectType = Error> {
return this;
}

public thenResolve(...rest: ResolveType[]): this {
public thenResolve(...rest: (ResolveType | undefined)[]): this {
this.convertToPropertyIfIsNotAFunction();
// Resolves undefined if no resolve values are given.
if (rest.length === 0) {
Expand Down
10 changes: 5 additions & 5 deletions src/Mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ export class Mocker {
return;
}
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
if (descriptor.get) {
if (descriptor?.get) {
this.createPropertyStub(name);
this.createInstancePropertyDescriptorListener(name, descriptor, obj);
this.createInstanceActionListener(name, obj);
} else if (typeof descriptor.value === "function") {
} else if (typeof descriptor?.value === "function") {
this.createMethodStub(name);
this.createInstanceActionListener(name, obj);
} else {
Expand Down Expand Up @@ -164,8 +164,8 @@ export class Mocker {
const action: MethodAction = new MethodAction(key, args);
this.methodActions.push(action);
const methodStub = this.getMethodStub(key, args);
methodStub.execute(args);
return methodStub.getValue();
methodStub?.execute(args);
return methodStub?.getValue();
};
}

Expand Down Expand Up @@ -225,7 +225,7 @@ export class Mocker {
};
}

private getMethodStub(key: string, args: any[]): MethodStub {
private getMethodStub(key: string, args: any[]): MethodStub | null {
const methodStub: MethodStubCollection = this.methodStubCollections[key];
if (methodStub && methodStub.hasMatchingInAnyGroup(args)) {
const groupIndex = methodStub.getLastMatchingGroupIndex(args);
Expand Down
4 changes: 2 additions & 2 deletions src/Spy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class Spy extends Mocker {
public reset(): void {
_.forEach(this.realMethods, (method, key) => {
if (method.instance) {
Object.defineProperty(this.instance, key, method.descriptor);
Object.defineProperty(this.instance, key, method.descriptor ?? {});
} else {
delete this.instance[key];
}
Expand All @@ -31,7 +31,7 @@ export class Spy extends Mocker {
const realMethod = this.realMethods[key];

if (realMethod) {
const method = realMethod.descriptor.get || realMethod.descriptor.value;
const method = realMethod.descriptor?.get || realMethod.descriptor?.value;
return new CallThroughMethodStub(this.instance, method);
}

Expand Down
2 changes: 1 addition & 1 deletion src/matcher/type/DeepEqualMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class DeepEqualMatcher<T> extends Matcher {

public match(value: any): boolean {
return _.isEqualWith(this.expectedValue, value,
(expected: any, actual: any): boolean => {
(expected: any, actual: any) => {
if (expected instanceof Matcher) {
return expected.match(actual);
}
Expand Down
2 changes: 1 addition & 1 deletion src/spy/RealMethod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export class RealMethod {
constructor(public descriptor: PropertyDescriptor,
constructor(public descriptor: PropertyDescriptor | undefined,
public instance: boolean) {}
}
1 change: 1 addition & 0 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"target": "es5",
"removeComments": true,
"sourceMap": true,
"strictNullChecks": true,
"declaration": true,
"lib": [
"es5",
Expand Down
24 changes: 15 additions & 9 deletions src/utils/MockableFunctionsFinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const methodTokenName = new Set([
const isFunctionNode = (node: _babel_types.Statement | FunctionNode): node is FunctionNode => methodTokenName.has(node.type);


function getAssignmentName(node: _babel_types.LVal) {
function getAssignmentName(node: _babel_types.LVal): string | null {
if (node.type === "Identifier")
return node.name;

Expand All @@ -38,28 +38,30 @@ function getAssignmentName(node: _babel_types.LVal) {
return null;
}

function handleClassProp(node: _babel_types.ClassProperty): string {
if (node.value.type !== 'ArrowFunctionExpression' && node.value.type !== 'FunctionExpression') return null;
function handleClassProp(node: _babel_types.ClassProperty): string | null {
if (node.value?.type !== 'ArrowFunctionExpression' && node.value?.type !== 'FunctionExpression') return null;

if ('name' in node.key) return node.key.name;
if ('value' in node.key) return node.key.value.toString();
return null;
}

function handleExpression(node: _babel_types.Expression): string {
function handleExpression(node: _babel_types.Expression): string | null {
if ('expression' in node && typeof node.expression !== 'boolean') return handleExpression(node.expression);

if (node.type === 'AssignmentExpression') {
return getAssignmentName(node.left);
}

return null;
}

function handleVariable(node: _babel_types.VariableDeclaration): string[] {
return node.declarations.filter(n => {
if (n.init.type === 'ArrowFunctionExpression') return true;
if (n.init.type === 'FunctionExpression') return true;
if (n.init?.type === 'ArrowFunctionExpression') return true;
if (n.init?.type === 'FunctionExpression') return true;
return false;
}).map(n => getAssignmentName(n.id));
}).map(n => getAssignmentName(n.id)).filter(Boolean) as string[];
}

function extractFunctionNames(nodes: (_babel_types.Statement | FunctionNode)[]) {
Expand All @@ -79,15 +81,19 @@ function extractFunctionNames(nodes: (_babel_types.Statement | FunctionNode)[])
}

if (node.type === "ExpressionStatement") {
names = [handleExpression(node.expression), ...names];
const name = handleExpression(node.expression);
if (name) names.push(name)
}

if (node.type === "VariableDeclaration") {
names = [...handleVariable(node), ...names];
}

if (node.type === "ClassProperty") {
names = [handleClassProp(node), ...extractFunctionNames([node.value]), ...names];
const propName = handleClassProp(node);
const funcNames = node.value ? extractFunctionNames([node.value]) : [];
if (propName) names.push(propName);
names = [...funcNames, ...names];
}
});

Expand Down
10 changes: 5 additions & 5 deletions src/utils/ObjectPropertyCodeRetriever.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ export class ObjectPropertyCodeRetriever {
${props.map(prop => {
let result = '';
const descriptor = Object.getOwnPropertyDescriptor(object, prop);
if (descriptor.get) {
if (descriptor?.get) {
result += `
${descriptor.get.toString()}
${descriptor?.get.toString()}
`;
}
if (descriptor.set) {
if (descriptor?.set) {
result += `
${descriptor.set.toString()}
${descriptor?.set.toString()}
`;
}
if (!descriptor.get && !descriptor.set && typeof object[prop] === 'function') {
if (!descriptor?.get && !descriptor?.set && typeof object[prop] === 'function') {
const propName = prop === 'constructor' ? 'mock_constructor' : '';
const fnStr = String(object[prop]);
result += `
Expand Down
1 change: 1 addition & 0 deletions test/matcher/type/AnyOfClassMatcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe("AnyOfClassMatcher", () => {
describe("checking if null matches null", () => {
it("throws error", () => {
try {
// @ts-ignore force type for test purposes
anyOfClass(null);
fail("If you reach this statement, the test failed.");
} catch (e) {
Expand Down
4 changes: 2 additions & 2 deletions test/matcher/type/DeepEqualMatcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe("deepEqual", () => {
describe("using in verify statements", () => {
it("can be used for equality", () => {
class Foo {
public add = (str: string, num: number, obj: {a: string}): number => null;
public add = (str: string, num: number, obj: {a: string}): number | null => null;
}
const foo = mock(Foo);
instance(foo).add("1", 2, {a: "sampleValue"});
Expand All @@ -138,7 +138,7 @@ describe("deepEqual", () => {
describe('when given circular dependency', () => {
type Bar = { bar?: Bar; };
class Foo {
public something = (bar: Bar): number => null;
public something = (bar: Bar): number | null => null;
}

it('should reject gracefully', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/mocking.types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,6 @@ class SampleGeneric<T> {
}

public getGenericTypedValue(): T {
return null;
return null as unknown as T;
}
}
4 changes: 2 additions & 2 deletions test/recording.multiple.behaviors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe("recording multiple behaviors", () => {

// when
const firstCallResult = foo.convertNumberToString(sampleValue);
let error: Error;
let error: Error | null = null;
try {
foo.convertNumberToString(sampleValue);
} catch (e) {
Expand All @@ -129,7 +129,7 @@ describe("recording multiple behaviors", () => {

// then
expect(firstCallResult).toEqual(firstMatchingStubResult);
expect(error.message).toEqual(firstMatchingError.message);
expect(error?.message).toEqual(firstMatchingError.message);
expect(thirdCallResult).toEqual(secondMatchingStubResult);
expect(fourthCallResult).toEqual(secondMatchingStubResult);
expect(fifthCallResult).toEqual(secondMatchingStubResult);
Expand Down
4 changes: 2 additions & 2 deletions test/stubbing.method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,15 @@ describe("mocking", () => {
when(mockedFoo.convertNumberToString(sampleValue)).thenThrow(sampleError);

// when
let error = null;
let error:Error | null = null;
try {
foo.convertNumberToString(sampleValue);
} catch (e) {
error = e;
}

// then
expect(error.message).toEqual("sampleError");
expect(error?.message).toEqual("sampleError");
});
});

Expand Down
4 changes: 2 additions & 2 deletions test/utils/Foo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ export class Foo {
}

public sampleMethodWithOptionalArgument(a: number, b?: number): number {
return a + b;
return a + (b ?? 0);
}

public sampleMethodWithTwoOptionalArguments(a?: number, b?: number): number {
return a + b;
return (a ?? 0) + (b ?? 0);
}

public sampleMethodReturningPromise(value: string): Promise<string> {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"removeComments": true,
"sourceMap": true,
"declaration": true,
"strictNullChecks": true,
"lib": [
"es5",
"es6",
Expand Down

0 comments on commit 6472f1a

Please sign in to comment.