Skip to content

Commit d2aa9ed

Browse files
yjaaidiclydin
authored andcommitted
feat(@schematics/angular): migrate fakeAsync's flush behavior when used in beforeEach
1 parent 23af499 commit d2aa9ed

2 files changed

Lines changed: 175 additions & 16 deletions

File tree

packages/schematics/angular/refactor/jasmine-vitest/transformers/fake-async-test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,30 @@ function _transformFakeAsyncCall(
107107
`Transformed \`fakeAsync\` to \`vi.useFakeTimers\`.`,
108108
);
109109

110+
let statements = callbackBody.statements;
111+
112+
// Append `vi.runOnlyPendingTimersAsync()` as the last statement of `beforeEach` to mimic flush behavior.
113+
if (
114+
ts.isCallExpression(node.parent) &&
115+
ts.isIdentifier(node.parent.expression) &&
116+
node.parent.expression.text === 'beforeEach' &&
117+
!_isFakeAsyncFlushDisabled(node)
118+
) {
119+
statements = ts.factory.createNodeArray([
120+
...statements,
121+
ts.factory.createExpressionStatement(
122+
ts.factory.createAwaitExpression(createViCallExpression(ctx, 'runOnlyPendingTimersAsync')),
123+
),
124+
]);
125+
}
126+
110127
return ts.factory.createArrowFunction(
111128
[ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)],
112129
fakeAsyncCallback.typeParameters,
113130
fakeAsyncCallback.parameters,
114131
undefined,
115132
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
116-
ts.factory.createBlock(callbackBody.statements),
133+
ts.factory.createBlock(statements),
117134
);
118135
}
119136

@@ -177,6 +194,25 @@ function _createFakeTimersHookStatements(ctx: RefactorContext): ts.Statement[] {
177194
];
178195
}
179196

197+
/**
198+
* Detects if the `flush` option is set to false in the `fakeAsync` call expression.
199+
* e.g. `fakeAsync(() => { ... }, { flush: false })`
200+
*/
201+
function _isFakeAsyncFlushDisabled(fakeAsyncCallExpression: ts.CallExpression): boolean {
202+
const options = fakeAsyncCallExpression.arguments[1];
203+
204+
return (
205+
options &&
206+
ts.isObjectLiteralExpression(options) &&
207+
options.properties.some(
208+
(property) =>
209+
ts.isPropertyAssignment(property) &&
210+
property.name.getText() === 'flush' &&
211+
property.initializer.getText() === 'false',
212+
)
213+
);
214+
}
215+
180216
interface CurrentOutermostDescribeContext {
181217
isUsingFakeAsync: boolean;
182218
}

packages/schematics/angular/refactor/jasmine-vitest/transformers/fake-async-test_spec.ts

