-
Notifications
You must be signed in to change notification settings - Fork 137
feat: add optional viewport field to plan files + --viewport CLI flag for responsive/mobile testing #306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add optional viewport field to plan files + --viewport CLI flag for responsive/mobile testing #306
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1895,6 +1895,15 @@ export interface CliPlanInput { | |
| name: string; | ||
| description?: string; | ||
| priority?: CliCreatePriority; | ||
| /** | ||
| * Optional desktop viewport for the frontend browser run, in | ||
| * `<width>x<height>` form (e.g. `"390x844"` for a mobile device). | ||
| * When present it is forwarded to the backend so the browser-use | ||
| * runner can size the viewport before executing plan steps — the | ||
| * only way responsive/mobile-only UI (e.g. `md:hidden` bottom nav) | ||
| * can be exercised. Absent means the runner's desktop default. | ||
| */ | ||
| viewport?: string; | ||
| planSteps: CliPlanStep[]; | ||
| } | ||
|
|
||
|
|
@@ -2336,6 +2345,13 @@ interface CreateFromPlanOptions extends CommonOptions { | |
| timeoutIsDefault?: boolean; | ||
| /** Reserved for the M3.3 chain. Per-run target URL override. */ | ||
| targetUrl?: string; | ||
| /** | ||
| * Optional browser viewport override for the frontend runner, in | ||
| * `<width>x<height>` form (e.g. "390x844"). When set alongside | ||
| * `--plan-from`, overrides any viewport in the plan JSON file. | ||
| * Validated client-side before the POST. | ||
| */ | ||
| viewport?: string; | ||
| /** | ||
| * Names of `test create` flags the caller supplied that `--plan-from` | ||
| * ignores (identity lives in the JSON). Surfaced as a stderr advisory | ||
|
|
@@ -2425,6 +2441,23 @@ export async function runCreateFromPlan( | |
|
|
||
| const plan = readPlanFromGuarded(opts.planFrom, { ignoredFlags: opts.ignoredFlags }); | ||
|
|
||
| // `--viewport` is a CLI-level override of the viewport in the plan JSON — | ||
| // the one `--plan-from` field that is legitimately overridable from the | ||
| // command line (the plan file pins projectId/type/name/planSteps, but the | ||
| // viewport is a run-environment concern the caller may want to vary | ||
| // without editing the file, e.g. a mobile smoke pass on a desktop plan). | ||
| if (opts.viewport !== undefined) { | ||
| if (!/^\d+x\d+$/.test(opts.viewport)) { | ||
| throw localValidationError( | ||
| 'viewport', | ||
| 'must be a string in `<width>x<height>` format (e.g. "390x844")', | ||
| undefined, | ||
| 'flag', | ||
| ); | ||
| } | ||
| plan.viewport = opts.viewport; | ||
| } | ||
|
|
||
| const stderrFn = deps.stderr ?? ((line: string) => process.stderr.write(`${line}\n`)); | ||
|
|
||
| // Non-fatal advisory for `{{...}}`-style placeholders in step | ||
|
|
@@ -2472,6 +2505,7 @@ export async function runCreateFromPlan( | |
| name: plan.name, | ||
| description: plan.description, | ||
| priority: plan.priority, | ||
| viewport: plan.viewport, | ||
| planSteps: plan.planSteps, | ||
| }; | ||
|
|
||
|
|
@@ -2726,6 +2760,17 @@ function assertPlanShape( | |
| requireEnum(`${prefix}priority`, obj.priority, CLI_CREATE_PRIORITIES); | ||
| } | ||
|
|
||
| if (obj.viewport !== undefined) { | ||
| if (typeof obj.viewport !== 'string' || !/^\d+x\d+$/.test(obj.viewport)) { | ||
| throw localValidationError( | ||
| `${prefix}viewport`, | ||
| 'must be a string in `<width>x<height>` format when present (e.g. "390x844")', | ||
| undefined, | ||
| 'field', | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // `planSteps` missing is the single most common agent | ||
| // hallucination: LLMs (Copilot included) reliably nest steps under | ||
| // `plan.steps` or a bare top-level `steps`. Point directly at the fix | ||
|
|
@@ -2806,6 +2851,11 @@ function collectPlanIssues( | |
| if (obj.priority !== undefined) { | ||
| check(() => requireEnum(`${prefix}priority`, obj.priority, CLI_CREATE_PRIORITIES)); | ||
| } | ||
| if (obj.viewport !== undefined) { | ||
| if (typeof obj.viewport !== 'string' || !/^\d+x\d+$/.test(obj.viewport)) { | ||
| issues.push({ field: `${prefix}viewport`, reason: 'must be a string in `<width>x<height>` format' }); | ||
| } | ||
| } | ||
| check(() => | ||
| requireArrayLength(`${prefix}planSteps`, obj.planSteps, { | ||
| min: 1, | ||
|
|
@@ -9548,10 +9598,16 @@ export function createTestCommand(deps: TestDeps = {}): Command { | |
| .option('--name <name>', 'human-readable test name (becomes `title` in storage)') | ||
| .option('--description <text>', 'optional human description (≤ 2000 chars)') | ||
| .option('--priority <prio>', 'optional priority — one of: p0, p1, p2, p3') | ||
| .option( | ||
| '--viewport <WxH>', | ||
| 'optional browser viewport for the frontend runner (e.g. "390x844" for mobile). ' + | ||
| 'With --plan-from, overrides the viewport in the plan JSON.', | ||
| ) | ||
|
Comment on lines
+9601
to
+9605
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Do not silently ignore The option is registered on the shared If viewport is plan-only, reject the flag before As per path instructions, this thin client must preserve correctness and clear error handling. Also applies to: 9708-9708 🤖 Prompt for AI AgentsSource: Path instructions |
||
| .option('--code-file <path>', 'file containing the test code (≤ 350 KB)') | ||
| .option( | ||
| '--plan-from <path>', | ||
| 'JSON file with the full FE test definition — projectId, type, name, planSteps[] all live in the file ' + | ||
| 'JSON file with the full FE test definition — projectId, type, name, planSteps[], ' + | ||
| 'and optional viewport/description/priority all live in the file ' + | ||
| '(≤ 256 KB; mutually exclusive with --code-file). In this mode --project/--type/--name/--description/--priority are ignored.', | ||
| ) | ||
| .option( | ||
|
|
@@ -9649,6 +9705,7 @@ export function createTestCommand(deps: TestDeps = {}): Command { | |
| { | ||
| ...resolveCommonOptions(command), | ||
| planFrom: cmdOpts.planFrom, | ||
| viewport: cmdOpts.viewport, | ||
| run: cmdOpts.run === true, | ||
| wait: cmdOpts.wait === true, | ||
| timeout: parseTimeoutFlag(cmdOpts.timeout, 'timeout'), | ||
|
|
@@ -10774,6 +10831,7 @@ interface CreateFlagOpts { | |
| planFrom?: string; | ||
| /** Print the canonical plan-file skeleton and exit. */ | ||
| planTemplate?: boolean; | ||
| viewport?: string; | ||
| run?: boolean; | ||
| wait?: boolean; | ||
| timeout?: string; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Use one positive-dimension validator for every viewport input.
schemas/plan.schema.jsonrequires each dimension to match[1-9]\d*, but these runtime checks use\d+. Therefore0x844,390x0, and0390x0844pass--viewport,assertPlanShape, andtest lint, even though the schema rejects them.test create --plan-fromcan send a value that local validation should reject.Define one
VIEWPORT_PATTERNand use it in the CLI override,assertPlanShape, andcollectPlanIssues.Proposed fix
As per path instructions, plan validation and
test lintare local/offline and should reject invalid plans before network requests.Also applies to: 2763-2772, 2854-2858
🤖 Prompt for AI Agents
Source: Path instructions