diff --git a/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts b/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts index d4b5e7c..7493c04 100644 --- a/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts +++ b/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts @@ -53,6 +53,20 @@ interface AwsSdkJestMockBaseMatchers extends Record { command: new (input: TCmdInput) => AwsCommand, input: Partial, ): R; + + /** + * Asserts {@link AwsStub Aws Client Mock} received a {@link command} as defined specific {@link call} + * number with matchin {@link input} + * + * @param call call number to assert + * @param command aws-sdk command constructor + * @param input + */ + toHaveReceivedNthSpecificCommandWith( + call: number, + command: new (input: TCmdInput) => AwsCommand, + input: Partial + ): R; } interface AwsSdkJestMockAliasMatchers { @@ -108,6 +122,20 @@ interface AwsSdkJestMockAliasMatchers { command: new (input: TCmdInput) => AwsCommand, input: Partial, ): R; + + /** + * Asserts {@link AwsStub Aws Client Mock} received a {@link command} as defined specific {@link call} + * number with matchin {@link input} + * + * @param call call number to assert + * @param command aws-sdk command constructor + * @param input + */ + toReceiveNthSpecificCommandWith( + call: number, + command: new (input: TCmdInput) => AwsCommand, + input: Partial + ): R; } /** @@ -348,6 +376,65 @@ const baseMatchers: { [P in keyof AwsSdkJestMockBaseMatchers]: jest.Cus ], }); }, + /** + * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedNthSpecificCommandWith} matcher + */ + toHaveReceivedNthSpecificCommandWith( + this: jest.MatcherContext, + mockClient: ClientMock, + call: number, + command: new () => AnyCommand, + input?: Record, + ) { + assert( + call && typeof call === 'number' && call > 0, + 'Call number must be a number greater than 0', + ); + + return processMatch<{ received: AnyCommand | undefined }>({ + ctx: this, + mockClient, + command, + check: ({commandCalls}) => { + if (commandCalls.length < call) { + return {pass: false, data: {received: undefined}}; + } + + const received = commandCalls[call - 1].args[0]; + + let pass = false; + if (received instanceof command) { + try { + expect(received.input).toEqual( + expect.objectContaining(input), + ); + pass = true; + } catch (e) { // eslint-disable-line no-empty + } + } + + return { + pass, + data: {received}, + }; + }, + message: ({cmd, client, data, notPrefix}) => [ + `Expected ${client} to ${notPrefix}receive ${call}. ${cmd} with ${this.utils.printExpected(input)}`, + ...(data.received + ? [ + `${client} received ${this.utils.printReceived(data.received.constructor.name)} with input:`, + this.utils.printDiffOrStringify( + input, + data.received.input, + 'Expected', + 'Received', + false, + ), + ] + : []), + ], + }); + }, }; /* typing ensures keys matching */ @@ -356,6 +443,7 @@ const aliasMatchers: { [P in keyof AwsSdkJestMockAliasMatchers]: jest.C toReceiveCommand: baseMatchers.toHaveReceivedCommand, toReceiveCommandWith: baseMatchers.toHaveReceivedCommandWith, toReceiveNthCommandWith: baseMatchers.toHaveReceivedNthCommandWith, + toReceiveNthSpecificCommandWith: baseMatchers.toHaveReceivedNthSpecificCommandWith, }; // Skip registration if jest expect does not exist diff --git a/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts b/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts index f89f5d8..3180ca4 100644 --- a/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts +++ b/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts @@ -1,6 +1,6 @@ import {AwsClientStub, mockClient} from 'aws-sdk-client-mock'; import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; -import {publishCmd1, publishCmd2} from 'aws-sdk-client-mock/test/fixtures'; +import {publishCmd1, publishCmd2, subscribeCmd1} from 'aws-sdk-client-mock/test/fixtures'; import '../src'; let snsMock: AwsClientStub; @@ -241,3 +241,26 @@ Calls: `); }); }); + +describe('toHaveReceivedNthSpecificCommandWith', () => { + it('passes on receiving second Command with partial match', async () => { + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(subscribeCmd1); + await sns.send(publishCmd2); + + expect(() => expect(snsMock).toHaveReceivedNthSpecificCommandWith(2, PublishCommand, {Message: publishCmd2.input.Message})).not.toThrow(); + }); + + it('fails on receiving less Commands than the nth expected', async () => { + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).toHaveReceivedNthSpecificCommandWith(2, PublishCommand, {Message: publishCmd2.input.Message})).toThrowErrorMatchingInlineSnapshot(` +"Expected SNSClient to receive 2. \\"PublishCommand\\" with {\\"Message\\": \\"second mock message\\"} + +Calls: + 1. PublishCommand: {\\"Message\\": \\"mock message\\", \\"TopicArn\\": \\"arn:aws:sns:us-east-1:111111111111:MyTopic\\"}" +`); + }); +}); \ No newline at end of file diff --git a/packages/aws-sdk-client-mock/test/fixtures.ts b/packages/aws-sdk-client-mock/test/fixtures.ts index e7d1ff7..4f5cc1b 100644 --- a/packages/aws-sdk-client-mock/test/fixtures.ts +++ b/packages/aws-sdk-client-mock/test/fixtures.ts @@ -1,4 +1,4 @@ -import {PublishCommand} from '@aws-sdk/client-sns'; +import {PublishCommand, SubscribeCommand} from '@aws-sdk/client-sns'; export const topicArn = 'arn:aws:sns:us-east-1:111111111111:MyTopic'; export const topicArn2 = 'arn:aws:sns:us-east-1:111111111111:MyOtherTopic'; @@ -15,6 +15,10 @@ export const publishCmd3 = new PublishCommand({ TopicArn: topicArn2, Message: 'third mock message', }); +export const subscribeCmd1 = new SubscribeCommand({ + TopicArn: topicArn, + Protocol: 'sqs', +}); export const uuid1 = '12345678-1111-1111-1111-111122223333'; export const uuid2 = '12345678-2222-2222-2222-111122223333';