Lines changed: 138 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,28 +154,79 @@ describe('transformFakeAsyncTest', () => {
154154
},
155155
{
156156
description:
157-
'should transform fakeAsync test to `vi.useFakeTimers()` in `beforeEach`, `afterEach`, `beforeAll`, `afterAll`',
157+
'should transform fakeAsync test to `vi.useFakeTimers()` in `beforeEach` and preserve flush behavior',
158158
input: `
159159
import { fakeAsync } from '@angular/core/testing';
160160
161161
describe('My fakeAsync suite', () => {
162-
beforeAll(fakeAsync(() => {
163-
console.log('beforeAll');
162+
163+
let count = 0;
164+
beforeEach(fakeAsync(() => {
165+
setTimeout(() => ++count, 100);
164166
}));
165167
166-
afterAll(fakeAsync(() => {
167-
console.log('afterAll');
168+
it('works', fakeAsync(() => {
169+
expect(count).toBe(1);
168170
}));
171+
});
172+
`,
173+
expected: `
174+
describe('My fakeAsync suite', () => {
175+
beforeEach(() => {
176+
vi.useFakeTimers({ advanceTimeDelta: 1, shouldAdvanceTime: true });
177+
});
178+
afterEach(() => {
179+
vi.useRealTimers();
180+
});
169181
170-
beforeEach(fakeAsync(() => {
171-
console.log('beforeEach');
172-
}));
182+
let count = 0;
183+
beforeEach(async () => {
184+
setTimeout(() => ++count, 100);
185+
await vi.runOnlyPendingTimersAsync();
186+
});
187+
188+
it('works', async () => {
189+
expect(count).toBe(1);
190+
});
191+
});
192+
`,
193+
},
194+
{
195+
description: 'should transform fakeAsync test to `vi.useFakeTimers()` in `afterEach`',
196+
input: `
197+
import { fakeAsync } from '@angular/core/testing';
173198
199+
describe('My fakeAsync suite', () => {
174200
afterEach(fakeAsync(() => {
175201
console.log('afterEach');
176202
}));
177203
});
178204
`,
205+
expected: `
206+
describe('My fakeAsync suite', () => {
207+
beforeEach(() => {
208+
vi.useFakeTimers({ advanceTimeDelta: 1, shouldAdvanceTime: true });
209+
});
210+
afterEach(() => {
211+
vi.useRealTimers();
212+
});
213+
afterEach(async () => {
214+
console.log('afterEach');
215+
});
216+
});
217+
`,
218+
},
219+
{
220+
description: 'should transform fakeAsync test to `vi.useFakeTimers()` in `beforeAll`',
221+
input: `
222+
import { fakeAsync } from '@angular/core/testing';
223+
224+
describe('My fakeAsync suite', () => {
225+
beforeAll(fakeAsync(() => {
226+
console.log('beforeAll');
227+
}));
228+
});
229+
`,
179230
expected: `
180231
describe('My fakeAsync suite', () => {
181232
beforeEach(() => {
@@ -187,17 +238,30 @@ describe('transformFakeAsyncTest', () => {
187238
beforeAll(async () => {
188239
console.log('beforeAll');
189240
});
241+
});
242+
`,
243+
},
244+
{
245+
description: 'should transform fakeAsync test to `vi.useFakeTimers()` in `afterAll`',
246+
input: `
247+
import { fakeAsync } from '@angular/core/testing';
190248
191-
afterAll(async () => {
249+
describe('My fakeAsync suite', () => {
250+
afterAll(fakeAsync(() => {
192251
console.log('afterAll');
252+
}));
253+
});
254+
`,
255+
expected: `
256+
describe('My fakeAsync suite', () => {
257+
beforeEach(() => {
258+
vi.useFakeTimers({ advanceTimeDelta: 1, shouldAdvanceTime: true });
193259
});
194-
195-
beforeEach(async () => {
196-
console.log('beforeEach');
260+
afterEach(() => {
261+
vi.useRealTimers();
197262
});
198-
199-
afterEach(async () => {
200-
console.log('afterEach');
263+
afterAll(async () => {
264+
console.log('afterAll');
201265
});
202266
});
203267
`,
@@ -240,6 +304,65 @@ describe('transformFakeAsyncTest', () => {
240304
});
241305
`,
242306
},
307+
{
308+
description: 'should not append `vi.runOnlyPendingTimersAsync()` in `test` or `afterEach`',
309+
input: `
310+
import { fakeAsync } from '@angular/core/testing';
311+
312+
describe('My fakeAsync suite', () => {
313+
afterEach(fakeAsync(() => {
314+
console.log('afterEach');
315+
}));
316+
317+
it('works', fakeAsync(() => {
318+
expect(1).toBe(1);
319+
}));
320+
});
321+
`,
322+
expected: `
323+
describe('My fakeAsync suite', () => {
324+
beforeEach(() => {
325+
vi.useFakeTimers({ advanceTimeDelta: 1, shouldAdvanceTime: true });
326+
});
327+
afterEach(() => {
328+
vi.useRealTimers();
329+
});
330+
afterEach(async () => {
331+
console.log('afterEach');
332+
});
333+
334+
it('works', async () => {
335+
expect(1).toBe(1);
336+
});
337+
});
338+
`,
339+
},
340+
{
341+
description:
342+
'should not append `vi.runOnlyPendingTimersAsync()` if `flush` option is set to false',
343+
input: `
344+
import { fakeAsync } from '@angular/core/testing';
345+
346+
describe('My fakeAsync suite', () => {
347+
beforeEach(fakeAsync(() => {
348+
console.log('beforeEach');
349+
}, {flush: false}));
350+
});
351+
`,
352+
expected: `
353+
describe('My fakeAsync suite', () => {
354+
beforeEach(() => {
355+
vi.useFakeTimers({ advanceTimeDelta: 1, shouldAdvanceTime: true });
356+
});
357+
afterEach(() => {
358+
vi.useRealTimers();
359+
});
360+
beforeEach(async () => {
361+
console.log('beforeEach');
362+
});
363+
});
364+
`,
365+
},
243366
];
244367

245368
testCases.forEach(({ description, input, expected }) => {

0 commit comments

Comments
 (0)