From d4f8eb51fc4c8243d6039886e1f26bd3dc1f3888 Mon Sep 17 00:00:00 2001 From: Stefan Schulte Date: Sun, 12 Jan 2025 13:17:18 +0100 Subject: [PATCH 1/5] maint: Rephrase tests The common practice seems to be to not call tests "it should" but rather specify the expected behaviour as facts, e.g. use "it passes" instead of "it should pass" --- tests/index.test.ts | 2 +- tests/matcher.test.ts | 232 +++++++++++++++++++++--------------------- tests/utils.test.ts | 8 +- 3 files changed, 121 insertions(+), 121 deletions(-) diff --git a/tests/index.test.ts b/tests/index.test.ts index 0f8223c..fd94e3a 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -52,7 +52,7 @@ describe('aws-sdk-client-mock-vitest', () => { 'toReceiveLastCommandWith', 'toReceiveNthCommandWith', 'toReceiveAnyCommand', - ])('should be able to extend with %s', (matcher) => { + ])('extend matcher to extend with %s', (matcher) => { expect(expect('something')).toHaveProperty(matcher); }); diff --git a/tests/matcher.test.ts b/tests/matcher.test.ts index 14ec7bf..d11c530 100644 --- a/tests/matcher.test.ts +++ b/tests/matcher.test.ts @@ -43,12 +43,12 @@ expect.extend({ }); describe('toReceiveCommandTimes', () => { - it('should pass with 0 received commands', () => { + it('passes with 0 received commands', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toReceiveCommandTimes(PutObjectCommand, 0); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -60,7 +60,7 @@ describe('toReceiveCommandTimes', () => { expect(s3Mock).toReceiveCommandTimes(GetBucketAclCommand, 3); }); - it.fails('should fail when command is not called as specified', async () => { + it.fails('fails when command is not called as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -71,19 +71,19 @@ describe('toReceiveCommandTimes', () => { }); describe('not', () => { - it('should pass when not called', () => { + it('passes when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toReceiveCommandTimes(PutObjectCommand, 1); }); - it('should pass when called different than specified', async () => { + it('passes when called different than specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).not.toReceiveCommandTimes(PutObjectCommand, 2); }); - it.fails('should fail when command received as specified', async () => { + it.fails('fails when command received as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -93,12 +93,12 @@ describe('toReceiveCommandTimes', () => { }); describe('toHaveReceivedCommandTimes', () => { - it('should pass with 0 received commands', () => { + it('passes with 0 received commands', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toHaveReceivedCommandTimes(PutObjectCommand, 0); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -110,7 +110,7 @@ describe('toHaveReceivedCommandTimes', () => { expect(s3Mock).toHaveReceivedCommandTimes(GetBucketAclCommand, 3); }); - it.fails('should fail when command is not called as specified', async () => { + it.fails('fails when command is not called as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -121,19 +121,19 @@ describe('toHaveReceivedCommandTimes', () => { }); describe('not', () => { - it('should pass when not called', () => { + it('passes when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toHaveReceivedCommandTimes(PutObjectCommand, 1); }); - it('should pass when called different than specified', async () => { + it('passes when called different than specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).not.toHaveReceivedCommandTimes(PutObjectCommand, 2); }); - it.fails('should fail when command received as specified', async () => { + it.fails('fails when command received as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -143,19 +143,19 @@ describe('toHaveReceivedCommandTimes', () => { }); describe('toReceiveCommandOnce', () => { - it('should pass when called exactly once', async () => { + it('passes when called exactly once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); }); - it.fails('should fail when not called', () => { + it.fails('fails when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); }); - it.fails('should fail when called more than once', async () => { + it.fails('fails when called more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -163,7 +163,7 @@ describe('toReceiveCommandOnce', () => { expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -173,26 +173,26 @@ describe('toReceiveCommandOnce', () => { }); describe('not', () => { - it('should pass when not called', () => { + it('passes when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); }); - it.fails('should fail when called exactly once', async () => { + it.fails('fails when called exactly once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); }); - it('should pass when called more than once', async () => { + it('passes when called more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); @@ -202,19 +202,19 @@ describe('toReceiveCommandOnce', () => { }); describe('toHaveReceivedCommandOnce', () => { - it('should pass when called exactly once', async () => { + it('passes when called exactly once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); }); - it.fails('should fail when not called', () => { + it.fails('fails when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); }); - it.fails('should fail when called more than once', async () => { + it.fails('fails when called more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -222,7 +222,7 @@ describe('toHaveReceivedCommandOnce', () => { expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -232,19 +232,19 @@ describe('toHaveReceivedCommandOnce', () => { }); describe('not', () => { - it('should pass when not called', () => { + it('passes when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toHaveReceivedCommandOnce(GetObjectCommand); }); - it.fails('should fail when called exactly once', async () => { + it.fails('fails when called exactly once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).not.toHaveReceivedCommandOnce(GetObjectCommand); }); - it('should pass when called more than once', async () => { + it('passes when called more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -252,7 +252,7 @@ describe('toHaveReceivedCommandOnce', () => { expect(s3Mock).not.toHaveReceivedCommandOnce(GetObjectCommand); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); @@ -262,19 +262,19 @@ describe('toHaveReceivedCommandOnce', () => { }); describe('toReceiveCommand', () => { - it.fails('should fail when no command received', () => { + it.fails('fails when no command received', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toReceiveCommand(GetObjectCommand); }); - it('should pass when received command once', async () => { + it('passes when received command once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).toReceiveCommand(GetObjectCommand); }); - it('should pass when received command more than once', async () => { + it('passes when received command more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -282,7 +282,7 @@ describe('toReceiveCommand', () => { expect(s3Mock).toReceiveCommand(GetObjectCommand); }); - it.fails('should fail when only received other commands', async () => { + it.fails('fails when only received other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); @@ -290,26 +290,26 @@ describe('toReceiveCommand', () => { }); describe('not', () => { - it('should pass when no command received', () => { + it('passes when no command received', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toReceiveCommand(GetObjectCommand); }); - it('should pass when other commands received', async () => { + it('passes when other commands received', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); expect(s3Mock).not.toReceiveCommand(GetObjectCommand); }); - it.fails('should fail when command received once', async () => { + it.fails('fails when command received once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).not.toReceiveCommand(GetObjectCommand); }); - it.fails('should fail when command received multiple times', async () => { + it.fails('fails when command received multiple times', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -320,19 +320,19 @@ describe('toReceiveCommand', () => { }); describe('toHaveReceivedCommand', () => { - it.fails('should fail when no command received', () => { + it.fails('fails when no command received', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); }); - it('should pass when received command once', async () => { + it('passes when received command once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); }); - it('should pass when received command more than once', async () => { + it('passes when received command more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -340,7 +340,7 @@ describe('toHaveReceivedCommand', () => { expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); }); - it.fails('should fail when only received other commands', async () => { + it.fails('fails when only received other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); @@ -348,26 +348,26 @@ describe('toHaveReceivedCommand', () => { }); describe('not', () => { - it('should pass when no command received', () => { + it('passes when no command received', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); }); - it('should pass when other commands received', async () => { + it('passes when other commands received', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); }); - it.fails('should fail when command received once', async () => { + it.fails('fails when command received once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); }); - it.fails('should fail when command received multiple times', async () => { + it.fails('fails when command received multiple times', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -378,7 +378,7 @@ describe('toHaveReceivedCommand', () => { }); describe('toReceiveCommandWith', () => { - it('should pass when received with command', async () => { + it('passes when received with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -388,7 +388,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when received with partial command', async () => { + it('passes when received with partial command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -397,7 +397,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when received at least once with command', async () => { + it('passes when received at least once with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'bucket1', Key: 'key1' })); @@ -410,7 +410,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when received at least once with partial command', async () => { + it('passes when received at least once with partial command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'bucket1', Key: 'key1' })); @@ -422,7 +422,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when received mutliple times with command', async () => { + it('passes when received mutliple times with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -433,7 +433,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when received mutliple times with partial command', async () => { + it('passes when received mutliple times with partial command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test1.txt' })); @@ -443,7 +443,7 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('should fail when received with wrong command', async () => { + it.fails('fails when received with wrong command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -453,7 +453,7 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('should fail when input does not match', async () => { + it.fails('fails when input does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -463,7 +463,7 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('should fail when input misses fields', async () => { + it.fails('fails when input misses fields', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -475,7 +475,7 @@ describe('toReceiveCommandWith', () => { }); describe('not', () => { - it('shoud pass when never called', () => { + it('passes when never called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toReceiveCommandWith(PutObjectCommand, { Bucket: 'foo', @@ -483,7 +483,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when not called with input', async () => { + it('passes when not called with input', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -493,7 +493,7 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('should fail when one matching input', async () => { + it.fails('fails when one matching input', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -505,7 +505,7 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('should fail on partial match', async () => { + it.fails('fails on partial match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -516,7 +516,7 @@ describe('toReceiveCommandWith', () => { }); }); - it('should pass when called with additional arguments', async () => { + it('passes when called with additional arguments', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -530,7 +530,7 @@ describe('toReceiveCommandWith', () => { }); describe('toHaveReceivedCommandWith', () => { - it('should pass when received with command', async () => { + it('passes when received with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -540,7 +540,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when received with partial command', async () => { + it('passes when received with partial command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -549,7 +549,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when received at least once with command', async () => { + it('passes when received at least once with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'bucket1', Key: 'key1' })); @@ -562,7 +562,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when received at least once with partial command', async () => { + it('passes when received at least once with partial command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'bucket1', Key: 'key1' })); @@ -574,7 +574,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when received mutliple times with command', async () => { + it('passes when received mutliple times with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -585,7 +585,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when received mutliple times with partial command', async () => { + it('passes when received mutliple times with partial command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test1.txt' })); @@ -595,7 +595,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('should fail when received with wrong command', async () => { + it.fails('fails when received with wrong command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -605,7 +605,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('should fail when input does not match', async () => { + it.fails('fails when input does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -615,7 +615,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('should fail when input misses fields', async () => { + it.fails('fails when input misses fields', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -627,7 +627,7 @@ describe('toHaveReceivedCommandWith', () => { }); describe('not', () => { - it('shoud pass when never called', () => { + it('passes when never called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toHaveReceivedCommandWith(PutObjectCommand, { Bucket: 'foo', @@ -635,7 +635,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when not called with input', async () => { + it('passes when not called with input', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -645,7 +645,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('should fail when one matching input', async () => { + it.fails('fails when one matching input', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -657,7 +657,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('should fail on partial match', async () => { + it.fails('fails on partial match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -668,7 +668,7 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it('should pass when called with additional arguments', async () => { + it('passes when called with additional arguments', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); @@ -682,7 +682,7 @@ describe('toHaveReceivedCommandWith', () => { }); describe('toReceiveNthCommandWith', () => { - it('should pass when command matches', async () => { + it('passes when command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -694,7 +694,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it('should pass when command partially matches', async () => { + it('passes when command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -705,7 +705,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -717,7 +717,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it.fails('should fail when number is incorrect', async () => { + it.fails('fails when number is incorrect', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -729,7 +729,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it.fails('should fail when call is beyond actual calls', async () => { + it.fails('fails when call is beyond actual calls', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -740,7 +740,7 @@ describe('toReceiveNthCommandWith', () => { }); describe('not', () => { - it('should succeed when command does not match', async () => { + it('succeeds when command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -752,7 +752,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it('should succeed when command has missing inputs', async () => { + it('succeeds when command has missing inputs', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -765,7 +765,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it('should succeed when call is beyond actual calls', async () => { + it('succeeds when call is beyond actual calls', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -775,7 +775,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it.fails('should fail when call matches', async () => { + it.fails('fails when call matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -785,7 +785,7 @@ describe('toReceiveNthCommandWith', () => { }); }); - it.fails('should fail when call partially matches', async () => { + it.fails('fails when call partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -797,7 +797,7 @@ describe('toReceiveNthCommandWith', () => { }); describe('toHaveReceivedNthCommandWith', () => { - it('should pass when command matches', async () => { + it('passes when command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -809,7 +809,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it('should pass when command partially matches', async () => { + it('passes when command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -820,7 +820,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -832,7 +832,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it.fails('should fail when number is incorrect', async () => { + it.fails('fails when number is incorrect', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -844,7 +844,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it.fails('should fail when call is beyond actual calls', async () => { + it.fails('fails when call is beyond actual calls', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -855,7 +855,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); describe('not', () => { - it('should succeed when command does not match', async () => { + it('succeeds when command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -867,7 +867,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it('should succeed when command has missing inputs', async () => { + it('succeeds when command has missing inputs', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -880,7 +880,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it('should succeed when call is beyond actual calls', async () => { + it('succeeds when call is beyond actual calls', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -890,7 +890,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it.fails('should fail when call matches', async () => { + it.fails('fails when call matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -900,7 +900,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it.fails('should fail when call partially matches', async () => { + it.fails('fails when call partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -912,7 +912,7 @@ describe('toHaveReceivedNthCommandWith', () => { }); describe('toReceiveLastCommandWith', () => { - it('should pass when only command matches', async () => { + it('passes when only command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -922,7 +922,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it('should pass when last command matches', async () => { + it('passes when last command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -934,7 +934,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it('should pass when last command partially matches', async () => { + it('passes when last command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -945,7 +945,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -957,7 +957,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('should fail when only command does not match', async () => { + it.fails('fails when only command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -967,7 +967,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('should fail when last command does not match', async () => { + it.fails('fails when last command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -979,7 +979,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('should fail when not called at all', () => { + it.fails('fails when not called at all', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { Bucket: 'foo', @@ -988,7 +988,7 @@ describe('toReceiveLastCommandWith', () => { }); describe('not', () => { - it('should pass when not called', () => { + it('passes when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toReceiveLastCommandWith(GetObjectCommand, { Bucket: 'foo', @@ -996,7 +996,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it('should pass when last command does not match', async () => { + it('passes when last command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1007,7 +1007,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it('should pass when input misses fields', async () => { + it('passes when input misses fields', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1019,7 +1019,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('should fail when last command matches', async () => { + it.fails('fails when last command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1030,7 +1030,7 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('should fail when last command partially matches', async () => { + it.fails('fails when last command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1043,7 +1043,7 @@ describe('toReceiveLastCommandWith', () => { }); describe('toHaveReceivedLastCommandWith', () => { - it('should pass when only command matches', async () => { + it('passes when only command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1053,7 +1053,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it('should pass when last command matches', async () => { + it('passes when last command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1065,7 +1065,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it('should pass when last command partially matches', async () => { + it('passes when last command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1076,7 +1076,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it('should ignore other commands', async () => { + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1088,7 +1088,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('should fail when only command does not match', async () => { + it.fails('fails when only command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1098,7 +1098,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('should fail when last command does not match', async () => { + it.fails('fails when last command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1110,7 +1110,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('should fail when not called at all', () => { + it.fails('fails when not called at all', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { Bucket: 'foo', @@ -1119,7 +1119,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); describe('not', () => { - it('should pass when not called', () => { + it('passes when not called', () => { const s3Mock = mockClient(S3Client); expect(s3Mock).not.toHaveReceivedLastCommandWith(GetObjectCommand, { Bucket: 'foo', @@ -1127,7 +1127,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it('should pass when last command does not match', async () => { + it('passes when last command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1138,7 +1138,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it('should pass when input misses fields', async () => { + it('passes when input misses fields', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1150,7 +1150,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('should fail when last command matches', async () => { + it.fails('fails when last command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); @@ -1161,7 +1161,7 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('should fail when last command partially matches', async () => { + it.fails('fails when last command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 14cd895..bcff592 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -3,15 +3,15 @@ import { describe, expect, it } from 'vitest'; import { notNull, ordinalOf } from '../src/utils.js'; describe('notNull', () => { - it('should return true when not null', () => { + it('returns true when not null', () => { expect(notNull('Hi')).toBe(true); }); - it('should return false when null', () => { + it('returns false when null', () => { expect(notNull(null)).toBe(false); }); - it('should return true when undefined', () => { + it('returns true when undefined', () => { expect(notNull(undefined)).toBe(true); }); }); @@ -39,7 +39,7 @@ describe('ordinalOf', () => { [1022, '1022nd'], [1023, '1023rd'], ]; - it.each(cases)('should translate %d to %s', (a, b) => { + it.each(cases)('translates %d to %s', (a, b) => { expect(ordinalOf(a)).toStrictEqual(b); }); }); From 127921c051b58a989adc42075d347304a90d4bf9 Mon Sep 17 00:00:00 2001 From: Stefan Schulte Date: Sun, 12 Jan 2025 13:50:01 +0100 Subject: [PATCH 2/5] maint: Add tests for asymmetric matchers Whenever compare call arguments (e.g. `toHaveReceivedCommandWith` we internally use ObjectContaining and `asymmetricMatch` of `vitest`. This is not only to allow partial matches, but also in order to use something like `expect.any(String)` or `expect.stringMatching()`. This now adds some tests to verify this does actually work as intended. --- tests/matcher.test.ts | 121 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tests/matcher.test.ts b/tests/matcher.test.ts index d11c530..3fc2163 100644 --- a/tests/matcher.test.ts +++ b/tests/matcher.test.ts @@ -6,6 +6,7 @@ import { S3Client, } from '@aws-sdk/client-s3'; import { mockClient } from 'aws-sdk-client-mock'; +import { randomUUID } from 'node:crypto'; import { describe, expect, it } from 'vitest'; import { @@ -397,6 +398,21 @@ describe('toReceiveCommandWith', () => { }); }); + it('passes with a correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }); + it('passes when received at least once with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); @@ -474,6 +490,21 @@ describe('toReceiveCommandWith', () => { }); }); + it.fails('fails on failed asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }); + describe('not', () => { it('passes when never called', () => { const s3Mock = mockClient(S3Client); @@ -516,6 +547,21 @@ describe('toReceiveCommandWith', () => { }); }); + it.fails('fails on correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }); + it('passes when called with additional arguments', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); @@ -526,6 +572,21 @@ describe('toReceiveCommandWith', () => { VersionId: 'abc', }); }); + + it('passes on incorrect asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }); }); }); @@ -549,6 +610,21 @@ describe('toHaveReceivedCommandWith', () => { }); }); + it('passes with a correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }); + it('passes when received at least once with command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); @@ -626,6 +702,21 @@ describe('toHaveReceivedCommandWith', () => { }); }); + it.fails('fails on failed asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }); + describe('not', () => { it('passes when never called', () => { const s3Mock = mockClient(S3Client); @@ -668,6 +759,21 @@ describe('toHaveReceivedCommandWith', () => { }); }); + it.fails('fails on correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }); + it('passes when called with additional arguments', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); @@ -678,6 +784,21 @@ describe('toHaveReceivedCommandWith', () => { VersionId: 'abc', }); }); + + it('passes on incorrect asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }); }); }); From 91f9dc0905f71669eef2b5a0ed822ae7154dd5fd Mon Sep 17 00:00:00 2001 From: Stefan Schulte Date: Fri, 17 Jan 2025 15:58:34 +0100 Subject: [PATCH 3/5] maint: Update dependencies This includes a major version bump of @vitest/expect, so we should later also bump the major version of our package. --- CHANGELOG.md | 4 + package-lock.json | 1047 ++++++++++++++++++++++----------------------- package.json | 20 +- 3 files changed, 530 insertions(+), 541 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3046d1c..e216943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Update dependencies. This bumps `@vitest/expect` dependency to `^3.0.1` + ## [5.1.0] - 2025-01-11 ### Changed diff --git a/package-lock.json b/package-lock.json index a17818e..34fc9d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,25 +9,25 @@ "version": "5.1.0", "license": "MIT", "dependencies": { - "@vitest/expect": "^2.1.3", - "tslib": "^2.8.0" + "@vitest/expect": "^3.0.2", + "tslib": "^2.8.1" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.726.1", - "@aws-sdk/client-secrets-manager": "^3.726.1", + "@aws-sdk/client-s3": "^3.730.0", + "@aws-sdk/client-secrets-manager": "^3.730.0", "@eslint/js": "^9.18.0", - "@stylistic/eslint-plugin": "^2.12.1", + "@stylistic/eslint-plugin": "^2.13.0", "@types/eslint__js": "^8.42.3", - "@types/node": "^22.10.5", - "@vitest/coverage-v8": "^2.1.8", - "@vitest/eslint-plugin": "^1.1.24", + "@types/node": "^22.10.7", + "@vitest/coverage-v8": "^3.0.2", + "@vitest/eslint-plugin": "^1.1.25", "aws-sdk-client-mock": "^4.1.0", "eslint": "^9.18.0", "eslint-config-flat-gitignore": "^1.0.0", "eslint-plugin-perfectionist": "^4.6.0", "typescript": "^5.7.3", - "typescript-eslint": "^8.19.1", - "vitest": "^2.1.8" + "typescript-eslint": "^8.20.0", + "vitest": "^3.0.2" }, "peerDependencies": { "@smithy/types": ">=3.5.0", @@ -267,35 +267,33 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.726.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.726.1.tgz", - "integrity": "sha512-UpOGcob87DiuS2d3fW6vDZg94g57mNiOSkzvR/6GOdvBSlUgk8LLwVzGASB71FdKMl1EGEr4MeD5uKH9JsG+dw==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.730.0.tgz", + "integrity": "sha512-45RAzBWCR3E2p9Oale1TqIHPxqcbJoNmLDU+z/5kqnLml32Ntkvioe3BRMq+eaSmiFtOyviF1snfuxPUkRrUWA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.726.0", - "@aws-sdk/client-sts": "3.726.1", - "@aws-sdk/core": "3.723.0", - "@aws-sdk/credential-provider-node": "3.726.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", "@aws-sdk/middleware-bucket-endpoint": "3.726.0", "@aws-sdk/middleware-expect-continue": "3.723.0", - "@aws-sdk/middleware-flexible-checksums": "3.723.0", + "@aws-sdk/middleware-flexible-checksums": "3.730.0", "@aws-sdk/middleware-host-header": "3.723.0", "@aws-sdk/middleware-location-constraint": "3.723.0", "@aws-sdk/middleware-logger": "3.723.0", "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-sdk-s3": "3.723.0", + "@aws-sdk/middleware-sdk-s3": "3.730.0", "@aws-sdk/middleware-ssec": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/middleware-user-agent": "3.730.0", "@aws-sdk/region-config-resolver": "3.723.0", - "@aws-sdk/signature-v4-multi-region": "3.723.0", + "@aws-sdk/signature-v4-multi-region": "3.730.0", "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-endpoints": "3.730.0", "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.726.0", + "@aws-sdk/util-user-agent-node": "3.730.0", "@aws-sdk/xml-builder": "3.723.0", "@smithy/config-resolver": "^4.0.0", "@smithy/core": "^3.0.0", @@ -337,27 +335,25 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.726.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.726.1.tgz", - "integrity": "sha512-eO9WpE8IyQrs2xWhfQCdHcVTHQTwJ56JGx3FhwhtFWWYHIS0c1bTIAvP5E3jSWAZNaK1iWdVexz3yGi3aAnGzA==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.730.0.tgz", + "integrity": "sha512-ysc8dXQrTICMCTOboJDJCHxI9pKG+r9trejHatR4lv5ayDwLVgQ3hv1VxImYlyZVHpBWzGK3fFn+cPCwmIp4mA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.726.0", - "@aws-sdk/client-sts": "3.726.1", - "@aws-sdk/core": "3.723.0", - "@aws-sdk/credential-provider-node": "3.726.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", "@aws-sdk/middleware-host-header": "3.723.0", "@aws-sdk/middleware-logger": "3.723.0", "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/middleware-user-agent": "3.730.0", "@aws-sdk/region-config-resolver": "3.723.0", "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-endpoints": "3.730.0", "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.726.0", + "@aws-sdk/util-user-agent-node": "3.730.0", "@smithy/config-resolver": "^4.0.0", "@smithy/core": "^3.0.0", "@smithy/fetch-http-handler": "^5.0.0", @@ -392,130 +388,24 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.726.0.tgz", - "integrity": "sha512-NM5pjv2qglEc4XN3nnDqtqGsSGv1k5YTmzDo3W3pObItHmpS8grSeNfX9zSH+aVl0Q8hE4ZIgvTPNZ+GzwVlqg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.723.0", - "@aws-sdk/middleware-host-header": "3.723.0", - "@aws-sdk/middleware-logger": "3.723.0", - "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.726.0", - "@aws-sdk/region-config-resolver": "3.723.0", - "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.726.0", - "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.726.0", - "@smithy/config-resolver": "^4.0.0", - "@smithy/core": "^3.0.0", - "@smithy/fetch-http-handler": "^5.0.0", - "@smithy/hash-node": "^4.0.0", - "@smithy/invalid-dependency": "^4.0.0", - "@smithy/middleware-content-length": "^4.0.0", - "@smithy/middleware-endpoint": "^4.0.0", - "@smithy/middleware-retry": "^4.0.0", - "@smithy/middleware-serde": "^4.0.0", - "@smithy/middleware-stack": "^4.0.0", - "@smithy/node-config-provider": "^4.0.0", - "@smithy/node-http-handler": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", - "@smithy/types": "^4.0.0", - "@smithy/url-parser": "^4.0.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.0", - "@smithy/util-defaults-mode-node": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", - "@smithy/util-middleware": "^4.0.0", - "@smithy/util-retry": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.726.0.tgz", - "integrity": "sha512-5JzTX9jwev7+y2Jkzjz0pd1wobB5JQfPOQF3N2DrJ5Pao0/k6uRYwE4NqB0p0HlGrMTDm7xNq7OSPPIPG575Jw==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.730.0.tgz", + "integrity": "sha512-mI8kqkSuVlZklewEmN7jcbBMyVODBld3MsTjCKSl5ztduuPX69JD7nXLnWWPkw1PX4aGTO24AEoRMGNxntoXUg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.723.0", - "@aws-sdk/credential-provider-node": "3.726.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/middleware-host-header": "3.723.0", "@aws-sdk/middleware-logger": "3.723.0", "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/middleware-user-agent": "3.730.0", "@aws-sdk/region-config-resolver": "3.723.0", "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-endpoints": "3.730.0", "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.726.0", - "@smithy/config-resolver": "^4.0.0", - "@smithy/core": "^3.0.0", - "@smithy/fetch-http-handler": "^5.0.0", - "@smithy/hash-node": "^4.0.0", - "@smithy/invalid-dependency": "^4.0.0", - "@smithy/middleware-content-length": "^4.0.0", - "@smithy/middleware-endpoint": "^4.0.0", - "@smithy/middleware-retry": "^4.0.0", - "@smithy/middleware-serde": "^4.0.0", - "@smithy/middleware-stack": "^4.0.0", - "@smithy/node-config-provider": "^4.0.0", - "@smithy/node-http-handler": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", - "@smithy/types": "^4.0.0", - "@smithy/url-parser": "^4.0.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.0", - "@smithy/util-defaults-mode-node": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", - "@smithy/util-middleware": "^4.0.0", - "@smithy/util-retry": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.726.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.726.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.726.1.tgz", - "integrity": "sha512-qh9Q9Vu1hrM/wMBOBIaskwnE4GTFaZu26Q6WHwyWNfj7J8a40vBxpW16c2vYXHLBtwRKM1be8uRLkmDwghpiNw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.726.0", - "@aws-sdk/core": "3.723.0", - "@aws-sdk/credential-provider-node": "3.726.0", - "@aws-sdk/middleware-host-header": "3.723.0", - "@aws-sdk/middleware-logger": "3.723.0", - "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.726.0", - "@aws-sdk/region-config-resolver": "3.723.0", - "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.726.0", - "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.726.0", + "@aws-sdk/util-user-agent-node": "3.730.0", "@smithy/config-resolver": "^4.0.0", "@smithy/core": "^3.0.0", "@smithy/fetch-http-handler": "^5.0.0", @@ -548,9 +438,9 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.723.0.tgz", - "integrity": "sha512-UraXNmvqj3vScSsTkjMwQkhei30BhXlW5WxX6JacMKVtl95c7z0qOXquTWeTalYkFfulfdirUhvSZrl+hcyqTw==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.730.0.tgz", + "integrity": "sha512-jonKyR+2GcqbZj2WDICZS0c633keLc9qwXnePu83DfAoFXMMIMyoR/7FOGf8F3OrIdGh8KzE9VvST+nZCK9EJA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -571,13 +461,13 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.723.0.tgz", - "integrity": "sha512-OuH2yULYUHTVDUotBoP/9AEUIJPn81GQ/YBtZLoo2QyezRJ2QiO/1epVtbJlhNZRwXrToLEDmQGA2QfC8c7pbA==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.730.0.tgz", + "integrity": "sha512-fFXgo3jBXLWqu8I07Hd96mS7RjrtpDgm3bZShm0F3lKtqDQF+hObFWq9A013SOE+RjMLVfbABhToXAYct3FcBw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/property-provider": "^4.0.0", "@smithy/types": "^4.0.0", @@ -588,13 +478,13 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.723.0.tgz", - "integrity": "sha512-DTsKC6xo/kz/ZSs1IcdbQMTgiYbpGTGEd83kngFc1bzmw7AmK92DBZKNZpumf8R/UfSpTcj9zzUUmrWz1kD0eQ==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.730.0.tgz", + "integrity": "sha512-1aF3elbCzpVhWLAuV63iFElfLOqLGGTp4fkf2VAFIDO3hjshpXUQssTgIWiBwwtJYJdOSxaFrCU7u8frjr/5aQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/fetch-http-handler": "^5.0.0", "@smithy/node-http-handler": "^4.0.0", @@ -610,18 +500,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.726.0.tgz", - "integrity": "sha512-seTtcKL2+gZX6yK1QRPr5mDJIBOatrpoyrO8D5b8plYtV/PDbDW3mtDJSWFHet29G61ZmlNElyXRqQCXn9WX+A==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.730.0.tgz", + "integrity": "sha512-zwsxkBuQuPp06o45ATAnznHzj3+ibop/EaTytNzSv0O87Q59K/jnS/bdtv1n6bhe99XCieRNTihvtS7YklzK7A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", - "@aws-sdk/credential-provider-env": "3.723.0", - "@aws-sdk/credential-provider-http": "3.723.0", - "@aws-sdk/credential-provider-process": "3.723.0", - "@aws-sdk/credential-provider-sso": "3.726.0", - "@aws-sdk/credential-provider-web-identity": "3.723.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/credential-provider-imds": "^4.0.0", "@smithy/property-provider": "^4.0.0", @@ -631,24 +522,21 @@ }, "engines": { "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.726.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.726.0.tgz", - "integrity": "sha512-jjsewBcw/uLi24x8JbnuDjJad4VA9ROCE94uVRbEnGmUEsds75FWOKp3fWZLQlmjLtzsIbJOZLALkZP86liPaw==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.730.0.tgz", + "integrity": "sha512-ztRjh1edY7ut2wwrj1XqHtqPY/NXEYIk5fYf04KKsp8zBi81ScVqP7C+Cst6PFKixjgLSG6RsqMx9GSAalVv0Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.723.0", - "@aws-sdk/credential-provider-http": "3.723.0", - "@aws-sdk/credential-provider-ini": "3.726.0", - "@aws-sdk/credential-provider-process": "3.723.0", - "@aws-sdk/credential-provider-sso": "3.726.0", - "@aws-sdk/credential-provider-web-identity": "3.723.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/credential-provider-imds": "^4.0.0", "@smithy/property-provider": "^4.0.0", @@ -661,13 +549,13 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.723.0.tgz", - "integrity": "sha512-fgupvUjz1+jeoCBA7GMv0L6xEk92IN6VdF4YcFhsgRHlHvNgm7ayaoKQg7pz2JAAhG/3jPX6fp0ASNy+xOhmPA==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.730.0.tgz", + "integrity": "sha512-cNKUQ81eptfZN8MlSqwUq3+5ln8u/PcY57UmLZ+npxUHanqO1akpgcpNsLpmsIkoXGbtSQrLuDUgH86lS/SWOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/property-provider": "^4.0.0", "@smithy/shared-ini-file-loader": "^4.0.0", @@ -679,15 +567,15 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.726.0.tgz", - "integrity": "sha512-WxkN76WeB08j2yw7jUH9yCMPxmT9eBFd9ZA/aACG7yzOIlsz7gvG3P2FQ0tVg25GHM0E4PdU3p/ByTOawzcOAg==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.730.0.tgz", + "integrity": "sha512-SdI2xrTbquJLMxUh5LpSwB8zfiKq3/jso53xWRgrVfeDlrSzZuyV6QghaMs3KEEjcNzwEnTfSIjGQyRXG9VrEw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.726.0", - "@aws-sdk/core": "3.723.0", - "@aws-sdk/token-providers": "3.723.0", + "@aws-sdk/client-sso": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/token-providers": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/property-provider": "^4.0.0", "@smithy/shared-ini-file-loader": "^4.0.0", @@ -699,13 +587,14 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.723.0.tgz", - "integrity": "sha512-tl7pojbFbr3qLcOE6xWaNCf1zEfZrIdSJtOPeSXfV/thFMMAvIjgf3YN6Zo1a6cxGee8zrV/C8PgOH33n+Ev/A==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.730.0.tgz", + "integrity": "sha512-l5vdPmvF/d890pbvv5g1GZrdjaSQkyPH/Bc8dO/ZqkWxkIP8JNgl48S2zgf4DkP3ik9K2axWO828L5RsMDQzdA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/property-provider": "^4.0.0", "@smithy/types": "^4.0.0", @@ -713,9 +602,6 @@ }, "engines": { "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.723.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { @@ -754,16 +640,16 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.723.0.tgz", - "integrity": "sha512-JY76mrUCLa0FHeMZp8X9+KK6uEuZaRZaQrlgq6zkXX/3udukH0T3YdFC+Y9uw5ddbiwZ5+KwgmlhnPpiXKfP4g==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.730.0.tgz", + "integrity": "sha512-D0YfQWJ32xo3DvFKcTuM5Aq0IxVr8N++ChVeK3ENYcdelUpxWA9xplZH3QTSbbM1iH3qs2maeAAnLyAns26aGg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/is-array-buffer": "^4.0.0", "@smithy/node-config-provider": "^4.0.0", @@ -841,13 +727,13 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.723.0.tgz", - "integrity": "sha512-wfjOvNJVp8LDWhq4wO5jtSMb8Vgf4tNlR7QTEQfoYc6AGU3WlK5xyUQcpfcpwytEhQTN9u0cJLQpSyXDO+qSCw==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.730.0.tgz", + "integrity": "sha512-5EJWF8cV5SWqGQfQWDd1sklawm5grUa6H/QHGAhmCdcw+isz675qg/+c970I5rBsSEky83ialjBQIirrBo7DYQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/types": "3.723.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/core": "^3.0.0", @@ -882,15 +768,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.726.0.tgz", - "integrity": "sha512-hZvzuE5S0JmFie1r68K2wQvJbzyxJFdzltj9skgnnwdvLe8F/tz7MqLkm28uV0m4jeHk0LpiBo6eZaPkQiwsZQ==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.730.0.tgz", + "integrity": "sha512-aPMZvNmf2a42B41au3bA3ODU4HfHka2nYT/SAIhhVXH1ENYfAmZo7FraFPxetKepFMCtL7j4QE6/LDucK6liIw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.723.0", + "@aws-sdk/core": "3.730.0", "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-endpoints": "3.730.0", "@smithy/core": "^3.0.0", "@smithy/protocol-http": "^5.0.0", "@smithy/types": "^4.0.0", @@ -900,6 +786,56 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.730.0.tgz", + "integrity": "sha512-vilIgf1/7kre8DdE5zAQkDOwHFb/TahMn/6j2RZwFLlK7cDk91r19deSiVYnKQkupDMtOfNceNqnorM4I3PDzw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz", @@ -919,13 +855,13 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.723.0.tgz", - "integrity": "sha512-lJlVAa5Sl589qO8lwMLVUtnlF1Q7I+6k1Iomv2goY9d1bRl4q2N5Pit2qJVr2AMW0sceQXeh23i2a/CKOqVAdg==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.730.0.tgz", + "integrity": "sha512-/a6ndc+oNnFwolBdB4QgAK7iO4cge0vm9hEp7ZeFQCviX64UmsWH/UdIhsi7wvKEXdIo1XR9v3O4o6UwcQngXg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.723.0", + "@aws-sdk/middleware-sdk-s3": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/protocol-http": "^5.0.0", "@smithy/signature-v4": "^5.0.0", @@ -937,12 +873,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.723.0.tgz", - "integrity": "sha512-hniWi1x4JHVwKElANh9afKIMUhAutHVBRD8zo6usr0PAoj+Waf220+1ULS74GXtLXAPCiNXl5Og+PHA7xT8ElQ==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.730.0.tgz", + "integrity": "sha512-BSPssGj54B/AABWXARIPOT/1ybFahM1ldlfmXy9gRmZi/afe9geWJGlFYCCt3PmqR+1Ny5XIjSfue+kMd//drQ==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/nested-clients": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/property-provider": "^4.0.0", "@smithy/shared-ini-file-loader": "^4.0.0", @@ -951,9 +888,6 @@ }, "engines": { "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.723.0" } }, "node_modules/@aws-sdk/types": { @@ -984,9 +918,9 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.726.0.tgz", - "integrity": "sha512-sLd30ASsPMoPn3XBK50oe/bkpJ4N8Bpb7SbhoxcY3Lk+fSASaWxbbXE81nbvCnkxrZCvkPOiDHzJCp1E2im71A==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.730.0.tgz", + "integrity": "sha512-1KTFuVnk+YtLgWr6TwDiggcDqtPpOY2Cszt3r2lkXfaEAX6kHyOZi1vdvxXjPU5LsOBJem8HZ7KlkmrEi+xowg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1026,13 +960,13 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.726.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.726.0.tgz", - "integrity": "sha512-iEj6KX9o6IQf23oziorveRqyzyclWai95oZHDJtYav3fvLJKStwSjygO4xSF7ycHcTYeCHSLO1FFOHgGVs4Viw==", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.730.0.tgz", + "integrity": "sha512-yBvkOAjqsDEl1va4eHNOhnFBk0iCY/DBFNyhvtTMqPF4NO+MITWpFs3J9JtZKzJlQ6x0Yb9TLQ8NhDjEISz5Ug==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/middleware-user-agent": "3.730.0", "@aws-sdk/types": "3.723.0", "@smithy/node-config-provider": "^4.0.0", "@smithy/types": "^4.0.0", @@ -1115,16 +1049,19 @@ } }, "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "cpu": [ "ppc64" ], @@ -1135,13 +1072,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", "cpu": [ "arm" ], @@ -1152,13 +1089,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", "cpu": [ "arm64" ], @@ -1169,13 +1106,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", "cpu": [ "x64" ], @@ -1186,13 +1123,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "cpu": [ "arm64" ], @@ -1203,13 +1140,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", "cpu": [ "x64" ], @@ -1220,13 +1157,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", "cpu": [ "arm64" ], @@ -1237,13 +1174,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", "cpu": [ "x64" ], @@ -1254,13 +1191,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", "cpu": [ "arm" ], @@ -1271,13 +1208,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", "cpu": [ "arm64" ], @@ -1288,13 +1225,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", "cpu": [ "ia32" ], @@ -1305,13 +1242,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", "cpu": [ "loong64" ], @@ -1322,13 +1259,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", "cpu": [ "mips64el" ], @@ -1339,13 +1276,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", "cpu": [ "ppc64" ], @@ -1356,13 +1293,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", "cpu": [ "riscv64" ], @@ -1373,13 +1310,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", "cpu": [ "s390x" ], @@ -1390,13 +1327,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "cpu": [ "x64" ], @@ -1407,13 +1344,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", "cpu": [ "x64" ], @@ -1424,13 +1378,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", "cpu": [ "x64" ], @@ -1441,13 +1412,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", "cpu": [ "x64" ], @@ -1458,13 +1429,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", "cpu": [ "arm64" ], @@ -1475,13 +1446,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", "cpu": [ "ia32" ], @@ -1492,13 +1463,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", "cpu": [ "x64" ], @@ -1509,7 +1480,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -2276,9 +2247,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.0.tgz", - "integrity": "sha512-swFv0wQiK7TGHeuAp6lfF5Kw1dHWsTrCuc+yh4Kh05gEShjsE2RUxHucEerR9ih9JITNtaHcSpUThn5Y/vDw0A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.1.tgz", + "integrity": "sha512-hhUZlBWYuh9t6ycAcN90XOyG76C1AzwxZZgaCVPMYpWqqk9uMFo7HGG5Zu2cEhCJn7DdOi5krBmlibWWWPgdsw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2287,7 +2258,7 @@ "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.0.1", + "@smithy/util-stream": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -2509,13 +2480,13 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.1.tgz", - "integrity": "sha512-hCCOPu9+sRI7Wj0rZKKnGylKXBEd9cQJetzjQqe8cT4PWvtQAbvNVa6cgAONiZg9m8LaXtP9/waxm3C3eO4hiw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.2.tgz", + "integrity": "sha512-Z9m67CXizGpj8CF/AW/7uHqYNh1VXXOn9Ap54fenWsCa0HnT4cJuE61zqG3cBkTZJDCy0wHJphilI41co/PE5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.1.0", + "@smithy/core": "^3.1.1", "@smithy/middleware-serde": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", @@ -2529,16 +2500,16 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.1.tgz", - "integrity": "sha512-n3g2zZFgOWaz2ZYCy8+4wxSmq+HSTD8QKkRhFDv+nkxY1o7gzyp4PDz/+tOdcNPMPZ/A6Mt4aVECYNjQNiaHJw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.3.tgz", + "integrity": "sha512-TiKwwQTwUDeDtwWW8UWURTqu7s6F3wN2pmziLU215u7bqpVT9Mk2oEvURjpRLA+5XeQhM68R5BpAGzVtomsqgA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/service-error-classification": "^4.0.1", - "@smithy/smithy-client": "^4.1.0", + "@smithy/smithy-client": "^4.1.2", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", @@ -2594,9 +2565,9 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.1.tgz", - "integrity": "sha512-ddQc7tvXiVLC5c3QKraGWde761KSk+mboCheZoWtuqnXh5l0WKyFy3NfDIM/dsKrI9HlLVH/21pi9wWK2gUFFA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz", + "integrity": "sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2715,18 +2686,18 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.0.tgz", - "integrity": "sha512-NiboZnrsrZY+Cy5hQNbYi+nVNssXVi2I+yL4CIKNIanOhH8kpC5PKQ2jx/MQpwVr21a3XcVoQBArlpRF36OeEQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.2.tgz", + "integrity": "sha512-0yApeHWBqocelHGK22UivZyShNxFbDNrgREBllGh5Ws0D0rg/yId/CJfeoKKpjbfY2ju8j6WgDUGZHYQmINZ5w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.1.0", - "@smithy/middleware-endpoint": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/middleware-endpoint": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.0.1", + "@smithy/util-stream": "^4.0.2", "tslib": "^2.6.2" }, "engines": { @@ -2829,14 +2800,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.1.tgz", - "integrity": "sha512-nkQifWzWUHw/D0aLPgyKut+QnJ5X+5E8wBvGfvrYLLZ86xPfVO6MoqfQo/9s4bF3Xscefua1M6KLZtobHMWrBg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.3.tgz", + "integrity": "sha512-7c5SF1fVK0EOs+2EOf72/qF199zwJflU1d02AevwKbAUPUZyE9RUZiyJxeUmhVxfKDWdUKaaVojNiaDQgnHL9g==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.0", + "@smithy/smithy-client": "^4.1.2", "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -2846,9 +2817,9 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.1.tgz", - "integrity": "sha512-LeAx2faB83litC9vaOdwFaldtto2gczUHxfFf8yoRwDU3cwL4/pDm7i0hxsuBCRk5mzHsrVGw+3EVCj32UZMdw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.3.tgz", + "integrity": "sha512-CVnD42qYD3JKgDlImZ9+On+MqJHzq9uJgPbMdeBE8c2x8VJ2kf2R3XO/yVFx+30ts5lD/GlL0eFIShY3x9ROgQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2856,7 +2827,7 @@ "@smithy/credential-provider-imds": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.0", + "@smithy/smithy-client": "^4.1.2", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -2922,14 +2893,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.1.tgz", - "integrity": "sha512-Js16gOgU6Qht6qTPfuJgb+1YD4AEO+5Y1UPGWKSp3BNo8ONl/qhXSYDhFKJtwybRJynlCqvP5IeiaBsUmkSPTQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz", + "integrity": "sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", @@ -2984,9 +2955,9 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.12.1.tgz", - "integrity": "sha512-fubZKIHSPuo07FgRTn6S4Nl0uXPRPYVNpyZzIDGfp7Fny6JjNus6kReLD7NI380JXi4HtUTSOZ34LBuNPO1XLQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.13.0.tgz", + "integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3039,9 +3010,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", - "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "dev": true, "license": "MIT", "dependencies": { @@ -3073,17 +3044,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", - "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/type-utils": "8.19.1", - "@typescript-eslint/utils": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3103,16 +3074,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz", - "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -3128,14 +3099,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz", - "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3146,14 +3117,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz", - "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -3170,9 +3141,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz", - "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "dev": true, "license": "MIT", "engines": { @@ -3184,14 +3155,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz", - "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3211,16 +3182,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz", - "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3235,13 +3206,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz", - "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3253,31 +3224,31 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", - "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.2.tgz", + "integrity": "sha512-U+hZYb0FtgNDb6B3E9piAHzXXIuxuBw2cd6Lvepc9sYYY4KjgiwCBmo3Sird9ZRu3ggLpLBTfw1ZRr77ipiSfw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.7", + "@bcoe/v8-coverage": "^1.0.2", + "debug": "^4.4.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.12", + "magic-string": "^0.30.17", "magicast": "^0.3.5", "std-env": "^3.8.0", "test-exclude": "^7.0.1", - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.8", - "vitest": "2.1.8" + "@vitest/browser": "3.0.2", + "vitest": "3.0.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -3286,9 +3257,9 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.1.24", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.24.tgz", - "integrity": "sha512-7IaENe4NNy33g0iuuy5bHY69JYYRjpv4lMx6H5Wp30W7ez2baLHwxsXF5TM4wa8JDYZt8ut99Ytoj7GiDO01hw==", + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.25.tgz", + "integrity": "sha512-u8DpDnMbPcqBmJOB4PeEtn6q7vKmLVTLFMpzoxSAo0hjYdl4iYSHRleqwPQo0ywc7UV0S6RKIahYRQ3BnZdMVw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3307,37 +3278,37 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", - "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.2.tgz", + "integrity": "sha512-dKSHLBcoZI+3pmP5hiZ7I5grNru2HRtEW8Z5Zp4IXog8QYcxhlox7JUPyIIFWfN53+3HW3KPLIl6nSzUGgKSuQ==", "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", + "@vitest/spy": "3.0.2", + "@vitest/utils": "3.0.2", "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", - "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.2.tgz", + "integrity": "sha512-Hr09FoBf0jlwwSyzIF4Xw31OntpO3XtZjkccpcBf8FeVW3tpiyKlkeUzxS/txzHqpUCNIX157NaTySxedyZLvA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.8", + "@vitest/spy": "3.0.2", "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0" + "vite": "^5.0.0 || ^6.0.0" }, "peerDependenciesMeta": { "msw": { @@ -3349,50 +3320,50 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", - "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.2.tgz", + "integrity": "sha512-yBohcBw/T/p0/JRgYD+IYcjCmuHzjC3WLAKsVE4/LwiubzZkE8N49/xIQ/KGQwDRA8PaviF8IRO8JMWMngdVVQ==", "license": "MIT", "dependencies": { - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", - "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.2.tgz", + "integrity": "sha512-GHEsWoncrGxWuW8s405fVoDfSLk6RF2LCXp6XhevbtDjdDme1WV/eNmUueDfpY1IX3MJaCRelVCEXsT9cArfEg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.8", - "pathe": "^1.1.2" + "@vitest/utils": "3.0.2", + "pathe": "^2.0.1" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", - "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.2.tgz", + "integrity": "sha512-h9s67yD4+g+JoYG0zPCo/cLTabpDqzqNdzMawmNPzDStTiwxwkyYM1v5lWE8gmGv3SVJ2DcxA2NpQJZJv9ym3g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.8", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" + "@vitest/pretty-format": "3.0.2", + "magic-string": "^0.30.17", + "pathe": "^2.0.1" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", - "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.2.tgz", + "integrity": "sha512-8mI2iUn+PJFMT44e3ISA1R+K6ALVs47W6eriDTfXe6lFqlflID05MB4+rIFhmDSLBj8iBsZkzBYlgSkinxLzSQ==", "license": "MIT", "dependencies": { "tinyspy": "^3.0.2" @@ -3402,14 +3373,14 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", - "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.2.tgz", + "integrity": "sha512-Qu01ZYZlgHvDP02JnMBRpX43nRaZtNpIzw3C1clDXmn8eakgX6iQVGzTQ/NjkIr64WD8ioqOjkaYRVvHQI5qiw==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.8", + "@vitest/pretty-format": "3.0.2", "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -3719,9 +3690,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3729,32 +3700,34 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/escape-string-regexp": { @@ -4818,9 +4791,9 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", "dev": true, "license": "MIT" }, @@ -4854,9 +4827,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -4874,7 +4847,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5283,9 +5256,9 @@ } }, "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "license": "MIT", "engines": { "node": ">=14.0.0" @@ -5370,15 +5343,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.1.tgz", - "integrity": "sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.20.0.tgz", + "integrity": "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.19.1", - "@typescript-eslint/parser": "8.19.1", - "@typescript-eslint/utils": "8.19.1" + "@typescript-eslint/eslint-plugin": "8.20.0", + "@typescript-eslint/parser": "8.20.0", + "@typescript-eslint/utils": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5424,21 +5397,21 @@ } }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", + "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.24.2", + "postcss": "^8.4.49", + "rollup": "^4.23.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5447,19 +5420,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -5480,74 +5459,80 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, "node_modules/vite-node": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", - "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.2.tgz", + "integrity": "sha512-hsEQerBAHvVAbv40m3TFQe/lTEbOp7yDpyqMJqr2Tnd+W58+DEYOt+fluQgekOePcsNBmR77lpVAnIU2Xu4SvQ==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.1", + "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/vitest": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", - "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.2.tgz", + "integrity": "sha512-5bzaHakQ0hmVVKLhfh/jXf6oETDBtgPo8tQCHYB+wftNgFJ+Hah67IsWc8ivx4vFL025Ow8UiuTf4W57z4izvQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.8", - "@vitest/mocker": "2.1.8", - "@vitest/pretty-format": "^2.1.8", - "@vitest/runner": "2.1.8", - "@vitest/snapshot": "2.1.8", - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", + "@vitest/expect": "3.0.2", + "@vitest/mocker": "3.0.2", + "@vitest/pretty-format": "^3.0.2", + "@vitest/runner": "3.0.2", + "@vitest/snapshot": "3.0.2", + "@vitest/spy": "3.0.2", + "@vitest/utils": "3.0.2", "chai": "^5.1.2", - "debug": "^4.3.7", + "debug": "^4.4.0", "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", + "magic-string": "^0.30.17", + "pathe": "^2.0.1", "std-env": "^3.8.0", "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.8", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.2", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.8", - "@vitest/ui": "2.1.8", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.2", + "@vitest/ui": "3.0.2", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 13c4c71..ebb9136 100644 --- a/package.json +++ b/package.json @@ -38,24 +38,24 @@ "aws-sdk-client-mock": ">=2.2.0" }, "dependencies": { - "@vitest/expect": "^2.1.3", - "tslib": "^2.8.0" + "@vitest/expect": "^3.0.2", + "tslib": "^2.8.1" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.726.1", - "@aws-sdk/client-secrets-manager": "^3.726.1", + "@aws-sdk/client-s3": "^3.730.0", + "@aws-sdk/client-secrets-manager": "^3.730.0", "@eslint/js": "^9.18.0", - "@stylistic/eslint-plugin": "^2.12.1", + "@stylistic/eslint-plugin": "^2.13.0", "@types/eslint__js": "^8.42.3", - "@types/node": "^22.10.5", - "@vitest/coverage-v8": "^2.1.8", - "@vitest/eslint-plugin": "^1.1.24", + "@types/node": "^22.10.7", + "@vitest/coverage-v8": "^3.0.2", + "@vitest/eslint-plugin": "^1.1.25", "aws-sdk-client-mock": "^4.1.0", "eslint": "^9.18.0", "eslint-config-flat-gitignore": "^1.0.0", "eslint-plugin-perfectionist": "^4.6.0", "typescript": "^5.7.3", - "typescript-eslint": "^8.19.1", - "vitest": "^2.1.8" + "typescript-eslint": "^8.20.0", + "vitest": "^3.0.2" } } From 62b3657076f8aff0f39c81bf4e7115f8191d16f4 Mon Sep 17 00:00:00 2001 From: Stefan Schulte Date: Fri, 17 Jan 2025 15:59:13 +0100 Subject: [PATCH 4/5] maint: Refactor failing tests When testing our custom matcher we want to ensure they also fail a test appropiatly (e.g. you test a mock was never called, but it was indeed called; we want to know this fails) Previously we did write a test like a user would write the test but instead of using `it` we use `if.fails`. While this did work, we are unable to validate *why* the test fails. We now don't assume failing tests but instead we wrap these in `expect(...).toThrow`. This should not only be easier to read but also allows us to capture the error message to validate the test fails for the correct reason. --- tests/matcher.test.ts | 515 +++++++++++++++++++++++++----------------- 1 file changed, 313 insertions(+), 202 deletions(-) diff --git a/tests/matcher.test.ts b/tests/matcher.test.ts index 3fc2163..f25083b 100644 --- a/tests/matcher.test.ts +++ b/tests/matcher.test.ts @@ -61,14 +61,16 @@ describe('toReceiveCommandTimes', () => { expect(s3Mock).toReceiveCommandTimes(GetBucketAclCommand, 3); }); - it.fails('fails when command is not called as specified', async () => { + it('fails when command is not called as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); await s3.send(new GetBucketAclCommand({ Bucket: 'bar' })); await s3.send(new GetBucketAclCommand({ Bucket: 'baz' })); - expect(s3Mock).toReceiveCommandTimes(GetBucketAclCommand, 1); + expect(() => { + expect(s3Mock).toReceiveCommandTimes(GetBucketAclCommand, 1); + }).toThrow(/expected "GetBucketAclCommand" to be called 1 times, but got 3 times/); }); describe('not', () => { @@ -84,11 +86,13 @@ describe('toReceiveCommandTimes', () => { expect(s3Mock).not.toReceiveCommandTimes(PutObjectCommand, 2); }); - it.fails('fails when command received as specified', async () => { + it('fails when command received as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).not.toReceiveCommandTimes(GetObjectCommand, 1); + expect(() => { + expect(s3Mock).not.toReceiveCommandTimes(GetObjectCommand, 1); + }).toThrow(/expected "GetObjectCommand" to not be called 1 times/); }); }); }); @@ -111,14 +115,16 @@ describe('toHaveReceivedCommandTimes', () => { expect(s3Mock).toHaveReceivedCommandTimes(GetBucketAclCommand, 3); }); - it.fails('fails when command is not called as specified', async () => { + it('fails when command is not called as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); await s3.send(new GetBucketAclCommand({ Bucket: 'bar' })); await s3.send(new GetBucketAclCommand({ Bucket: 'baz' })); - expect(s3Mock).toHaveReceivedCommandTimes(GetBucketAclCommand, 1); + expect(() => { + expect(s3Mock).toHaveReceivedCommandTimes(GetBucketAclCommand, 1); + }).toThrow(/expected "GetBucketAclCommand" to be called 1 times, but got 3 times/); }); describe('not', () => { @@ -134,11 +140,13 @@ describe('toHaveReceivedCommandTimes', () => { expect(s3Mock).not.toHaveReceivedCommandTimes(PutObjectCommand, 2); }); - it.fails('fails when command received as specified', async () => { + it('fails when command received as specified', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).not.toReceiveCommandTimes(GetObjectCommand, 1); + expect(() => { + expect(s3Mock).not.toReceiveCommandTimes(GetObjectCommand, 1); + }).toThrow(/expected "GetObjectCommand" to not be called 1 times/); }); }); }); @@ -151,17 +159,21 @@ describe('toReceiveCommandOnce', () => { expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); }); - it.fails('fails when not called', () => { + it('fails when not called', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); + expect(() => { + expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called once, but got 0 times/); }); - it.fails('fails when called more than once', async () => { + it('fails when called more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); - expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); + expect(() => { + expect(s3Mock).toReceiveCommandOnce(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called once, but got 2 times/); }); it('ignores other commands', async () => { @@ -179,11 +191,13 @@ describe('toReceiveCommandOnce', () => { expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); }); - it.fails('fails when called exactly once', async () => { + it('fails when called exactly once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); + expect(() => { + expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to not be called once/); }); it('passes when called more than once', async () => { @@ -193,6 +207,7 @@ describe('toReceiveCommandOnce', () => { await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); expect(s3Mock).not.toReceiveCommandOnce(GetObjectCommand); }); + it('ignores other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); @@ -210,17 +225,21 @@ describe('toHaveReceivedCommandOnce', () => { expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); }); - it.fails('fails when not called', () => { + it('fails when not called', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); + expect(() => { + expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called once, but got 0 times/); }); - it.fails('fails when called more than once', async () => { + it('fails when called more than once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); - expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); + expect(() => { + expect(s3Mock).toHaveReceivedCommandOnce(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called once, but got 2 times/); }); it('ignores other commands', async () => { @@ -238,11 +257,13 @@ describe('toHaveReceivedCommandOnce', () => { expect(s3Mock).not.toHaveReceivedCommandOnce(GetObjectCommand); }); - it.fails('fails when called exactly once', async () => { + it('fails when called exactly once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).not.toHaveReceivedCommandOnce(GetObjectCommand); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommandOnce(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to not be called once/); }); it('passes when called more than once', async () => { @@ -263,9 +284,11 @@ describe('toHaveReceivedCommandOnce', () => { }); describe('toReceiveCommand', () => { - it.fails('fails when no command received', () => { + it('fails when no command received', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toReceiveCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).toReceiveCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called at least once/); }); it('passes when received command once', async () => { @@ -283,11 +306,13 @@ describe('toReceiveCommand', () => { expect(s3Mock).toReceiveCommand(GetObjectCommand); }); - it.fails('fails when only received other commands', async () => { + it('fails when only received other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); - expect(s3Mock).toReceiveCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).toReceiveCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called at least once/); }); describe('not', () => { @@ -303,11 +328,13 @@ describe('toReceiveCommand', () => { expect(s3Mock).not.toReceiveCommand(GetObjectCommand); }); - it.fails('fails when command received once', async () => { + it('fails when command received once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).not.toReceiveCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).not.toReceiveCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to not be called at all, but actually been called 1 times/); }); it.fails('fails when command received multiple times', async () => { @@ -321,9 +348,11 @@ describe('toReceiveCommand', () => { }); describe('toHaveReceivedCommand', () => { - it.fails('fails when no command received', () => { + it('fails when no command received', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called at least once/); }); it('passes when received command once', async () => { @@ -341,11 +370,13 @@ describe('toHaveReceivedCommand', () => { expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); }); - it.fails('fails when only received other commands', async () => { + it('fails when only received other commands', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetBucketAclCommand({ Bucket: 'foo' })); - expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).toHaveReceivedCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to be called at least once/); }); describe('not', () => { @@ -361,19 +392,23 @@ describe('toHaveReceivedCommand', () => { expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); }); - it.fails('fails when command received once', async () => { + it('fails when command received once', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to not be called at all, but actually been called 1 times/); }); - it.fails('fails when command received multiple times', async () => { + it('fails when command received multiple times', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); - expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommand(GetObjectCommand); + }).toThrow(/expected "GetObjectCommand" to not be called at all, but actually been called 2 times/); }); }); }); @@ -459,38 +494,44 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('fails when received with wrong command', async () => { + it('fails when received with wrong command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).toReceiveCommandWith(PutObjectCommand, { - Bucket: 'foo', - Key: 'test.txt', - }); + expect(() => { + expect(s3Mock).toReceiveCommandWith(PutObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }).toThrow(/expected "PutObjectCommand" to be called with arguments/); }); - it.fails('fails when input does not match', async () => { + it('fails when input does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'wrongkey.txt', - }); + expect(() => { + expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'wrongkey.txt', + }); + }).toThrow(/expected "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when input misses fields', async () => { + it('fails when input misses fields', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'test.txt', - VersionId: '10', - }); + expect(() => { + expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + VersionId: '10', + }); + }).toThrow(/expected "GetObjectCommand" to be called with arguments/); }); - it.fails('fails on failed asymmetric match', async () => { + it('fails on failed asymmetric match', async () => { // Assume code that uses a random string for the bucket key with a known extension const name = randomUUID().toString(); const s3Mock = mockClient(S3Client); @@ -499,10 +540,12 @@ describe('toReceiveCommandWith', () => { // asymmetric matchers like stringMatching are typed as `any` so we cast them // as we want to compare them to strings - expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: expect.stringMatching(/.jpg/) as string, - }); + expect(() => { + expect(s3Mock).toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to be called with arguments/); }); describe('not', () => { @@ -524,30 +567,34 @@ describe('toReceiveCommandWith', () => { }); }); - it.fails('fails when one matching input', async () => { + it('fails when one matching input', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'baz', Key: 'test.txt' })); - expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { - Bucket: 'bar', - Key: 'test.txt', - }); + expect(() => { + expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { + Bucket: 'bar', + Key: 'test.txt', + }); + }).toThrow(/expected "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails on partial match', async () => { + it('fails on partial match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'baz', Key: 'test.txt' })); - expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { - Bucket: 'bar', - }); + expect(() => { + expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { + Bucket: 'bar', + }); + }).toThrow(/expected "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails on correct asymmetric match', async () => { + it('fails on correct asymmetric match', async () => { // Assume code that uses a random string for the bucket key with a known extension const name = randomUUID().toString(); const s3Mock = mockClient(S3Client); @@ -556,10 +603,12 @@ describe('toReceiveCommandWith', () => { // asymmetric matchers like stringMatching are typed as `any` so we cast them // as we want to compare them to strings - expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: expect.stringMatching(/.txt$/) as string, - }); + expect(() => { + expect(s3Mock).not.toReceiveCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to not be called with arguments/); }); it('passes when called with additional arguments', async () => { @@ -671,38 +720,44 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('fails when received with wrong command', async () => { + it('fails when received with wrong command', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).toHaveReceivedCommandWith(PutObjectCommand, { - Bucket: 'foo', - Key: 'test.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedCommandWith(PutObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }).toThrow(/expected "PutObjectCommand" to be called with arguments/); }); - it.fails('fails when input does not match', async () => { + it('fails when input does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'wrongkey.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'wrongkey.txt', + }); + }).toThrow(/expected "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when input misses fields', async () => { + it('fails when input misses fields', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); - expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'test.txt', - VersionId: '10', - }); + expect(() => { + expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + VersionId: '10', + }); + }).toThrow(/expected "GetObjectCommand" to be called with arguments/); }); - it.fails('fails on failed asymmetric match', async () => { + it('fails on failed asymmetric match', async () => { // Assume code that uses a random string for the bucket key with a known extension const name = randomUUID().toString(); const s3Mock = mockClient(S3Client); @@ -711,10 +766,12 @@ describe('toHaveReceivedCommandWith', () => { // asymmetric matchers like stringMatching are typed as `any` so we cast them // as we want to compare them to strings - expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: expect.stringMatching(/.jpg/) as string, - }); + expect(() => { + expect(s3Mock).toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to be called with arguments/); }); describe('not', () => { @@ -736,30 +793,34 @@ describe('toHaveReceivedCommandWith', () => { }); }); - it.fails('fails when one matching input', async () => { + it('fails when one matching input', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'baz', Key: 'test.txt' })); - expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { - Bucket: 'bar', - Key: 'test.txt', - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'bar', + Key: 'test.txt', + }); + }).toThrow(/expected "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails on partial match', async () => { + it('fails on partial match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'baz', Key: 'test.txt' })); - expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { - Bucket: 'bar', - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'bar', + }); + }).toThrow(/expected "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails on correct asymmetric match', async () => { + it('fails on correct asymmetric match', async () => { // Assume code that uses a random string for the bucket key with a known extension const name = randomUUID().toString(); const s3Mock = mockClient(S3Client); @@ -768,10 +829,12 @@ describe('toHaveReceivedCommandWith', () => { // asymmetric matchers like stringMatching are typed as `any` so we cast them // as we want to compare them to strings - expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: expect.stringMatching(/.txt$/) as string, - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to not be called with arguments/); }); it('passes when called with additional arguments', async () => { @@ -838,26 +901,30 @@ describe('toReceiveNthCommandWith', () => { }); }); - it.fails('fails when number is incorrect', async () => { + it('fails when number is incorrect', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file3.txt' })); - expect(s3Mock).toReceiveNthCommandWith(GetObjectCommand, 3, { - Bucket: 'foo', - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).toReceiveNthCommandWith(GetObjectCommand, 3, { + Bucket: 'foo', + Key: 'file2.txt', + }); + }).toThrow(/expected 3rd "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when call is beyond actual calls', async () => { + it('fails when call is beyond actual calls', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).toReceiveNthCommandWith(GetObjectCommand, 2, { - Bucket: 'foo', - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).toReceiveNthCommandWith(GetObjectCommand, 2, { + Bucket: 'foo', + Key: 'file2.txt', + }); + }).toThrow(/expected 2nd "GetObjectCommand" to be called with arguments/); }); describe('not', () => { @@ -896,23 +963,27 @@ describe('toReceiveNthCommandWith', () => { }); }); - it.fails('fails when call matches', async () => { + it('fails when call matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).not.toReceiveNthCommandWith(GetObjectCommand, 1, { - Bucket: 'foo', - Key: 'file1.txt', - }); + expect(() => { + expect(s3Mock).not.toReceiveNthCommandWith(GetObjectCommand, 1, { + Bucket: 'foo', + Key: 'file1.txt', + }); + }).toThrow(/expected 1st "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails when call partially matches', async () => { + it('fails when call partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).not.toReceiveNthCommandWith(GetObjectCommand, 1, { - Bucket: 'foo', - }); + expect(() => { + expect(s3Mock).not.toReceiveNthCommandWith(GetObjectCommand, 1, { + Bucket: 'foo', + }); + }).toThrow(/expected 1st "GetObjectCommand" to not be called with arguments/); }); }); }); @@ -953,26 +1024,30 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it.fails('fails when number is incorrect', async () => { + it('fails when number is incorrect', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file3.txt' })); - expect(s3Mock).toHaveReceivedNthCommandWith(GetObjectCommand, 3, { - Bucket: 'foo', - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedNthCommandWith(GetObjectCommand, 3, { + Bucket: 'foo', + Key: 'file2.txt', + }); + }).toThrow(/expected 3rd "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when call is beyond actual calls', async () => { + it('fails when call is beyond actual calls', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).toHaveReceivedNthCommandWith(GetObjectCommand, 2, { - Bucket: 'foo', - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedNthCommandWith(GetObjectCommand, 2, { + Bucket: 'foo', + Key: 'file2.txt', + }); + }).toThrow(/expected 2nd "GetObjectCommand" to be called with arguments/); }); describe('not', () => { @@ -1011,23 +1086,27 @@ describe('toHaveReceivedNthCommandWith', () => { }); }); - it.fails('fails when call matches', async () => { + it('fails when call matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).not.toHaveReceivedNthCommandWith(GetObjectCommand, 1, { - Bucket: 'foo', - Key: 'file1.txt', - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedNthCommandWith(GetObjectCommand, 1, { + Bucket: 'foo', + Key: 'file1.txt', + }); + }).toThrow(/expected 1st "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails when call partially matches', async () => { + it('fails when call partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).not.toHaveReceivedNthCommandWith(GetObjectCommand, 1, { - Bucket: 'foo', - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedNthCommandWith(GetObjectCommand, 1, { + Bucket: 'foo', + }); + }).toThrow(/expected 1st "GetObjectCommand" to not be called with arguments/); }); }); }); @@ -1078,34 +1157,40 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('fails when only command does not match', async () => { + it('fails when only command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file3.txt', - }); + expect(() => { + expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file3.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when last command does not match', async () => { + it('fails when last command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file3.txt' })); - expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file1.txt', - }); + expect(() => { + expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file1.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when not called at all', () => { + it('fails when not called at all', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file1.txt', - }); + expect(() => { + expect(s3Mock).toReceiveLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file1.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to be called with arguments/); }); describe('not', () => { @@ -1140,25 +1225,29 @@ describe('toReceiveLastCommandWith', () => { }); }); - it.fails('fails when last command matches', async () => { + it('fails when last command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); - expect(s3Mock).not.toReceiveLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).not.toReceiveLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file2.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails when last command partially matches', async () => { + it('fails when last command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); - expect(s3Mock).not.toReceiveLastCommandWith(GetObjectCommand, { - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).not.toReceiveLastCommandWith(GetObjectCommand, { + Key: 'file2.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to not be called with arguments/); }); }); }); @@ -1209,34 +1298,40 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('fails when only command does not match', async () => { + it('fails when only command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file3.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file3.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when last command does not match', async () => { + it('fails when last command does not match', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file3.txt' })); - expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file1.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file1.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to be called with arguments/); }); - it.fails('fails when not called at all', () => { + it('fails when not called at all', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file1.txt', - }); + expect(() => { + expect(s3Mock).toHaveReceivedLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file1.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to be called with arguments/); }); describe('not', () => { @@ -1271,33 +1366,39 @@ describe('toHaveReceivedLastCommandWith', () => { }); }); - it.fails('fails when last command matches', async () => { + it('fails when last command matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); - expect(s3Mock).not.toHaveReceivedLastCommandWith(GetObjectCommand, { - Bucket: 'foo', - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedLastCommandWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'file2.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to not be called with arguments/); }); - it.fails('fails when last command partially matches', async () => { + it('fails when last command partially matches', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); - expect(s3Mock).not.toHaveReceivedLastCommandWith(GetObjectCommand, { - Key: 'file2.txt', - }); + expect(() => { + expect(s3Mock).not.toHaveReceivedLastCommandWith(GetObjectCommand, { + Key: 'file2.txt', + }); + }).toThrow(/expected last "GetObjectCommand" to not be called with arguments/); }); }); }); describe('toReceiveAnyCommand', () => { - it.fails('fails when no command received', () => { + it('fails when no command received', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toReceiveAnyCommand(); + expect(() => { + expect(s3Mock).toReceiveAnyCommand(); + }).toThrow(/expected client "S3Client" to have been called, but was not called/); }); it('passes when one command was received', async () => { @@ -1313,28 +1414,34 @@ describe('toReceiveAnyCommand', () => { expect(s3Mock).not.toReceiveAnyCommand(); }); - it.fails('fails when one command was received', async () => { + it('fails when one command was received', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).not.toReceiveAnyCommand(); + expect(() => { + expect(s3Mock).not.toReceiveAnyCommand(); + }).toThrow(/expected client "S3Client" to not receive any calls, but was called/); }); - it.fails('fails when multiple commands were received', async () => { + it('fails when multiple commands were received', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); await s3.send(new GetObjectAclCommand({ Bucket: 'foo', Key: 'file3.txt' })); - expect(s3Mock).not.toReceiveAnyCommand(); + expect(() => { + expect(s3Mock).not.toReceiveAnyCommand(); + }).toThrow(/expected client "S3Client" to not receive any calls, but was called/); }); }); }); describe('toHaveReceivedAnyCommand', () => { - it.fails('fails when no command received', () => { + it('fails when no command received', () => { const s3Mock = mockClient(S3Client); - expect(s3Mock).toHaveReceivedAnyCommand(); + expect(() => { + expect(s3Mock).toHaveReceivedAnyCommand(); + }).toThrow(/expected client "S3Client" to have been called, but was not called/); }); it('passes when one command was received', async () => { @@ -1350,20 +1457,24 @@ describe('toHaveReceivedAnyCommand', () => { expect(s3Mock).not.toHaveReceivedAnyCommand(); }); - it.fails('fails when one command was received', async () => { + it('fails when one command was received', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); - expect(s3Mock).not.toHaveReceivedAnyCommand(); + expect(() => { + expect(s3Mock).not.toHaveReceivedAnyCommand(); + }).toThrow(/expected client "S3Client" to not receive any calls, but was called/); }); - it.fails('fails when multiple commands were received', async () => { + it('fails when multiple commands were received', async () => { const s3Mock = mockClient(S3Client); const s3 = new S3Client({}); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file1.txt' })); await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'file2.txt' })); await s3.send(new GetObjectAclCommand({ Bucket: 'foo', Key: 'file3.txt' })); - expect(s3Mock).not.toHaveReceivedAnyCommand(); + expect(() => { + expect(s3Mock).not.toHaveReceivedAnyCommand(); + }).toThrow(/expected client "S3Client" to not receive any calls, but was called/); }); }); }); From 38bd57f94c6454363232a43e876de31d4fae139b Mon Sep 17 00:00:00 2001 From: Stefan Schulte Date: Fri, 17 Jan 2025 16:41:07 +0100 Subject: [PATCH 5/5] Add new matcher toHaveReceivedCommandExactlyOnceWith The new matcher can be used in similar way as [toHaveBeenCalledExactlyOnceWith][1] of vitest but targeted against AWS mocks [1]: https://github.com/vitest-dev/vitest/pull/6894 --- CHANGELOG.md | 5 + README.md | 6 +- src/index.ts | 2 + src/matcher.ts | 40 ++++- tests/index.test.ts | 6 + tests/matcher.test.ts | 402 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 458 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e216943..e54ea46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- matcher `toHaveReceivedCommandExactlyOnceWith` can be used to verify there are + no additional calls + ### Changed - Update dependencies. This bumps `@vitest/expect` dependency to `^3.0.1` diff --git a/README.md b/README.md index 3a28c39..600adff 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ You must register the new matchers explicity (think about putting this to a [set }, }); - to add the custom mat chers before each test run + to add the custom matchers before each test run */ import { expect } from "vitest"; import { @@ -57,6 +57,8 @@ import { toHaveReceivedLastCommandWith, toReceiveAnyCommand, toHaveReceivedAnyCommand, + toReceiveCommandExactlyOnceWith, + toHaveReceivedCommandExactlyOnceWith, } from "aws-sdk-client-mock-vitest"; expect.extend({ @@ -74,6 +76,8 @@ expect.extend({ toHaveReceivedLastCommandWith, toReceiveAnyCommand, toHaveReceivedAnyCommand, + toReceiveCommandExactlyOnceWith, + toHaveReceivedCommandExactlyOnceWith, }); ``` diff --git a/src/index.ts b/src/index.ts index 27eca9a..571f90d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ export type { CustomMatcher } from './matcher.js'; export { toHaveReceivedAnyCommand, toHaveReceivedCommand, + toHaveReceivedCommandExactlyOnceWith, toHaveReceivedCommandOnce, toHaveReceivedCommandTimes, toHaveReceivedCommandWith, @@ -9,6 +10,7 @@ export { toHaveReceivedNthCommandWith, toReceiveAnyCommand, toReceiveCommand, + toReceiveCommandExactlyOnceWith, toReceiveCommandOnce, toReceiveCommandTimes, toReceiveCommandWith, diff --git a/src/matcher.ts b/src/matcher.ts index 404cf03..e92dfc7 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -15,6 +15,7 @@ import { notNull, ordinalOf } from './utils.js'; interface AliasMatcher { toReceiveAnyCommand: BaseMatcher['toHaveReceivedAnyCommand']; toReceiveCommand: BaseMatcher['toHaveReceivedCommand']; + toReceiveCommandExactlyOnceWith: BaseMatcher['toHaveReceivedCommandExactlyOnceWith']; toReceiveCommandOnce: BaseMatcher['toHaveReceivedCommandOnce']; toReceiveCommandTimes: BaseMatcher['toHaveReceivedCommandTimes']; toReceiveCommandWith: BaseMatcher['toHaveReceivedCommandWith']; @@ -39,6 +40,14 @@ interface BaseMatcher { command: AwsCommandConstructur ): R; + toHaveReceivedCommandExactlyOnceWith< + Input extends object, + Output extends MetadataBearer, + >( + command: AwsCommandConstructur, + input: Partial + ): R; + toHaveReceivedCommandOnce< Input extends object, Output extends MetadataBearer, @@ -209,9 +218,34 @@ function toHaveReceivedCommandWith( }; }; const toReceiveCommandWith = toHaveReceivedCommandWith; -/* - */ +function toHaveReceivedCommandExactlyOnceWith( + this: MatcherState, + client: AwsStub, + command: AwsCommandConstructur, + input: Record, +): ExpectationResult { + const { isNot, utils } = this; + const calls = client.commandCalls(command); + + const hasCallWithArgs = calls.some(call => + new ObjectContaining(input).asymmetricMatch(call.args[0].input), + ); + + const pass = calls.length === 1 && hasCallWithArgs; + + return { + message: () => { + const message = isNot + ? `expected "${command.name}" to not be called once with arguments: ${utils.printExpected(input)}` + : `expected "${command.name}" to be called once with arguments: ${utils.printExpected(input)}`; + return formatCalls(this, client, command, input, message); + }, + pass, + }; +}; +const toReceiveCommandExactlyOnceWith = toHaveReceivedCommandExactlyOnceWith; + function toHaveReceivedNthCommandWith( this: MatcherState, client: AwsStub, @@ -289,6 +323,7 @@ export type { CustomMatcher }; export { toHaveReceivedAnyCommand, toHaveReceivedCommand, + toHaveReceivedCommandExactlyOnceWith, toHaveReceivedCommandOnce, toHaveReceivedCommandTimes, toHaveReceivedCommandWith, @@ -296,6 +331,7 @@ export { toHaveReceivedNthCommandWith, toReceiveAnyCommand, toReceiveCommand, + toReceiveCommandExactlyOnceWith, toReceiveCommandOnce, toReceiveCommandTimes, toReceiveCommandWith, diff --git a/tests/index.test.ts b/tests/index.test.ts index fd94e3a..55524cc 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -5,6 +5,7 @@ import { describe, expect, it } from 'vitest'; import { toHaveReceivedAnyCommand, toHaveReceivedCommand, + toHaveReceivedCommandExactlyOnceWith, toHaveReceivedCommandOnce, toHaveReceivedCommandTimes, toHaveReceivedCommandWith, @@ -12,6 +13,7 @@ import { toHaveReceivedNthCommandWith, toReceiveAnyCommand, toReceiveCommand, + toReceiveCommandExactlyOnceWith, toReceiveCommandOnce, toReceiveCommandTimes, toReceiveCommandWith, @@ -22,6 +24,7 @@ import { expect.extend({ toHaveReceivedAnyCommand, toHaveReceivedCommand, + toHaveReceivedCommandExactlyOnceWith, toHaveReceivedCommandOnce, toHaveReceivedCommandTimes, toHaveReceivedCommandWith, @@ -29,6 +32,7 @@ expect.extend({ toHaveReceivedNthCommandWith, toReceiveAnyCommand, toReceiveCommand, + toReceiveCommandExactlyOnceWith, toReceiveCommandOnce, toReceiveCommandTimes, toReceiveCommandWith, @@ -52,6 +56,8 @@ describe('aws-sdk-client-mock-vitest', () => { 'toReceiveLastCommandWith', 'toReceiveNthCommandWith', 'toReceiveAnyCommand', + 'toReceiveCommandExactlyOnceWith', + 'toHaveReceivedCommandExactlyOnceWith', ])('extend matcher to extend with %s', (matcher) => { expect(expect('something')).toHaveProperty(matcher); }); diff --git a/tests/matcher.test.ts b/tests/matcher.test.ts index f25083b..0e328a3 100644 --- a/tests/matcher.test.ts +++ b/tests/matcher.test.ts @@ -12,6 +12,7 @@ import { describe, expect, it } from 'vitest'; import { toHaveReceivedAnyCommand, toHaveReceivedCommand, + toHaveReceivedCommandExactlyOnceWith, toHaveReceivedCommandOnce, toHaveReceivedCommandTimes, toHaveReceivedCommandWith, @@ -19,6 +20,7 @@ import { toHaveReceivedNthCommandWith, toReceiveAnyCommand, toReceiveCommand, + toReceiveCommandExactlyOnceWith, toReceiveCommandOnce, toReceiveCommandTimes, toReceiveCommandWith, @@ -29,6 +31,7 @@ import { expect.extend({ toHaveReceivedAnyCommand, toHaveReceivedCommand, + toHaveReceivedCommandExactlyOnceWith, toHaveReceivedCommandOnce, toHaveReceivedCommandTimes, toHaveReceivedCommandWith, @@ -36,6 +39,7 @@ expect.extend({ toHaveReceivedNthCommandWith, toReceiveAnyCommand, toReceiveCommand, + toReceiveCommandExactlyOnceWith, toReceiveCommandOnce, toReceiveCommandTimes, toReceiveCommandWith, @@ -1478,3 +1482,401 @@ describe('toHaveReceivedAnyCommand', () => { }); }); }); + +describe('toHaveReceivedCommandExactlyOnceWith', () => { + it('passes if called exactly once with command', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }); + + it('passes if called exactly once with partial command', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + }); + }); + + it('passes with a correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }); + + it('fails when not called', () => { + const s3Mock = mockClient(S3Client); + + expect(() => { + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bucket2', + Key: 'key2', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails when received with wrong command', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(PutObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }).toThrow(/expected "PutObjectCommand" to be called once with arguments/); + }); + + it('fails when input does not match', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'wrongkey.txt', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails when input misses fields', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + VersionId: '10', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails on failed asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(() => { + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails when received to often', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test2.txt' })); + expect(() => { + expect(s3Mock).toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + describe('not', () => { + it('passes when never called', () => { + const s3Mock = mockClient(S3Client); + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(PutObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }); + + it('passes when not called with input', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bar', + Key: 'test.txt', + }); + }); + + it('passes when called multiple times', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); + await s3.send(new GetObjectCommand({ Bucket: 'baz', Key: 'test.txt' })); + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bar', + Key: 'test.txt', + }); + }); + + it('fails on partial match', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bar', + }); + }).toThrow(/expected "GetObjectCommand" to not be called once with arguments/); + }); + + it('fails on correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(() => { + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to not be called once with arguments/); + }); + + it('passes when called with additional arguments', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + VersionId: 'abc', + }); + }); + + it('passes on incorrect asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).not.toHaveReceivedCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }); + }); +}); + +describe('toReceiveCommandExactlyOnceWith', () => { + it('passes if called exactly once with command', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }); + + it('passes if called exactly once with partial command', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + }); + }); + + it('passes with a correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }); + + it('fails when not called', () => { + const s3Mock = mockClient(S3Client); + + expect(() => { + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bucket2', + Key: 'key2', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails when received with wrong command', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).toReceiveCommandExactlyOnceWith(PutObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }).toThrow(/expected "PutObjectCommand" to be called once with arguments/); + }); + + it('fails when input does not match', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'wrongkey.txt', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails when input misses fields', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + VersionId: '10', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails on failed asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(() => { + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + it('fails when received to often', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test2.txt' })); + expect(() => { + expect(s3Mock).toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }).toThrow(/expected "GetObjectCommand" to be called once with arguments/); + }); + + describe('not', () => { + it('passes when never called', () => { + const s3Mock = mockClient(S3Client); + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(PutObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + }); + }); + + it('passes when not called with input', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bar', + Key: 'test.txt', + }); + }); + + it('passes when called multiple times', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); + await s3.send(new GetObjectCommand({ Bucket: 'baz', Key: 'test.txt' })); + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bar', + Key: 'test.txt', + }); + }); + + it('fails on partial match', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'bar', Key: 'test.txt' })); + expect(() => { + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'bar', + }); + }).toThrow(/expected "GetObjectCommand" to not be called once with arguments/); + }); + + it('fails on correct asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(() => { + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.txt$/) as string, + }); + }).toThrow(/expected "GetObjectCommand" to not be called once with arguments/); + }); + + it('passes when called with additional arguments', async () => { + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: 'test.txt' })); + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: 'test.txt', + VersionId: 'abc', + }); + }); + + it('passes on incorrect asymmetric match', async () => { + // Assume code that uses a random string for the bucket key with a known extension + const name = randomUUID().toString(); + const s3Mock = mockClient(S3Client); + const s3 = new S3Client({}); + await s3.send(new GetObjectCommand({ Bucket: 'foo', Key: `${name}.txt` })); + + // asymmetric matchers like stringMatching are typed as `any` so we cast them + // as we want to compare them to strings + expect(s3Mock).not.toReceiveCommandExactlyOnceWith(GetObjectCommand, { + Bucket: 'foo', + Key: expect.stringMatching(/.jpg/) as string, + }); + }); + }); +});