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

Custom Jest matchers not working with Typescript #180

Closed
1 task done
andrewh318 opened this issue Aug 13, 2023 · 12 comments · Fixed by #198
Closed
1 task done

Custom Jest matchers not working with Typescript #180

andrewh318 opened this issue Aug 13, 2023 · 12 comments · Fixed by #198
Labels
bug Something isn't working

Comments

@andrewh318
Copy link

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

The custom Jest matchers don't seem to be properly supported with Typescript. I'm getting the following error when trying to use toHaveReceivedCommand. Note that I'm using import 'aws-sdk-client-mock-jest' as per the documentation.

Property 'toHaveReceivedCommand' does not exist on type 'Matchers<void, AwsStub<ServiceInputTypes, ServiceOutputTypes, S3ClientResolvedConfig>> & SnapshotMatchers<...> & Inverse<...> & PromiseMatchers<...>'

When I @ts-ignore the line the matcher works fine, so it seems like a typing issue? (for the expect call?). Is there any additional configuration required to get the matchers set up for Typescript?

Reproduction

import { describe, it, expect, beforeEach } from '@jest/globals';
import { PutObjectAclCommand, S3Client } from '@aws-sdk/client-s3';
import { mockClient } from 'aws-sdk-client-mock';
import 'aws-sdk-client-mock-jest';

describe('example', () => {
    const s3Mock = mockClient(S3Client);

    beforeEach(() => {
        s3Mock.reset();
    });
    it('test', async () => {
        // Property 'toHaveReceivedCommand' does not exist on type 'Matchers<void, AwsStub<ServiceInputTypes, ServiceOutputTypes, S3ClientResolvedConfig>> & SnapshotMatchers<...> & Inverse<...> & PromiseMatchers<...>'`
        expect(s3Mock).toHaveReceivedCommand(PutObjectAclCommand);
    });
});

Environment

  • Node version: 16.14.0
  • Testing lib and version: Jest
  • Typescript version: 5.0.4
  • AWS SDK v3 Client mock version: 3.0.0
  • AWS JS SDK libs and versions:
    • @aws-sdk/client-s3": "3.387.0"
@andrewh318 andrewh318 added the bug Something isn't working label Aug 13, 2023
@mx0c
Copy link

mx0c commented Aug 15, 2023

