Skip to content

Commit bc9a4e3

Browse files
fix(runner): keep the may-still-be-executing warning on the unreachable snippet answer
1 parent 664abf7 commit bc9a4e3

5 files changed

Lines changed: 40 additions & 61 deletions

File tree

.changeset/evaluate-snippet-failure-reasons.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"@qawolf/cli": patch
33
---
44

5-
Tell a runner that cannot evaluate snippets apart from one that could not be reached, so the message names which it is and only the reachable case suggests retrying.
5+
`qawolf runner exec` now tells apart a runner with nothing attached to evaluate a snippet (`runner-cannot-evaluate-snippets`, exit `2`, which will never clear) from one that could not be reached (`runner-unreachable`, exit `4`, which may still be starting or busy) instead of reporting both the same way. Only the unreachable message warns that the snippet may still be executing, since only that case can have taken effect before its answer was lost.

src/core/messages/interactiveRunner/interact.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ export const interactMessages = {
1818
`The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
1919
runnerHasNoScreen:
2020
"This runner does not run a browser on a virtual desktop, so there is nothing about it to see or drive. Retrying will never help: launch a playwright runner instead.",
21-
runnerHasNoScreenToEvaluate:
22-
"The runner has no live page to evaluate the snippet against: a freshly launched runner has no page until a run opens one, so run a flow on it with qawolf runner run first. If it is not a runner that runs a browser at all, this will never clear. It is not proof the snippet did not run, so do not resubmit one that mutates the page without reading qawolf runner events console first.",
23-
evaluateAnsweredUnknown: (failureReason: string) =>
24-
`The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
2521
inspectNeedsABrowserRunner:
2622
"This runner is a mobile device, and element and page HTML are a browser's shapes. Retrying will never help. Launch a playwright runner to inspect one.",
2723
nothingToInspect: (errorMessage: string | undefined) =>
@@ -50,8 +46,16 @@ export const interactMessages = {
5046
`Could not read "${path}". Name a readable file, or "-" to read the snippet from stdin.`,
5147
snippetRan:
5248
"The snippet ran. Its value is not returned: read anything it printed with qawolf runner events console.",
49+
// Alone among these failure reasons, this one may have taken effect before its
50+
// answer was lost, so the message must not invite a bare repeat.
51+
snippetRunnerUnreachable:
52+
"The runner could not be reached. It may still be starting, or it may have terminated after inactivity. It is not proof the snippet did not run: a snippet that outlives the answer window is still executing when you read this, so do not blindly resubmit one that mutates the page without reading qawolf runner events console first.",
5353
snippetStopped:
5454
"The snippet was interrupted before it finished. Anything it printed first is in qawolf runner events console.",
55+
runnerCannotEvaluateSnippets:
56+
"This runner has no snippet evaluator attached, so there is nothing to run code against. Retrying will never help: launch a playwright, android, ios, or windows runner instead.",
57+
snippetAnsweredUnknown: (failureReason: string) =>
58+
`The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
5559
stdinEmptyAction:
5660
'Nothing arrived on stdin. Pipe the action in, or name the action type instead of "-".',
5761
stdinEmptySnippet:

src/domains/interactiveRunner/evaluateSnippet.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,27 @@ describe("handleRunnerExec", () => {
218218
expect(result?.errorBody).toContain("quota reached");
219219
expect(result?.exitCode).toBe(4);
220220
});
221+
222+
// A runner with nothing attached to serve `exec` will never clear, unlike a
223+
// genuinely unreachable one, which may still have run the snippet before its
224+
// answer was lost.
225+
it.each([
226+
["runner-cannot-evaluate-snippets", 2, "no snippet evaluator attached"],
227+
["runner-unreachable", 4, "not proof the snippet did not run"],
228+
])("reports %s", async (failureReason, exitCode, text) => {
229+
const { callPublicApi, ctx } = makeAuthCtx();
230+
callPublicApi.mockResolvedValue({
231+
ok: true,
232+
value: { failureReason, outcome: "failure" },
233+
});
234+
235+
const result = await handleRunnerExec(
236+
ctx,
237+
{ contextFile: undefined, runner: "ci", source: "flow.ts" },
238+
makeTestDeps(),
239+
);
240+
241+
expect(result?.exitCode).toBe(exitCode);
242+
expect(result?.error).toContain(text);
243+
});
221244
});

src/domains/interactiveRunner/evaluateSnippetFailure.test.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/domains/interactiveRunner/evaluateSnippetFailure.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { publicContractsV1 } from "@qawolf/api-contracts/v1";
22
import type { z } from "zod";
33

44
import { interactiveRunnerMessages } from "~/core/messages/index.js";
5+
import type { CommandResult } from "~/shell/commandContext.js";
56
import { exitCodes } from "~/shell/exit.js";
67

78
type EvaluateSnippetFailure = Extract<
@@ -10,30 +11,28 @@ type EvaluateSnippetFailure = Extract<
1011
>;
1112

1213
/** Why a snippet was not evaluated, and what the caller should do about it. */
13-
export type EvaluateSnippetRefusal = {
14-
error: string;
15-
exitCode: number;
16-
};
17-
1814
export function describeEvaluateSnippetFailure(
1915
failure: EvaluateSnippetFailure,
20-
): EvaluateSnippetRefusal {
16+
): Exclude<CommandResult, void> {
2117
const { failureReason } = failure;
2218
switch (failureReason) {
2319
case "runner-cannot-evaluate-snippets":
2420
return {
25-
error: interactiveRunnerMessages.runnerHasNoScreenToEvaluate,
21+
error: interactiveRunnerMessages.runnerCannotEvaluateSnippets,
2622
exitCode: exitCodes.invalidArgs,
2723
};
24+
// Alone among these failure reasons, this one may have taken effect
25+
// before its answer was lost, so the message must not invite a bare
26+
// repeat.
2827
case "runner-unreachable":
2928
return {
30-
error: interactiveRunnerMessages.runnerUnreachable,
29+
error: interactiveRunnerMessages.snippetRunnerUnreachable,
3130
exitCode: exitCodes.network,
3231
};
3332
default:
3433
failureReason satisfies never;
3534
return {
36-
error: interactiveRunnerMessages.evaluateAnsweredUnknown(failureReason),
35+
error: interactiveRunnerMessages.snippetAnsweredUnknown(failureReason),
3736
exitCode: exitCodes.network,
3837
};
3938
}

0 commit comments

Comments
 (0)