mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
668b64f3da
* chore: remove deprecated rotate CLI command alias (#1277) The rotate CLI command alias was renamed to orientation a few versions ago and is now removed at the next minor, aligned with the gesture-shim deprecation window. - Remove the rotate -> orientation entry from src/cli-command-aliases.ts. - Add an actionable parser error: invoking rotate now fails with "rotate was renamed to orientation". - Update the command-suggestion guard comments and the true-alias list in the curated suggestion map test. - Update CLI parser/help usage tests and src/__tests__/cli-help.test.ts to assert the migration error. - Remove the rotate deprecation note from the commands doc and add a breaking migration note to the changelog. No other command-name aliases are marked deprecated; long-press, metrics, tap, launch, and relaunch remain supported true aliases. Fixes #1277 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore: disambiguate removed rotate alias error message (#1277) Update the migration error so users who meant the two-finger gesture are pointed to `gesture rotate` instead of `orientation`. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore: align CHANGELOG with runtime rotate migration message (#1277) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Michał Pierzchała <thymikee@gmail.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
377 lines
13 KiB
TypeScript
377 lines
13 KiB
TypeScript
import { test } from 'vitest';
|
|
import assert from 'node:assert/strict';
|
|
import { AppError } from '../../../kernel/errors.ts';
|
|
import { parseArgs } from '../args.ts';
|
|
|
|
test('parseArgs accepts clipboard subcommands', () => {
|
|
const read = parseArgs(['clipboard', 'read'], { strictFlags: true });
|
|
assert.equal(read.command, 'clipboard');
|
|
assert.deepEqual(read.positionals, ['read']);
|
|
|
|
const write = parseArgs(['clipboard', 'write', 'otp', '123456'], { strictFlags: true });
|
|
assert.equal(write.command, 'clipboard');
|
|
assert.deepEqual(write.positionals, ['write', 'otp', '123456']);
|
|
});
|
|
|
|
test('parseArgs accepts keyboard subcommands', () => {
|
|
const status = parseArgs(['keyboard', 'status'], { strictFlags: true });
|
|
assert.equal(status.command, 'keyboard');
|
|
assert.deepEqual(status.positionals, ['status']);
|
|
|
|
const dismiss = parseArgs(['keyboard', 'dismiss'], { strictFlags: true });
|
|
assert.equal(dismiss.command, 'keyboard');
|
|
assert.deepEqual(dismiss.positionals, ['dismiss']);
|
|
|
|
const enter = parseArgs(['keyboard', 'enter'], { strictFlags: true });
|
|
assert.equal(enter.command, 'keyboard');
|
|
assert.deepEqual(enter.positionals, ['enter']);
|
|
});
|
|
|
|
test('parseArgs accepts tv-remote', () => {
|
|
const remote = parseArgs(['tv-remote', 'press', 'select', '--duration-ms', '250'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(remote.command, 'tv-remote');
|
|
assert.deepEqual(remote.positionals, ['press', 'select']);
|
|
assert.equal(remote.flags.durationMs, 250);
|
|
|
|
const longpress = parseArgs(['tv-remote', 'longpress', 'select'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(longpress.command, 'tv-remote');
|
|
assert.deepEqual(longpress.positionals, ['longpress', 'select']);
|
|
});
|
|
|
|
test('parseArgs accepts scroll pixel distance and duration flags', () => {
|
|
const parsed = parseArgs(['scroll', 'down', '--pixels', '240', '--duration-ms', '50'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'scroll');
|
|
assert.deepEqual(parsed.positionals, ['down']);
|
|
assert.equal(parsed.flags.pixels, 240);
|
|
assert.equal(parsed.flags.durationMs, 50);
|
|
});
|
|
|
|
test('parseArgs keeps no-record accepted on recordable commands', () => {
|
|
const press = parseArgs(['press', '10', '10', '--no-record'], { strictFlags: true });
|
|
assert.equal(press.flags.noRecord, true);
|
|
|
|
const swipe = parseArgs(['swipe', '0', '0', '10', '10', '--no-record'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(swipe.flags.noRecord, true);
|
|
});
|
|
|
|
test('parseArgs recognizes press series flags', () => {
|
|
const parsed = parseArgs([
|
|
'press',
|
|
'300',
|
|
'500',
|
|
'--count',
|
|
'12',
|
|
'--interval-ms=45',
|
|
'--hold-ms',
|
|
'120',
|
|
'--jitter-px',
|
|
'3',
|
|
]);
|
|
assert.equal(parsed.command, 'press');
|
|
assert.deepEqual(parsed.positionals, ['300', '500']);
|
|
assert.equal(parsed.flags.count, 12);
|
|
assert.equal(parsed.flags.intervalMs, 45);
|
|
assert.equal(parsed.flags.holdMs, 120);
|
|
assert.equal(parsed.flags.jitterPx, 3);
|
|
});
|
|
|
|
test('parseArgs recognizes press selector + snapshot flags', () => {
|
|
const parsed = parseArgs(['press', '@e2', '--depth', '3', '--scope', 'Sign In', '--raw'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'press');
|
|
assert.deepEqual(parsed.positionals, ['@e2']);
|
|
assert.equal(parsed.flags.snapshotDepth, 3);
|
|
assert.equal(parsed.flags.snapshotScope, 'Sign In');
|
|
assert.equal(parsed.flags.snapshotRaw, true);
|
|
});
|
|
|
|
test('parseArgs recognizes click series flags', () => {
|
|
const parsed = parseArgs(['click', '@e5', '--count', '4', '--interval-ms', '10'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'click');
|
|
assert.deepEqual(parsed.positionals, ['@e5']);
|
|
assert.equal(parsed.flags.count, 4);
|
|
assert.equal(parsed.flags.intervalMs, 10);
|
|
});
|
|
|
|
test('parseArgs recognizes click button flag', () => {
|
|
const parsed = parseArgs(['click', '@e5', '--button', 'secondary'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'click');
|
|
assert.deepEqual(parsed.positionals, ['@e5']);
|
|
assert.equal(parsed.flags.clickButton, 'secondary');
|
|
});
|
|
|
|
test('parseArgs recognizes double-tap flag for repeated press', () => {
|
|
const parsed = parseArgs(['press', '201', '545', '--count', '5', '--double-tap'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'press');
|
|
assert.deepEqual(parsed.positionals, ['201', '545']);
|
|
assert.equal(parsed.flags.count, 5);
|
|
assert.equal(parsed.flags.doubleTap, true);
|
|
});
|
|
|
|
test('parseArgs recognizes swipe positional + pattern flags', () => {
|
|
const parsed = parseArgs([
|
|
'swipe',
|
|
'540',
|
|
'1500',
|
|
'540',
|
|
'500',
|
|
'120',
|
|
'--count',
|
|
'8',
|
|
'--pause-ms',
|
|
'30',
|
|
'--pattern',
|
|
'ping-pong',
|
|
]);
|
|
assert.equal(parsed.command, 'swipe');
|
|
assert.deepEqual(parsed.positionals, ['540', '1500', '540', '500', '120']);
|
|
assert.equal(parsed.flags.count, 8);
|
|
assert.equal(parsed.flags.pauseMs, 30);
|
|
assert.equal(parsed.flags.pattern, 'ping-pong');
|
|
});
|
|
|
|
test('parseArgs recognizes gesture subcommand positionals', () => {
|
|
const pan = parseArgs(
|
|
['gesture', 'pan', '200', '420', '0', '-80', '500', '--pointer-count', '2'],
|
|
{
|
|
strictFlags: true,
|
|
},
|
|
);
|
|
assert.equal(pan.command, 'gesture');
|
|
assert.deepEqual(pan.positionals, ['pan', '200', '420', '0', '-80', '500']);
|
|
assert.equal(pan.flags.pointerCount, 2);
|
|
|
|
const fling = parseArgs(['gesture', 'fling', 'right', '200', '420', '180'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(fling.command, 'gesture');
|
|
assert.deepEqual(fling.positionals, ['fling', 'right', '200', '420', '180']);
|
|
|
|
const rotate = parseArgs(['gesture', 'rotate', '35', '200', '420'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(rotate.command, 'gesture');
|
|
assert.deepEqual(rotate.positionals, ['rotate', '35', '200', '420']);
|
|
|
|
const transform = parseArgs(['gesture', 'transform', '200', '420', '80', '-40', '2', '35'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(transform.command, 'gesture');
|
|
assert.deepEqual(transform.positionals, ['transform', '200', '420', '80', '-40', '2', '35']);
|
|
});
|
|
|
|
test('parseArgs recognizes type and fill delay flags', () => {
|
|
const typeParsed = parseArgs(['type', 'hello', '--delay-ms', '75'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(typeParsed.command, 'type');
|
|
assert.deepEqual(typeParsed.positionals, ['hello']);
|
|
assert.equal(typeParsed.flags.delayMs, 75);
|
|
|
|
const fillParsed = parseArgs(['fill', '@e5', 'search', '--delay-ms', '40'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(fillParsed.command, 'fill');
|
|
assert.deepEqual(fillParsed.positionals, ['@e5', 'search']);
|
|
assert.equal(fillParsed.flags.delayMs, 40);
|
|
});
|
|
|
|
test('parseArgs recognizes record --fps flag', () => {
|
|
const parsed = parseArgs(['record', 'start', './capture.mp4', '--fps', '30'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'record');
|
|
assert.deepEqual(parsed.positionals, ['start', './capture.mp4']);
|
|
assert.equal(parsed.flags.fps, 30);
|
|
});
|
|
|
|
test('parseArgs recognizes record --quality flag', () => {
|
|
const parsed = parseArgs(['record', 'start', './capture.mp4', '--quality', 'high'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'record');
|
|
assert.deepEqual(parsed.positionals, ['start', './capture.mp4']);
|
|
assert.equal(parsed.flags.quality, 'high');
|
|
});
|
|
|
|
test('parseArgs recognizes record --max-size flag', () => {
|
|
const parsed = parseArgs(['record', 'start', './capture.mp4', '--max-size', '1024'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'record');
|
|
assert.deepEqual(parsed.positionals, ['start', './capture.mp4']);
|
|
assert.equal(parsed.flags.screenshotMaxSize, 1024);
|
|
});
|
|
|
|
test('parseArgs recognizes record --hide-touches flag', () => {
|
|
const parsed = parseArgs(['record', 'start', './capture.mp4', '--hide-touches'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'record');
|
|
assert.deepEqual(parsed.positionals, ['start', './capture.mp4']);
|
|
assert.equal(parsed.flags.hideTouches, true);
|
|
});
|
|
|
|
test('parseArgs recognizes record --scope flag', () => {
|
|
const parsed = parseArgs(['record', 'start', './capture.mp4', '--scope', 'device'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'record');
|
|
assert.deepEqual(parsed.positionals, ['start', './capture.mp4']);
|
|
assert.equal(parsed.flags.recordingScope, 'device');
|
|
});
|
|
|
|
test('parseArgs recognizes screenshot flags', () => {
|
|
const parsed = parseArgs(
|
|
[
|
|
'screenshot',
|
|
'page.png',
|
|
'--pixel-density',
|
|
'2',
|
|
'--full',
|
|
'-f',
|
|
'--fullscreen',
|
|
'--max-size',
|
|
'1024',
|
|
'--no-stabilize',
|
|
'--normalize-status-bar',
|
|
],
|
|
{
|
|
strictFlags: true,
|
|
},
|
|
);
|
|
assert.equal(parsed.command, 'screenshot');
|
|
assert.deepEqual(parsed.positionals, ['page.png']);
|
|
assert.equal(parsed.flags.screenshotPixelDensity, 2);
|
|
assert.equal(parsed.flags.screenshotFullscreen, true);
|
|
assert.equal(parsed.flags.screenshotMaxSize, 1024);
|
|
assert.equal(parsed.flags.screenshotNoStabilize, true);
|
|
assert.equal(parsed.flags.screenshotNormalizeStatusBar, true);
|
|
});
|
|
|
|
test('parseArgs recognizes viewport command', () => {
|
|
const parsed = parseArgs(['viewport', '1280', '900', '--platform', 'web'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'viewport');
|
|
assert.deepEqual(parsed.positionals, ['1280', '900']);
|
|
assert.equal(parsed.flags.platform, 'web');
|
|
});
|
|
|
|
test('parseArgs recognizes longpress command', () => {
|
|
const parsed = parseArgs(['longpress', '300', '500', '800'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'longpress');
|
|
assert.deepEqual(parsed.positionals, ['300', '500', '800']);
|
|
});
|
|
|
|
test('parseArgs supports legacy long-press alias', () => {
|
|
const parsed = parseArgs(['long-press', '300', '500', '800'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'longpress');
|
|
assert.deepEqual(parsed.positionals, ['300', '500', '800']);
|
|
});
|
|
|
|
test('parseArgs supports tap alias for press', () => {
|
|
const parsed = parseArgs(['tap', '@e3'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'press');
|
|
assert.deepEqual(parsed.positionals, ['@e3']);
|
|
});
|
|
|
|
test('parseArgs preserves flags when tap is aliased to press', () => {
|
|
const parsed = parseArgs(['tap', '@e3', '--json'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'press');
|
|
assert.deepEqual(parsed.positionals, ['@e3']);
|
|
assert.equal(parsed.flags.json, true);
|
|
});
|
|
|
|
test('parseArgs supports trigger-app-event payload argument', () => {
|
|
const parsed = parseArgs(['trigger-app-event', 'screenshot_taken', '{"source":"qa"}'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'trigger-app-event');
|
|
assert.deepEqual(parsed.positionals, ['screenshot_taken', '{"source":"qa"}']);
|
|
});
|
|
|
|
test('parseArgs accepts the orientation command', () => {
|
|
const parsed = parseArgs(['orientation', 'left'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'orientation');
|
|
assert.deepEqual(parsed.positionals, ['left']);
|
|
});
|
|
|
|
test('parseArgs rejects the removed rotate alias with a migration error', () => {
|
|
assert.throws(
|
|
() => parseArgs(['rotate', 'left'], { strictFlags: true }),
|
|
(error: unknown) =>
|
|
error instanceof AppError &&
|
|
error.code === 'INVALID_ARGS' &&
|
|
/rotate was renamed to orientation/.test(String(error.message)),
|
|
);
|
|
});
|
|
|
|
test('parseArgs recognizes test --record-video flag', () => {
|
|
const parsed = parseArgs(['test', './suite', '--record-video'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'test');
|
|
assert.equal(parsed.flags.recordVideo, true);
|
|
});
|
|
|
|
test('snapshot command accepts command-specific flags', () => {
|
|
const ignoredLegacyFlag = '-' + 'c';
|
|
const parsed = parseArgs(
|
|
['snapshot', '-i', ignoredLegacyFlag, '--depth', '3', '-s', 'Login', '--timeout', '120000'],
|
|
{
|
|
strictFlags: true,
|
|
},
|
|
);
|
|
assert.equal(parsed.command, 'snapshot');
|
|
assert.equal(parsed.flags.snapshotInteractiveOnly, true);
|
|
assert.equal(parsed.flags.snapshotDepth, 3);
|
|
assert.equal(parsed.flags.snapshotScope, 'Login');
|
|
assert.equal(parsed.flags.timeoutMs, 120000);
|
|
});
|
|
|
|
test('snapshot command accepts diff alias flag', () => {
|
|
const parsed = parseArgs(['snapshot', '--diff', '-i', '--depth', '4', '--scope', 'Counter'], {
|
|
strictFlags: true,
|
|
});
|
|
assert.equal(parsed.command, 'diff');
|
|
assert.deepEqual(parsed.positionals, ['snapshot']);
|
|
assert.equal(parsed.flags.snapshotDiff, undefined);
|
|
assert.equal(parsed.flags.snapshotInteractiveOnly, true);
|
|
assert.equal(parsed.flags.snapshotDepth, 4);
|
|
assert.equal(parsed.flags.snapshotScope, 'Counter');
|
|
});
|
|
|
|
test('snapshot --diff --help stays on snapshot command help', () => {
|
|
const parsed = parseArgs(['snapshot', '--diff', '--help'], { strictFlags: true });
|
|
assert.equal(parsed.command, 'snapshot');
|
|
assert.equal(parsed.flags.snapshotDiff, true);
|
|
assert.equal(parsed.flags.help, true);
|
|
});
|
|
|
|
test('diff snapshot command accepts snapshot flags', () => {
|
|
const parsed = parseArgs(
|
|
['diff', 'snapshot', '-i', '--depth', '4', '--scope', 'Counter', '--raw'],
|
|
{ strictFlags: true },
|
|
);
|
|
assert.equal(parsed.command, 'diff');
|
|
assert.deepEqual(parsed.positionals, ['snapshot']);
|
|
assert.equal(parsed.flags.snapshotInteractiveOnly, true);
|
|
assert.equal(parsed.flags.snapshotDepth, 4);
|
|
assert.equal(parsed.flags.snapshotScope, 'Counter');
|
|
assert.equal(parsed.flags.snapshotRaw, true);
|
|
});
|