same here :(

@Tanemahuta
Copy link

Tanemahuta commented Aug 29, 2023

Try this:

import * as matchers from 'aws-sdk-client-mock-jest';

describe("blubb", () => {
  expect.extend(matchers);
});

Be happy.

@mx0c
Copy link

mx0c commented Aug 30, 2023

still doesn't seem to work:
image
image

@samchungy
Copy link
Contributor

The import modifies the global types which are only available when you install @types/jest

@orien
Copy link
Contributor

orien commented Sep 24, 2023

It would be awesome if it worked without using @types/jest. Jest now provides its own types via @jest/globals, as is used in the code block supplied with this issue.

@orien
Copy link
Contributor

orien commented Sep 24, 2023

A work around is to redeclare the matcher functions. ie. copy those in use from https://github.com/m-radzikowski/aws-sdk-client-mock/blob/v3.0.0/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts#L7-L70

  import { describe, it, expect, beforeEach } from '@jest/globals';
  import { PutObjectAclCommand, S3Client } from '@aws-sdk/client-s3';
- import { mockClient } from 'aws-sdk-client-mock';
+ import { MetadataBearer } from '@smithy/types'
+ import { AwsCommand, mockClient } from 'aws-sdk-client-mock';
  import 'aws-sdk-client-mock-jest';

+ declare module 'expect' {
+   interface Matchers<R> {
+     toHaveReceivedCommand<TCmdInput extends object, TCmdOutput extends MetadataBearer>
+       (command: new (input: TCmdInput) => AwsCommand<TCmdInput, TCmdOutput>): R;
+   }
+ }

  describe('example', () => {
      const s3Mock = mockClient(S3Client);

      beforeEach(() => {
          s3Mock.reset();
      });
      it('test', async () => {
          expect(s3Mock).toHaveReceivedCommand(PutObjectAclCommand);
      });
  });

@adiktofsugar
Copy link

adiktofsugar commented Nov 20, 2023

They solved this with testing-library in definitely typed like this: DefinitelyTyped/DefinitelyTyped#65981

I fixed in a similar way by adding to custom.d.ts:

import type { AwsSdkJestMockMatchers } from 'aws-sdk-client-mock-jest/dist/types/jestMatchers';

declare module 'expect' {
    interface Matchers<R = void, T = {}> extends AwsSdkJestMockMatchers<R> {}
}

It would be nice if that was just in the package, though.

EDIT: this seemed to have fixed the problem but apparently it was just VSCode fooling me. I expect something like this would work, though.

m-radzikowski added a commit that referenced this issue Dec 31, 2023
Jest now includes own types in @jest/globals packages.
Use it for custom Jest matchers types.
Add declaration exposing custom matchers
in the "expect" library types.

Fixes #180
m-radzikowski added a commit that referenced this issue Jan 1, 2024
Jest now includes own types in @jest/globals packages.
Use it for custom Jest matchers types.
Add declaration exposing custom matchers
in the "expect" library types.

Fixes #180
@m-radzikowski
Copy link
Owner

I've migrated to using @jest/globals types in Jest matcher definition, along with adding type definition for expect lib using under the hood. Now, matchers should work with both @types/jest and @jest/globals expect().

Released in v3.1.0-beta.0. Please test it if you have a moment.

@nmussy
Copy link

nmussy commented Mar 7, 2024

Hey @m-radzikowski, thanks for attempting to fix this, but it's still not working for me, neither for 3.1.0-beta.0 nor 3.1.0-beta.1. Let me know if I'm doing something wrong here:

import {
	DescribeDBEngineVersionsCommand,
	RDSClient,
} from "@aws-sdk/client-rds";
import { beforeEach, expect, it } from "@jest/globals";
import { mockClient } from "aws-sdk-client-mock";
import "aws-sdk-client-mock-jest";

const rdsMock = mockClient(RDSClient);

beforeEach(() => {
	rdsMock.reset();
});

it("demo", async () => {
	expect(rdsMock).toHaveReceivedCommand(DescribeDBEngineVersionsCommand);
});

package.json:

  "devDependencies": {
    "@jest/globals": "^29.7.0",
    "@types/node": "^20.11.24",
    "aws-sdk-client-mock": "3.1.0-beta.1",
    "aws-sdk-client-mock-jest": "^3.0.1",
    "esbuild": "^0.20.1",
    "esbuild-jest": "^0.5.0",
    "jest": "^29.7.0",
    "typescript": "^5.3.3"
  },
  "dependencies": {
    "@aws-sdk/client-rds": "^3.525.0"
  }

I get the following type error:

Property 'toHaveReceivedCommand' does not exist on type 'Matchers<void, AwsStub<object, MetadataBearer, unknown>> & SnapshotMatchers<void, AwsStub<object, MetadataBearer, unknown>> & Inverse<JestMatchers<void, AwsStub<object, MetadataBearer, unknown>>> & PromiseMatchers<...>'.ts(2339)

I've also tried to expect.extend(matchers);, to no avail

@m-radzikowski
Copy link
Owner

@nmussy you need to update aws-sdk-client-mock-jest to 3.1.0-beta.1 as well. Then your code works fine for me.

@nmussy
Copy link

nmussy commented Mar 17, 2024

That was my issue, thanks 👍
For reference, here is the corrected package.json dependencies:

  "devDependencies": {
    "@jest/globals": "^29.7.0",
    "@types/node": "^20.11.24",
    "aws-sdk-client-mock": "3.1.0-beta.1",
    "aws-sdk-client-mock-jest": "3.1.0-beta.1",
    "esbuild": "^0.20.1",
    "esbuild-jest": "^0.5.0",
    "jest": "^29.7.0",
    "typescript": "^5.3.3"
  },
  "dependencies": {
    "@aws-sdk/client-rds": "^3.525.0"
  }

@adiktofsugar
Copy link

3.1.0-beta.1 in both packages worked for me too. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants