From 67e6a0df78d3cff32d101d1bbe3f119c0766a9af Mon Sep 17 00:00:00 2001 From: Andrew Goldis Date: Thu, 14 Sep 2023 01:56:03 -0700 Subject: [PATCH] fix: pick test:after results explicitly --- examples/webapp/cypress/e2e/hang.spec.js | 12 ++ .../cypress-cloud/lib/results/captureHooks.ts | 46 +------ packages/cypress-cloud/lib/results/combine.ts | 19 ++- packages/cypress-cloud/lib/state/execution.ts | 112 +++++++++++++----- packages/cypress-cloud/support/index.ts | 47 ++++++-- 5 files changed, 149 insertions(+), 87 deletions(-) create mode 100644 examples/webapp/cypress/e2e/hang.spec.js diff --git a/examples/webapp/cypress/e2e/hang.spec.js b/examples/webapp/cypress/e2e/hang.spec.js new file mode 100644 index 0000000..141e3f4 --- /dev/null +++ b/examples/webapp/cypress/e2e/hang.spec.js @@ -0,0 +1,12 @@ +describe("Example spec", () => { + beforeEach(() => { + console.log("beforeEach"); + }); + afterEach(() => { + console.log("after"); + }); + it("should open a page", () => { + cy.visit("https://launchdarkly.com/"); + cy.get("a").contains("Get started").should("be.visible"); + }); +}); diff --git a/packages/cypress-cloud/lib/results/captureHooks.ts b/packages/cypress-cloud/lib/results/captureHooks.ts index ba998b2..e764f2f 100644 --- a/packages/cypress-cloud/lib/results/captureHooks.ts +++ b/packages/cypress-cloud/lib/results/captureHooks.ts @@ -1,4 +1,4 @@ -import { ExecutionState } from "../state"; +import { ExecutionState, TestAfterTaskPayload } from "../state"; export function handleScreenshotEvent( screenshot: Cypress.ScreenshotDetails, @@ -25,46 +25,6 @@ export function handleTestAfter( testAttempt: string, executionState: ExecutionState ) { - const test = JSON.parse(testAttempt); - const { - title, - body, - retries, - _currentRetry, - pending, - type, - invocationDetails, - id, - hooks, - order, - wallClockStartedAt, - timings, - _events, - _eventsCount, - duration, - err, - state, - fullTitle, - } = test; - const attempt = { - title, - fullTitle, - body, - retries, - _currentRetry, - pending, - type, - invocationDetails, - id, - hooks, - order, - wallClockStartedAt, - timings, - _events, - _eventsCount, - duration, - err, - state, - }; - executionState.setAttemptsData(attempt); + const test: TestAfterTaskPayload = JSON.parse(testAttempt); + executionState.setAttemptsData(test); } diff --git a/packages/cypress-cloud/lib/results/combine.ts b/packages/cypress-cloud/lib/results/combine.ts index 6007e79..8d64204 100644 --- a/packages/cypress-cloud/lib/results/combine.ts +++ b/packages/cypress-cloud/lib/results/combine.ts @@ -1,9 +1,14 @@ import { parseISO } from "date-fns"; import _ from "lodash"; import { SpecResult } from "../runner/spec.type"; -import { ExecutionState } from "../state"; +import { + AfterScreenshotPayload, + ExecutionState, + MochaError, + TestAfterTaskPayload, +} from "../state"; -function getAttemptError(err: any) { +function getAttemptError(err: MochaError | null) { if (!err) { return null; } @@ -15,7 +20,10 @@ function getAttemptError(err: any) { }; } -function parseScreenshotResults(results: any, allScreenshots?: any[]) { +function parseScreenshotResults( + results: ReturnType, + allScreenshots?: AfterScreenshotPayload[] +) { if (!allScreenshots?.length) { return results; } @@ -37,7 +45,10 @@ function getAttemptVideoTimestamp( ) { return Math.max(attemptStartedAtMs - specStartedAtMs, 0); } -function getSpecResults(specResults: SpecResult, attempts?: any[]) { +function getSpecResults( + specResults: SpecResult, + attempts?: TestAfterTaskPayload[] +) { if (!attempts) { return specResults; } diff --git a/packages/cypress-cloud/lib/state/execution.ts b/packages/cypress-cloud/lib/state/execution.ts index 8abdb1d..be95532 100644 --- a/packages/cypress-cloud/lib/state/execution.ts +++ b/packages/cypress-cloud/lib/state/execution.ts @@ -26,7 +26,60 @@ type InstanceExecutionState = { specFileData?: SpecResult; }; -type InvocationDetails = { +export type TestAfterTaskPayload = { + async: boolean; + body: string; + duration: number; + err: MochaError; + final: boolean; + hooks: HookData[]; + id: string; + invocationDetails: InvocationDetails; + order: number; + pending: boolean; + retries: number; + state: string; + sync: boolean; + timedOut: boolean; + timings: Timing; + type: string; + wallClockStartedAt: string; + title: string; + currentRetry: string; + fullTitle: string; +}; + +interface ParsedStackItem { + message: string; + whitespace: string; + function?: string; + fileUrl?: string; + originalFile?: string; + relativeFile?: string; + absoluteFile?: string; + line?: number; + column?: number; +} + +interface CodeFrame { + line: number; + column: number; + originalFile: string; + relativeFile: string; + absoluteFile: string; + frame: string; + language: string; +} + +export interface MochaError { + message: string; + name: string; + stack: string; + parsedStack: ParsedStackItem[]; + codeFrame: CodeFrame; +} + +interface InvocationDetails { function: string; fileUrl: string; originalFile: string; @@ -36,42 +89,41 @@ type InvocationDetails = { column: number; whitespace: string; stack: string; -}; +} -type AttepmtHook = { +interface HookData { title: string; hookName: string; hookId: string; pending: boolean; body: string; type: string; - file: string | null; + file: null | string; invocationDetails: InvocationDetails; currentRetry: number; retries: number; _slow: number; -}; +} -export type AttemptData = { - title: string; - body: string; - retries: number; - _currentRetry: number; - pending: boolean; - type: string; - invocationDetails: InvocationDetails; - id: string; - hooks: AttepmtHook[]; - order: number; - wallClockStartedAt: string; - timings: Record; - _eventsCount: number; - duration: number; - err: Record; - state: string; -}; +interface TimingDetail { + hookId: string; + fnDuration: number; + afterFnDuration: number; +} + +interface TestTiminDetail { + fnDuration: number; + afterFnDuration: number; +} + +interface Timing { + lifecycle: number; + "before each": TimingDetail[]; + test: TestTiminDetail; + "after each": TimingDetail[]; +} -export type ScreenshotData = { +export type AfterScreenshotPayload = { testAttemptIndex: number; size: number; takenAt: string; @@ -85,8 +137,8 @@ export type ScreenshotData = { }; export class ExecutionState { - private attemptsData: AttemptData[] = []; - private screenshotsData: ScreenshotData[] = []; + private attemptsData: TestAfterTaskPayload[] = []; + private screenshotsData: AfterScreenshotPayload[] = []; private currentTestID?: string; private state: Record = {}; @@ -217,19 +269,19 @@ export class ExecutionState { }); } - public setAttemptsData(attemptDetails: AttemptData) { + public setAttemptsData(attemptDetails: TestAfterTaskPayload) { this.attemptsData.push(attemptDetails); } - public getAttemptsData(): AttemptData[] | undefined { + public getAttemptsData(): TestAfterTaskPayload[] | undefined { return this.attemptsData; } - public setScreenshotsData(screenshotsData: ScreenshotData) { + public setScreenshotsData(screenshotsData: AfterScreenshotPayload) { this.screenshotsData.push(screenshotsData); } - public getScreenshotsData(): ScreenshotData[] | undefined { + public getScreenshotsData(): AfterScreenshotPayload[] | undefined { return this.screenshotsData; } diff --git a/packages/cypress-cloud/support/index.ts b/packages/cypress-cloud/support/index.ts index fac23d4..7d69c66 100644 --- a/packages/cypress-cloud/support/index.ts +++ b/packages/cypress-cloud/support/index.ts @@ -4,6 +4,40 @@ import safeStringify from "fast-safe-stringify"; const afterReportedTests: string[] = []; const beforeReportedTests: string[] = []; +function pickTestData(test: Mocha.Runnable) { + return { + async: test.async, + body: test.body, + duration: test.duration, + // @ts-ignore + err: test.err, + // @ts-ignore + final: test.final, + // @ts-ignore + hooks: test.hooks, + // @ts-ignore + id: test.id, + // @ts-ignore + invocationDetails: test.invocationDetails, + // @ts-ignore + order: test.order, + pending: test.pending, + retries: test.retries(), + state: test.state, + sync: test.sync, + timedOut: test.timedOut, + // @ts-ignore + timings: test.timings, + // @ts-ignore + type: test.type, + // @ts-ignore + wallClockStartedAt: test.wallClockStartedAt, + title: test.title, + // @ts-ignore + currentRetry: test._currentRetry, + fullTitle: test.fullTitle(), + }; +} function sendTestAfterMetrics(test: Mocha.Runnable) { if (test.pending || !test.state) { // Test is either skipped or hasn't ran yet. @@ -12,16 +46,9 @@ function sendTestAfterMetrics(test: Mocha.Runnable) { } // @ts-ignore afterReportedTests.push(getTestHash(test)); - cy.task( - `currents:test:after:run`, - safeStringify({ - ...test, - fullTitle: test.fullTitle(), - }), - { - log: false, - } - ); + cy.task(`currents:test:after:run`, safeStringify(pickTestData(test)), { + log: false, + }); } function sendTestBeforeMetrics(test: Mocha.Runnable) {