From 3c92adf9ffae806ec71db4815d89607a280f9a38 Mon Sep 17 00:00:00 2001 From: MeAkib Date: Sat, 31 Oct 2026 01:14:58 +0600 Subject: [PATCH] fix(@schematics/angular): keep the meaning of negated toHaveBeenCalledOnceWith in refactor-jasmine-vitest `expect(spy).not.toHaveBeenCalledOnceWith(...)` was split into `not.toHaveBeenCalledTimes(1)` and `not.toHaveBeenCalledWith(...)`, which asserts that neither condition holds instead of not both. For example, a spy called twice with the same arguments passed before the migration and failed after it. Transform the negated form to `not.toHaveBeenCalledExactlyOnceWith(...)`, which has the same meaning. --- .../transformers/jasmine-matcher.ts | 25 ++++++++++++++++--- .../transformers/jasmine-matcher_spec.ts | 5 ++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts index 1e0a87dc80f2..c8ba2baadefa 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts @@ -474,15 +474,34 @@ export function transformCalledOnceWith( return node; } + const expectCall = call.expression.expression; + const args = call.arguments; + + // Splitting a negated assertion would assert that neither condition holds instead of not both. + if (ts.isPropertyAccessExpression(expectCall) && expectCall.name.text === 'not') { + reporter.reportTransformation( + sourceFile, + node, + 'Transformed `.not.toHaveBeenCalledOnceWith()` to `.not.toHaveBeenCalledExactlyOnceWith()`.', + ); + + return ts.factory.updateExpressionStatement( + node, + ts.factory.updateCallExpression( + call, + createPropertyAccess(expectCall, 'toHaveBeenCalledExactlyOnceWith'), + call.typeArguments, + args, + ), + ); + } + reporter.reportTransformation( sourceFile, node, 'Transformed `.toHaveBeenCalledOnceWith()` to `.toHaveBeenCalledTimes(1)` and `.toHaveBeenCalledWith()`.', ); - const expectCall = call.expression.expression; - const args = call.arguments; - const timesCall = ts.factory.createCallExpression( createPropertyAccess(expectCall, 'toHaveBeenCalledTimes'), undefined, diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts index 274202ed1689..f123ac3b84b3 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts @@ -113,6 +113,11 @@ describe('transformCalledOnceWith', () => { expect(mySpy).toHaveBeenCalledWith('foo', 'bar'); `, }, + { + description: 'should transform a negated toHaveBeenCalledOnceWith(...) into a single call', + input: `expect(mySpy).not.toHaveBeenCalledOnceWith('foo');`, + expected: `expect(mySpy).not.toHaveBeenCalledExactlyOnceWith('foo');`, + }, ]; testCases.forEach(({ description, input, expected }) => {