Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ jobs:
uses: pnpm/action-setup@v4
with:
version: 10
cache: true

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
Expand Down
4 changes: 3 additions & 1 deletion packages/testing/ts-jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@
"access": "public"
},
"devDependencies": {
"@aws-sdk/client-s3": "^3.962.0",
"@jest/globals": "^27.4.3",
"jest": "^27.4.3",
"jest-mock": "27.5.1"
"jest-mock": "27.5.1",
"typeorm": "^0.3.28"
},
"jest": {
"moduleFileExtensions": [
Expand Down
15 changes: 15 additions & 0 deletions packages/testing/ts-jest/src/mocks.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ExecutionContext } from '@nestjs/common';
import { HttpArgumentsHost } from '@nestjs/common/interfaces';
import { Test, TestingModule } from '@nestjs/testing';
import { S3Client } from '@aws-sdk/client-s3';
import { createMock, DeepMocked } from './mocks';
import { Repository } from 'typeorm';

interface TestInterface {
someNum: number;
Expand Down Expand Up @@ -222,6 +224,19 @@ describe('Mocks', () => {
expect(result).toBe(true);
});

it('should handle real AWS SDK S3Client without type instantiation errors', () => {
// This is the real-world case that triggers TS2589 with the current DeepMocked implementation
const s3Client: S3Client = createMock<S3Client>();
expect(s3Client).toBeDefined();
});

it('should handle real TypeORM Repository without too complex of a type errors', () => {
// This is the real-world case that triggers TS2590: with the current DeepMocked implementation
const repo: Repository<{ a: number }> =
createMock<Repository<{ a: number }>>();
expect(repo).toBeDefined();
});

it('should work with classes having nested properties', () => {
const mock = createMock<TestClass>();
mock.nested.someOtherMethod.mockReturnValueOnce(99);
Expand Down
21 changes: 17 additions & 4 deletions packages/testing/ts-jest/src/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-empty-object-type */
import { jest } from '@jest/globals';
import { Mock } from 'jest-mock';

Expand All @@ -17,11 +18,23 @@ export type PartialFuncReturn<T> = {
: DeepPartial<T[K]>;
};

type IsExactlyUnknown<T> = unknown extends T
? T extends {}
? false
: true
: false;

export type DeepMocked<T> = {
[K in keyof T]: Required<T>[K] extends (...args: any[]) => infer U
? jest.MockInstance<ReturnType<Required<T>[K]>, jest.ArgsType<T[K]>> &
((...args: jest.ArgsType<T[K]>) => DeepMocked<U>)
: DeepMocked<T[K]>;
[K in keyof T]: IsExactlyUnknown<T[K]> extends true
? any
: NonNullable<T[K]> extends (...args: any[]) => infer U
? jest.MockInstance<ReturnType<NonNullable<T[K]>>, jest.ArgsType<T[K]>> &
((...args: jest.ArgsType<T[K]>) => DeepMocked<U>)
: NonNullable<T[K]> extends object
? undefined extends T[K]
? DeepMocked<NonNullable<T[K]>> | undefined
: DeepMocked<T[K]>
: T[K];
} & T;

const jestFnProps = new Set([
Expand Down
Loading