Skip to content

Commit

Permalink
fix: status
Browse files Browse the repository at this point in the history
  • Loading branch information
vCaisim committed Jul 15, 2024
1 parent 8fea1d2 commit efda9f6
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 115 deletions.
33 changes: 15 additions & 18 deletions packages/cmd/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
export enum TestState {
Failed = "failed",
Passed = "passed",
Pending = "pending",
Skipped = "skipped",
}

export enum TestExpectedStatus {
Passed = "passed",
Failed = "failed",
TimedOut = "timedOut",
Skipped = "skipped",
Interrupted = "interrupted",
}
// possible currents test case statuses from reported results
export type TestCaseStatus = "passed" | "failed" | "pending";

// currents values suitable for jest status
export type TestRunnerStatus = "passed" | "failed" | "skipped";

// currents values suitable for jest expected status
export type ExpectedStatus = "passed" | "skipped";

// jest test case statuses available in results
export type JestTestCaseStatus = "pending" | "todo" | "failed" | "passed";

export type InstanceReportStats = {
suites: number;
Expand Down Expand Up @@ -46,7 +43,7 @@ export type WorkerInfo = {
};

export type InstanceReportTestAttempt = {
_s: TestState;
_s: TestCaseStatus;
attempt: number;
workerIndex: number;
parallelIndex: number;
Expand All @@ -55,7 +52,7 @@ export type InstanceReportTestAttempt = {
steps: unknown[];

duration: number;
status: TestExpectedStatus;
status: TestRunnerStatus;

stderr?: string[];
stdout?: string[];
Expand All @@ -68,10 +65,10 @@ export type InstanceReportTest = {
_t: number;
testId: string;
title: string[];
state: TestState;
state: TestCaseStatus;
isFlaky?: boolean;
retries: number;
expectedStatus?: TestExpectedStatus;
expectedStatus?: ExpectedStatus;
annotations?: unknown[];
timeout: number;
location: LocationSchema;
Expand Down
112 changes: 47 additions & 65 deletions packages/jest/src/lib/test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Test, TestCaseResult } from "@jest/reporters";
import type { Circus } from "@jest/types";
import { flowRight } from "lodash";
import crypto from "node:crypto";
import { P, match } from "ts-pattern";
import { TestExpectedStatus, TestState } from "../types";
import {
ExpectedStatus,
JestTestCaseStatus,
TestCaseStatus,
TestRunnerStatus,
} from "../types";
import { getRelativeFileLocation } from "./relativeFileLocation";

export type TestCaseInvocationStart = {
Expand Down Expand Up @@ -71,54 +74,60 @@ export function getWorker() {
};
}

export function statusToCurrentsStatus(
testStatus: TestCaseResult["status"]
): TestState {
export function getTestCaseStatus(
testStatus: JestTestCaseStatus
): TestCaseStatus {
switch (testStatus) {
case "passed":
return TestState.Passed;
return "passed";
case "failed":
return TestState.Failed;
case "skipped":
return "failed";
case "pending":
case "todo":
return "pending";

default:
throw new Error("Invalid Jest test case status");
}
}

export function getTestRunnerStatus(
status: JestTestCaseStatus
): TestRunnerStatus {
switch (status) {
case "passed":
return "passed";
case "failed":
return "failed";
case "pending":
case "disabled":
return TestState.Pending;
case "todo":
return "skipped";

// case "focused":
default:
throw new Error("Invalid Jest test case status");
}
}

export function getExpectedStatus(status: JestTestCaseStatus): ExpectedStatus {
switch (status) {
case "pending":
case "todo":
return "skipped";

default:
return TestState.Failed;
return "passed";
}
}

export function getRawTestStatus(
testCaseResults: TestCaseResult[]
): TestExpectedStatus {
const allStatuses = testCaseResults.map((i) => i.status);

// if all the attempts have similar status
if (allStatuses.every((status) => status === allStatuses[0])) {
return match(allStatuses[0])
.with(
P.union("disabled", "skipped", "todo", "pending"),
() => TestExpectedStatus.Skipped
)
.with("passed", () => TestExpectedStatus.Passed)
.with("failed", () => TestExpectedStatus.Failed)
.otherwise(() => TestExpectedStatus.Failed);
export function jestStatusFromInvocations(testResults: TestCaseResult[]) {
const statuses = testResults.map((r) => r.status as JestTestCaseStatus);
if (statuses.every((status) => status === statuses[0])) {
return statuses[0];
}

// otherwise, it is a mix of passed and failed attempts, so it is flaky
// and it doesn't pass the expected status
return TestExpectedStatus.Failed;
return "failed";
}

export const getTestCaseStatus = flowRight(
statusToCurrentsStatus,
getRawTestStatus
);

export function getAttemptNumber(result: TestCaseResult) {
return result?.invocations ?? 1;
}
Expand All @@ -127,34 +136,7 @@ export function getAttemptNumber(result: TestCaseResult) {
export function isTestFlaky(testResults: TestCaseResult[]): boolean {
return (
testResults.length > 1 &&
testResults.some((r) => r.status === TestState.Failed) &&
testResults[testResults.length - 1].status === TestState.Passed
testResults.some((r) => r.status === "failed") &&
testResults[testResults.length - 1].status === "passed"
);
}

export function getTestModeFromResult(
result: TestCaseResult
): Circus.BlockMode {
switch (result.status) {
case "skipped":
case "pending":
case "disabled":
return "skip";
case "todo":
return "todo";
default:
return void 0;
}
}

export function testModeToExpectedStatus(
mode: Circus.BlockMode
): TestExpectedStatus {
switch (mode) {
case "skip":
case "todo":
return TestExpectedStatus.Skipped;
default:
return TestExpectedStatus.Passed;
}
}
28 changes: 14 additions & 14 deletions packages/jest/src/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,27 @@ import {
formatError,
generateShortHash,
getAttemptNumber,
getTestCaseStatus,
getError,
getProjectId,
getRawTestStatus,
getTestCaseFullTitle,
getTestCaseId,
getTestCaseStatus,
getTestModeFromResult,
getWorker,
isTestFlaky,
testModeToExpectedStatus,
jestStatusFromInvocations,
testToSpecName,
writeFileAsync,
getExpectedStatus,
getTestRunnerStatus,
} from "./lib";
import { getReportConfig } from "./lib/getReportConfig";
import { info } from "./logger";
import { InstanceReport, TestExpectedStatus, WorkerInfo } from "./types";
import { InstanceReport, JestTestCaseStatus, WorkerInfo } from "./types";

type TestCase = {
id: string;
timestamps: number[];
title: string[];
mode: Circus.BlockMode;
result: TestCaseResult[];
worker: WorkerInfo;
config: Test["context"]["config"];
Expand Down Expand Up @@ -130,6 +129,10 @@ export default class CustomReporter implements Reporter {
debug("Spec execution started [%s]: %o", specName, this.specInfo[specKey]);
}

/**
* Called before running a spec (prior to `before` hooks)
* Not called for `skipped` and `todo` specs
*/
async onTestCaseStart(
test: Test,
testCaseStartInfo: Circus.TestCaseStartInfo
Expand Down Expand Up @@ -160,7 +163,6 @@ export default class CustomReporter implements Reporter {
id: testId,
timestamps: [testCaseStartInfo.startedAt ?? new Date().getTime()],
title: getTestCaseFullTitle(testCaseStartInfo),
mode: testCaseStartInfo.mode,
result: [],
worker: getWorker(),
config: test.context.config,
Expand Down Expand Up @@ -207,7 +209,6 @@ export default class CustomReporter implements Reporter {
id: testId,
timestamps: [],
title: getTestCaseFullTitle(testCaseResult),
mode: getTestModeFromResult(testCaseResult),
result: [],
worker: getWorker(),
config: test.context.config,
Expand Down Expand Up @@ -252,7 +253,6 @@ export default class CustomReporter implements Reporter {
id: testId,
timestamps: [],
title: getTestCaseFullTitle(testResult),
mode: getTestModeFromResult(testResult),
result: [testResult],
worker: getWorker(),
config: test.context.config,
Expand All @@ -276,15 +276,15 @@ export default class CustomReporter implements Reporter {

const tests = Object.values(this.specInfo[specKey].testCaseList).map(
(testCase) => {
const status = getTestCaseStatus(testCase.result);
const jestStatus = jestStatusFromInvocations(testCase.result);

return {
_t: testCase.timestamps[0] ?? testResult.perfStats.start,
testId: testCase.id,
title: testCase.title,
state: status,
state: getTestCaseStatus(jestStatus),
isFlaky: isTestFlaky(testCase.result),
expectedStatus: testModeToExpectedStatus(testCase.mode),
expectedStatus: getExpectedStatus(jestStatus),
timeout: 0,
location: {
column: 1,
Expand All @@ -306,7 +306,7 @@ export default class CustomReporter implements Reporter {
);

return {
_s: getTestCaseStatus([result]),
_s: getTestCaseStatus(result.status as JestTestCaseStatus),
attempt: getAttemptNumber(result),

workerIndex: testCase.worker.workerIndex,
Expand All @@ -319,7 +319,7 @@ export default class CustomReporter implements Reporter {
steps: [],

duration: testCase.result[index].duration ?? 0,
status: getRawTestStatus([result]),
status: getTestRunnerStatus(result.status as JestTestCaseStatus),

stdout: [],
stderr: result.failureMessages ?? [],
Expand Down
33 changes: 15 additions & 18 deletions packages/jest/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
export enum TestState {
Failed = "failed",
Passed = "passed",
Pending = "pending",
Skipped = "skipped",
}

export enum TestExpectedStatus {
Passed = "passed",
Failed = "failed",
TimedOut = "timedOut",
Skipped = "skipped",
Interrupted = "interrupted",
}
// possible currents test case statuses from reported results
export type TestCaseStatus = "passed" | "failed" | "pending";

// currents values suitable for jest status
export type TestRunnerStatus = "passed" | "failed" | "skipped";

// currents values suitable for jest expected status
export type ExpectedStatus = "passed" | "skipped";

// jest test case statuses available in results
export type JestTestCaseStatus = "pending" | "todo" | "failed" | "passed";

export type InstanceReportStats = {
suites: number;
Expand Down Expand Up @@ -46,7 +43,7 @@ export type WorkerInfo = {
};

export type InstanceReportTestAttempt = {
_s: TestState;
_s: TestCaseStatus;
attempt: number;
workerIndex: number;
parallelIndex: number;
Expand All @@ -55,7 +52,7 @@ export type InstanceReportTestAttempt = {
steps: unknown[];

duration: number;
status: TestExpectedStatus;
status: TestRunnerStatus;

stderr?: string[];
stdout?: string[];
Expand All @@ -68,10 +65,10 @@ export type InstanceReportTest = {
_t: number;
testId: string;
title: string[];
state: TestState;
state: TestCaseStatus;
isFlaky?: boolean;
retries: number;
expectedStatus?: TestExpectedStatus;
expectedStatus?: ExpectedStatus;
annotations?: unknown[];
timeout: number;
location: LocationSchema;
Expand Down

0 comments on commit efda9f6

Please sign in to comment.