Skip to content

Commit

Permalink
chore(create-cloudflare): test select prompt from user perspective (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundhung authored Aug 2, 2024
1 parent 8a3c6c0 commit eb6f249
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 29 deletions.
81 changes: 61 additions & 20 deletions packages/create-cloudflare/e2e-tests/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { setTimeout } from "timers/promises";
import { stripAnsi } from "@cloudflare/cli";
import { spawn } from "cross-spawn";
import { retry } from "helpers/retry";
import { sleep } from "helpers/sleep";
import { fetch } from "undici";
import { expect } from "vitest";
import { version } from "../package.json";
Expand Down Expand Up @@ -49,7 +48,7 @@ const testEnv = {

export type PromptHandler = {
matcher: RegExp;
input: string[];
input: string[] | { type: "select"; target: RegExp | string };
};

export type RunnerConfig = {
Expand All @@ -71,32 +70,74 @@ export const runC3 = async (
const cmd = ["node", "./dist/cli.js", ...argv];
const proc = spawnWithLogging(cmd, { env: testEnv }, logStream);

const onData = (data: string) => {
handlePrompt(data);
};

// Clone the prompt handlers so we can consume them destructively
promptHandlers = promptHandlers && [...promptHandlers];
promptHandlers = [...promptHandlers];

const onData = (data: string) => {
// When changing selection, stdout updates but onData may not include the question itself
// so we store the current PromptHandler if we have already matched the question
let currentSelectDialog: PromptHandler | undefined;
const handlePrompt = (data: string) => {
const lines: string[] = data.toString().split("\n");
const currentDialog = promptHandlers[0];
const currentDialog = currentSelectDialog ?? promptHandlers[0];

lines.forEach(async (line) => {
if (currentDialog && currentDialog.matcher.test(line)) {
// Add a small sleep to avoid input race
await sleep(1000);
if (!currentDialog) {
return;
}

currentDialog.input.forEach((keystroke) => {
proc.stdin.write(keystroke);
});
const matchesPrompt = lines.some((line) =>
currentDialog.matcher.test(line),
);

// Consume the handler once we've used it
promptHandlers.shift();
// if we don't match the current question and we haven't already matched it previously
if (!matchesPrompt && !currentSelectDialog) {
return;
}

// If we've consumed the last prompt handler, close the input stream
// Otherwise, the process wont exit properly
if (promptHandlers[0] === undefined) {
proc.stdin.end();
}
if (Array.isArray(currentDialog.input)) {
// keyboard input prompt handler
currentDialog.input.forEach((keystroke) => {
proc.stdin.write(keystroke);
});
} else if (currentDialog.input.type === "select") {
// select prompt handler

// Our select prompt options start with ○ for unselected options and ● for the current selection
const currentSelection = lines.find((line) => line.startsWith("●"));

if (!currentSelection) {
// sometimes `lines` contain only the 'clear screen' ANSI codes and not the prompt options
return;
}
});

const matchesSelectionTarget =
typeof currentDialog.input.target === "string"
? currentSelection.includes(currentDialog.input.target)
: currentDialog.input.target.test(currentSelection);

if (matchesSelectionTarget) {
// matches selection, so hit enter
proc.stdin.write(keys.enter);
currentSelectDialog = undefined;
} else {
// target not selected, hit down and wait for stdout to update (onData will be called again)
proc.stdin.write(keys.down);
currentSelectDialog = currentDialog;
return;
}
}

// Consume the handler once we've used it
promptHandlers.shift();

// If we've consumed the last prompt handler, close the input stream
// Otherwise, the process wont exit properly
if (promptHandlers[0] === undefined) {
proc.stdin.end();
}
};

return waitForExit(proc, onData);
Expand Down
19 changes: 10 additions & 9 deletions packages/create-cloudflare/e2e-tests/workers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { frameworkToTest } from "./frameworkToTest";
import {
createTestLogStream,
isQuarantineMode,
keys,
recreateLogFolder,
runC3,
testProjectDir,
Expand All @@ -29,28 +28,28 @@ type WorkerTestConfig = RunnerConfig & {
const workerTemplates: WorkerTestConfig[] = [
{
template: "hello-world",
variants: ["ts", "js", "python"],
variants: ["TypeScript", "JavaScript", "Python"],
verifyDeploy: {
route: "/",
expectedText: "Hello World!",
},
},
{
template: "common",
variants: ["ts", "js"],
variants: ["TypeScript", "JavaScript"],
verifyDeploy: {
route: "/",
expectedText: "Try making requests to:",
},
},
{
template: "queues",
variants: ["ts", "js"],
variants: ["TypeScript", "JavaScript"],
// Skipped for now, since C3 does not yet support resource creation
},
{
template: "scheduled",
variants: ["ts", "js"],
variants: ["TypeScript", "JavaScript"],
// Skipped for now, since it's not possible to test scheduled events on deployed Workers
},
{
Expand Down Expand Up @@ -79,15 +78,17 @@ describe
workerTemplates
.flatMap<WorkerTestConfig>((template) =>
template.variants.length > 0
? template.variants.map((variant, index) => {
? template.variants.map((variant) => {
return {
...template,
name: `${template.name ?? template.template}-${variant}`,
name: `${template.name ?? template.template}-${variant.toLowerCase()}`,
promptHandlers: [
{
matcher: /Which language do you want to use\?/,
// Assuming the variants are defined in the same order it is displayed in the prompt
input: Array(index).fill(keys.down).concat(keys.enter),
input: {
type: "select",
target: variant,
},
},
],
};
Expand Down

0 comments on commit eb6f249

Please sign in to comment.