Skip to content

Commit

Permalink
fix: prevent shared execution state
Browse files Browse the repository at this point in the history
  • Loading branch information
agoldis committed Jul 6, 2023
1 parent 5225d56 commit f71ad89
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 217 deletions.
3 changes: 3 additions & 0 deletions examples/webapp/cypress/e2e_smoke/smoke.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
describe("TodoMVC", function () {
// a very simple example helpful during presentations
it("adds 2 todos", function () {
if (!!Cypress.env("CURRENTS_TESTING_FAIL")) {
throw new Error("oh!");
}
cy.get(".new-todo").type("learn testing{enter}").type("be cool{enter}");
cy.get(".todo-list li").should("have.length", 2);
});
Expand Down
32 changes: 26 additions & 6 deletions examples/webapp/scripts/currents-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,37 @@ import { run } from "cypress-cloud";
const projectId = process.env.CURRENTS_PROJECT_ID || "projectId";
const recordKey = process.env.CURRENTS_RECORD_KEY || "someKey";

const result = await run({
ciBuildId: `run-api-smoke-${new Date().toISOString()}`,
const ciBuildId = `run-api-smoke-${new Date().toISOString()}`;
const resultA = await run({
ciBuildId,
spec: ["cypress/e2e_smoke/**/*.spec.js"],
projectId,
recordKey,
group: "groupA",
});

if (result?.status === "failed") {
const resultB = await run({
ciBuildId: ciBuildId + "b",
spec: ["cypress/e2e_smoke/**/*.spec.js"],
projectId,
recordKey,
group: "groupB",
env: {
CURRENTS_TESTING_FAIL: "yes",
},
});

if (resultA?.status === "failed") {
process.exit(1);
}
assert(resultA?.runUrl?.match(/(\S+)/));
assert(resultA?.totalPassed === 1);
assert(resultA?.totalTests === 1);

if (resultB?.status === "failed") {
process.exit(1);
}
assert(result?.runUrl?.match(/(\S+)/));
assert(result?.totalPassed === 1);
assert(result?.totalTests === 1);
assert(resultB?.totalPassed === 0);
assert(resultB?.totalFailed === 1);
assert(resultB?.totalTests === 1);
})();
5 changes: 3 additions & 2 deletions packages/cypress-cloud/lib/results/mapResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from "cypress-cloud/types";

import * as SpecAfter from "../runner/spec.type";
import { getConfig } from "../runner/state";
import { ConfigState } from "../state";
import { getFakeTestFromException } from "./results";

function getScreenshot(s: SpecAfter.Screenshot): CypressScreenshot {
Expand Down Expand Up @@ -45,12 +45,13 @@ function getTest(
}

export function specResultsToCypressResults(
configState: ConfigState,
specAfterResult: SpecAfter.SpecResult
): CypressCommandLine.CypressRunResult {
return {
status: "finished",
// @ts-ignore
config: getConfig(),
config: configState.getConfig(),
totalDuration: specAfterResult.stats.wallClockDuration,
totalSuites: specAfterResult.stats.suites,
totalTests: specAfterResult.stats.tests,
Expand Down
21 changes: 12 additions & 9 deletions packages/cypress-cloud/lib/results/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
UpdateInstanceResultsPayload,
} from "../api";
import { MergedConfig } from "../config";
import { getConfig } from "../runner/state";
import { ConfigState } from "../state";

const debug = Debug("currents:results");

Expand Down Expand Up @@ -215,18 +215,21 @@ const getDummyFailedTest = (start: string, error: string) => ({
],
});

export function getFailedDummyResult({
specs,
error,
}: {
specs: string[];
error: string;
}): CypressCommandLine.CypressRunResult {
export function getFailedDummyResult(
configState: ConfigState,
{
specs,
error,
}: {
specs: string[];
error: string;
}
): CypressCommandLine.CypressRunResult {
const start = new Date().toISOString();
const end = new Date().toISOString();
return {
// @ts-ignore
config: getConfig() ?? {},
config: configState.getConfig() ?? {},
status: "finished",
startedTestsAt: new Date().toISOString(),
endedTestsAt: new Date().toISOString(),
Expand Down
34 changes: 19 additions & 15 deletions packages/cypress-cloud/lib/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,19 @@ import { pubsub } from "./pubsub";
import { summarizeTestResults, summaryTable } from "./results";
import {
createReportTaskSpec,
getExecutionStateResults,
reportTasks,
runTillDoneOrCancelled,
setConfig,
setSpecAfter,
setSpecBefore,
setSpecOutput,
} from "./runner";
import { shutdown } from "./shutdown";
import { getSpecFiles } from "./specMatcher";
import { ConfigState, ExecutionState } from "./state";
import { startWSS } from "./ws";

const debug = Debug("currents:run");

export async function run(params: CurrentsRunParameters = {}) {
const executionState = new ExecutionState();
const configState = new ConfigState();
activateDebug(params.cloudDebug);
debug("run params %o", params);
params = preprocessParams(params);
Expand All @@ -62,9 +60,7 @@ export async function run(params: CurrentsRunParameters = {}) {
} = validatedParams;

const config = await getMergedConfig(validatedParams);

// %state
setConfig(config?.resolved);
configState.setConfig(config?.resolved);

const { specs, specPattern } = await getSpecFiles({
config,
Expand Down Expand Up @@ -110,9 +106,11 @@ export async function run(params: CurrentsRunParameters = {}) {
cutInitialOutput();

await startWSS();
listenToSpecEvents();
listenToSpecEvents(configState, executionState);

await runTillDoneOrCancelled(
executionState,
configState,
{
runId: run.runId,
groupId: run.groupId,
Expand All @@ -126,7 +124,10 @@ export async function run(params: CurrentsRunParameters = {}) {
divider();

await Promise.allSettled(reportTasks);
const _summary = summarizeTestResults(getExecutionStateResults(), config);
const _summary = summarizeTestResults(
executionState.getResults(configState),
config
);

title("white", "Cloud Run Finished");
console.log(summaryTable(_summary));
Expand All @@ -145,19 +146,22 @@ export async function run(params: CurrentsRunParameters = {}) {
return _summary;
}

function listenToSpecEvents() {
function listenToSpecEvents(
configState: ConfigState,
executionState: ExecutionState
) {
pubsub.on("before:spec", async ({ spec }: { spec: Cypress.Spec }) => {
debug("before:spec %o", spec);
setSpecBefore(spec.relative);
executionState.setSpecBefore(spec.relative);
});

pubsub.on(
"after:spec",
async ({ spec, results }: { spec: Cypress.Spec; results: any }) => {
debug("after:spec %o %o", spec, results);
setSpecAfter(spec.relative, results);
setSpecOutput(spec.relative, getCapturedOutput());
createReportTaskSpec(spec.relative);
executionState.setSpecAfter(spec.relative, results);
executionState.setSpecOutput(spec.relative, getCapturedOutput());
createReportTaskSpec(configState, executionState, spec.relative);
}
);
}
1 change: 0 additions & 1 deletion packages/cypress-cloud/lib/runner/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./cancellable";
export * from "./reportTask";
export * from "./state";
34 changes: 19 additions & 15 deletions packages/cypress-cloud/lib/runner/reportTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,49 @@ import { InstanceId } from "cypress-cloud/types";
import Debug from "debug";
import { error } from "../log";
import { getReportResultsTask } from "../results";
import {
getExecutionStateInstance,
getExecutionStateSpec,
getInstanceResults,
} from "./state";
import { ConfigState, ExecutionState } from "../state";

const debug = Debug("currents:reportTask");

export const reportTasks: Promise<any>[] = [];

export const createReportTask = (instanceId: InstanceId) => {
const executionState = getExecutionStateInstance(instanceId);
if (!executionState) {
export const createReportTask = (
configState: ConfigState,
executionState: ExecutionState,
instanceId: InstanceId
) => {
const instance = executionState.getInstance(instanceId);
if (!instance) {
error("Cannot find execution state for instance %s", instanceId);
return;
}
if (executionState.reportStartedAt) {
if (instance.reportStartedAt) {
debug("Report task already created for instance %s", instanceId);
return;
}

executionState.reportStartedAt = new Date();
instance.reportStartedAt = new Date();

debug("Creating report task for instanceId %s", instanceId);
reportTasks.push(
getReportResultsTask(
instanceId,
getInstanceResults(instanceId),
executionState.output ?? "no output captured"
executionState.getInstanceResults(configState, instanceId),
instance.output ?? "no output captured"
).catch(error)
);
};

export const createReportTaskSpec = (spec: string) => {
const i = getExecutionStateSpec(spec);
export const createReportTaskSpec = (
configState: ConfigState,
executionState: ExecutionState,
spec: string
) => {
const i = executionState.getSpec(spec);
if (!i) {
error("Cannot find execution state for spec %s", spec);
return;
}
debug("Creating report task for spec %s", spec);
return createReportTask(i.instanceId);
return createReportTask(configState, executionState, i.instanceId);
};
56 changes: 32 additions & 24 deletions packages/cypress-cloud/lib/runner/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@ import {
import { runSpecFileSafe } from "../cypress";
import { isCurrents } from "../env";
import { divider, info, title, warn } from "../log";
import { ConfigState, ExecutionState } from "../state";
import { createReportTask, reportTasks } from "./reportTask";
import {
initExecutionState,
setInstanceOutput,
setInstanceResult,
} from "./state";

const debug = Debug("currents:runner");

export async function runTillDone(
executionState: ExecutionState,
configState: ConfigState,
{
runId,
groupId,
Expand All @@ -41,7 +39,7 @@ export async function runTillDone(
let hasMore = true;

while (hasMore) {
const newTasks = await runBatch({
const newTasks = await runBatch(executionState, configState, {
runMeta: {
runId,
groupId,
Expand All @@ -56,24 +54,30 @@ export async function runTillDone(
hasMore = false;
break;
}
newTasks.forEach((t) => createReportTask(t.instanceId));
newTasks.forEach((t) =>
createReportTask(configState, executionState, t.instanceId)
);
}
}

async function runBatch({
runMeta,
params,
allSpecs,
}: {
runMeta: {
runId: string;
groupId: string;
machineId: string;
platform: CreateInstancePayload["platform"];
};
allSpecs: SpecWithRelativeRoot[];
params: ValidatedCurrentsParameters;
}) {
async function runBatch(
executionState: ExecutionState,
configState: ConfigState,
{
runMeta,
params,
allSpecs,
}: {
runMeta: {
runId: string;
groupId: string;
machineId: string;
platform: CreateInstancePayload["platform"];
};
allSpecs: SpecWithRelativeRoot[];
params: ValidatedCurrentsParameters;
}
) {
let batch = {
specs: [] as InstanceResponseSpecDetails[],
claimedInstances: 0,
Expand Down Expand Up @@ -121,7 +125,7 @@ async function runBatch({
*/

// %state
batch.specs.forEach(initExecutionState);
batch.specs.forEach((i) => executionState.initInstance(i));

divider();
info(
Expand All @@ -148,12 +152,16 @@ async function runBatch({

// %state
batch.specs.forEach((spec) => {
setInstanceOutput(spec.instanceId, output);
executionState.setInstanceOutput(spec.instanceId, output);
const specRunResult = getCypressRunResultForSpec(spec.spec, rawResult);
if (!specRunResult) {
return;
}
setInstanceResult(spec.instanceId, specRunResult);
executionState.setInstanceResult(
configState,
spec.instanceId,
specRunResult
);
});

resetCapture();
Expand Down
Loading

0 comments on commit f71ad89

Please sign in to comment.