Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/jest/src/__tests__/execute-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,46 @@ describe('executeRun', () => {
]);
});

it('includes the last active test when the device stops responding during a test', async () => {
let testRunnerListener:
| ((event: TestRunnerTestStartedEvent | TestRunnerTestFinishedEvent) => void)
| undefined;
const { emitEvent, calls } = makeEmitEvent();
const session = makeSession({
onTestRunnerEvent: vi.fn((listener) => {
testRunnerListener = listener as typeof testRunnerListener;
return () => undefined;
}),
});

mockRunHarnessTestFile.mockImplementation(async () => {
testRunnerListener?.({
type: 'test-started',
file: 'example.ts',
suite: 'VisionCamera - Controller',
name: 'runs a zoom animation',
ancestorTitles: ['VisionCamera - Controller'],
fullName: 'VisionCamera - Controller runs a zoom animation',
startedAt: 10,
});

throw new DeviceNotRespondingError('runTests', []);
});

await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());

expect(calls).toContainEqual([
'test-file-failure',
expect.anything(),
expect.objectContaining({
message: expect.stringContaining(
'Last active test: VisionCamera - Controller runs a zoom animation',
),
stack: '',
}),
]);
});

it('passes AppBridgeDisconnectedError to onFailure with an empty stack', async () => {
const { emitEvent, calls } = makeEmitEvent();
const session = makeSession({
Expand Down
41 changes: 37 additions & 4 deletions packages/jest/src/execute-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,32 @@ class CancelRun extends Error {
}
}

const buildTestFailure = (err: unknown): { message: string; stack: string } => {
const isSameTestRun = (
started: TestRunnerTestStartedEvent,
finished: TestRunnerTestFinishedEvent,
): boolean =>
started.file === finished.file &&
started.fullName === finished.fullName &&
started.startedAt === finished.startedAt;

const buildTestFailure = (
err: unknown,
activeTest?: TestRunnerTestStartedEvent | null,
): { message: string; stack: string } => {
if (
err instanceof NativeCrashError ||
err instanceof RuntimeDisconnectError ||
err instanceof StartupStallError ||
err instanceof AppBridgeDisconnectedError ||
err instanceof DeviceNotRespondingError
) {
return { message: (err as Error).message, stack: '' };
let message = (err as Error).message;

if (err instanceof DeviceNotRespondingError && activeTest) {
message += `\nLast active test: ${activeTest.fullName}`;
}

return { message, stack: '' };
}
return err as { message: string; stack: string };
};
Expand Down Expand Up @@ -128,8 +145,15 @@ export const executeRun = async (
const testFiles = tests.map((t) => path.relative(rootDir, t.path));
const summary = createRunSummary();
let caseEventChain = Promise.resolve();
let activeTest: TestRunnerTestStartedEvent | null = null;
const unsubscribe = session.onTestRunnerEvent((event) => {
if (isHarnessCaseEvent(event)) {
if (event.type === 'test-started') {
activeTest = event;
} else if (activeTest && isSameTestRun(activeTest, event)) {
activeTest = null;
}

caseEventChain = caseEventChain.then(() =>
event.type === 'test-started'
? emitHarnessTestStarted(emitEvent, event)
Expand Down Expand Up @@ -183,6 +207,7 @@ export const executeRun = async (
const relativeTestPath = path.relative(rootDir, test.path);
const fileStartedAt = Date.now();
let emittedTestFileFinished = false;
activeTest = null;

const emitTestFileFinished = async (options: {
status: 'passed' | 'failed' | 'skipped' | 'todo';
Expand Down Expand Up @@ -224,7 +249,11 @@ export const executeRun = async (
});
}
updateRunState({ error: err });
await emitEvent('test-file-failure', test, buildTestFailure(err));
await emitEvent(
'test-file-failure',
test,
buildTestFailure(err, activeTest),
);
}
continue;
}
Expand Down Expand Up @@ -288,7 +317,11 @@ export const executeRun = async (

updateRunState({ error: isRuntimeFailure ? undefined : err });
await caseEventChain;
await emitEvent('test-file-failure', test, buildTestFailure(err));
await emitEvent(
'test-file-failure',
test,
buildTestFailure(err, activeTest),
);
}
}
} catch (err) {
Expand Down