Skip to content

Commit 438bb7e

Browse files
committed
refactor: rename rotate command to orientation, keep rotate as a deprecated alias
The top-level `rotate` command (device orientation: portrait/landscape) shared a name with the `gesture rotate` two-finger rotation gesture. Rename the orientation command to `orientation` and keep `rotate` working as a minimal, silent CLI alias (same mechanism as `tap`->`press`) for a few versions. The rename is applied across every layer: - command-descriptor registry `name`, daemon dispatch handler, and the typed system facet (metadata/cliReader/daemonWriter/schema/output formatter) - navigation projection + `CommandResultMap` (`OrientationCommandResult`, `action: 'orientation'`), client types (`OrientationCommandOptions`), and the runtime family (`device.system.orientation`) - interactor + backend methods -> `setOrientation` (matching the backend's `setKeyboard`/`setClipboard` verb convention); Android helper `rotateAndroid` -> `setAndroidOrientation` - Apple/cloud-webdriver capability keys and plugin gate - user-facing docs (commands.md, client-api.md) Client SDK method is `orientation` (client convention = camelCase of the command name, matching `back`/`home`/`appSwitcher`); execution layers use the imperative `setOrientation`. Deliberately unchanged: - the Swift runner wire protocol keeps `command: 'rotate'` — the runner has its own command namespace with no gesture collision, so renaming it would only risk CLI<->installed-runner version skew on physical devices - the `DeviceRotation` value type / `parseDeviceRotation` (names the orientation values, no collision) Note: `client.command.rotate` / `device.system.rotate` and the `RotateCommand*` exported types are removed (the alias only rewrites CLI tokens); SDK consumers must use `orientation`. The JSON `action` value changes `rotate` -> `orientation`.
1 parent 7c935fa commit 438bb7e

47 files changed

Lines changed: 184 additions & 152 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/__tests__/cli-help.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,15 @@ test('launch dispatches as a plain open without forcing a relaunch', async () =>
254254
assert.notEqual(result.calls[0]?.flags?.relaunch, true);
255255
});
256256

257+
test('rotate dispatches as orientation (deprecated alias) with positionals preserved', async () => {
258+
const result = await runCliCapture(['rotate', 'landscape-left', '--json']);
259+
assert.doesNotMatch(result.stderr, /Unknown command/);
260+
// Canonicalization: the daemon call must record orientation, never rotate.
261+
assert.equal(result.calls.length, 1);
262+
assert.equal(result.calls[0]?.command, 'orientation');
263+
assert.deepEqual(result.calls[0]?.positionals, ['landscape-left']);
264+
});
265+
257266
// From #1052 (credit: @vku2018): the alias must compose with the bare-ref
258267
// hint — `tap e3` normalizes to press, then gets the @e3 suggestion.
259268
test('tap with a bare ref gets the @ref hint, not an unknown-command error', async () => {

src/__tests__/contracts-schema-public.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
AppSwitcherCommandResult,
1212
BackCommandResult,
1313
HomeCommandResult,
14-
RotateCommandResult,
14+
OrientationCommandResult,
1515
TvRemoteCommandResult,
1616
} from '../contracts/navigation.ts';
1717
import type { ViewportCommandResult } from '../contracts/viewport.ts';
@@ -121,12 +121,12 @@ test('command result contracts are assignable to command result map', () => {
121121
} satisfies AppSwitcherCommandResult;
122122
const appSwitcherFromMap: CommandResult<'app-switcher'> = appSwitcher;
123123

124-
const rotate = {
125-
action: 'rotate',
124+
const orientation = {
125+
action: 'orientation',
126126
orientation: 'portrait',
127127
message: 'Rotated to portrait',
128-
} satisfies RotateCommandResult;
129-
const rotateFromMap: CommandResult<'rotate'> = rotate;
128+
} satisfies OrientationCommandResult;
129+
const orientationFromMap: CommandResult<'orientation'> = orientation;
130130

131131
const tvRemote = {
132132
action: 'tv-remote',
@@ -155,7 +155,7 @@ test('command result contracts are assignable to command result map', () => {
155155
assert.equal(backFromMap.mode, 'in-app');
156156
assert.equal(homeFromMap.action, 'home');
157157
assert.equal(appSwitcherFromMap.action, 'app-switcher');
158-
assert.equal(rotateFromMap.orientation, 'portrait');
158+
assert.equal(orientationFromMap.orientation, 'portrait');
159159
assert.equal(tvRemoteFromMap.button, 'select');
160160
assert.equal(clipboardFromMap.action === 'write' ? clipboardFromMap.textLength : -1, 11);
161161
assert.equal(

src/__tests__/runtime-public.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ test('internal backend, commands, and io modules are usable', () => {
258258
assert.equal(typeof commands.interactions.gesture, 'function');
259259
assert.equal(typeof commands.system.back, 'function');
260260
assert.equal(typeof commands.system.home, 'function');
261-
assert.equal(typeof commands.system.rotate, 'function');
261+
assert.equal(typeof commands.system.orientation, 'function');
262262
assert.equal(typeof commands.system.keyboard, 'function');
263263
assert.equal(typeof commands.system.clipboard, 'function');
264264
assert.equal(typeof commands.system.settings, 'function');

src/backend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ export type AgentDeviceBackend = {
493493
context: BackendCommandContext,
494494
options: BackendTvRemoteOptions,
495495
): Promise<BackendActionResult>;
496-
rotate?(
496+
setOrientation?(
497497
context: BackendCommandContext,
498498
orientation: BackendDeviceOrientation,
499499
): Promise<BackendActionResult>;

src/cli-command-aliases.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const CLI_COMMAND_ALIASES: readonly CliCommandAlias[] = [
1616
{ alias: 'tap', command: 'press' },
1717
{ alias: 'launch', command: 'open' },
1818
{ alias: 'relaunch', command: 'open', impliedFlags: ['relaunch'] },
19+
// Deprecated: `rotate` collided with the `gesture rotate` two-finger gesture.
20+
{ alias: 'rotate', command: 'orientation' },
1921
];
2022

2123
const aliasByToken: ReadonlyMap<string, CliCommandAlias> = new Map(

src/cli/parser/__tests__/args-parse-interaction.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,15 @@ test('parseArgs supports trigger-app-event payload argument', () => {
305305
assert.deepEqual(parsed.positionals, ['screenshot_taken', '{"source":"qa"}']);
306306
});
307307

308-
test('parseArgs accepts rotate orientation aliases', () => {
308+
test('parseArgs accepts the orientation command', () => {
309+
const parsed = parseArgs(['orientation', 'left'], { strictFlags: true });
310+
assert.equal(parsed.command, 'orientation');
311+
assert.deepEqual(parsed.positionals, ['left']);
312+
});
313+
314+
test('parseArgs normalizes the deprecated rotate alias to orientation', () => {
309315
const parsed = parseArgs(['rotate', 'left'], { strictFlags: true });
310-
assert.equal(parsed.command, 'rotate');
316+
assert.equal(parsed.command, 'orientation');
311317
assert.deepEqual(parsed.positionals, ['left']);
312318
});
313319

src/cli/parser/__tests__/cli-help-command-usage.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,17 @@ test('keyboard command usage is documented', async () => {
372372
);
373373
});
374374

375-
test('rotate command usage is documented', async () => {
375+
test('orientation command usage is documented', async () => {
376+
const help = await usageForCommand('orientation');
377+
if (help === null) throw new Error('Expected command help text');
378+
assert.match(help, /orientation <portrait\|portrait-upside-down\|landscape-left\|landscape-right>/);
379+
assert.match(help, /Set device orientation on iOS and Android/);
380+
});
381+
382+
test('deprecated rotate alias resolves to orientation usage', async () => {
376383
const help = await usageForCommand('rotate');
377384
if (help === null) throw new Error('Expected command help text');
378-
assert.match(help, /rotate <portrait\|portrait-upside-down\|landscape-left\|landscape-right>/);
379-
assert.match(help, /Rotate device orientation on iOS and Android/);
385+
assert.match(help, /orientation <portrait\|portrait-upside-down\|landscape-left\|landscape-right>/);
380386
});
381387

382388
test('settings usage documents canonical faceid states', async () => {

src/cli/parser/__tests__/cli-help-topics.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test('usage includes concise top-level commands', async () => {
3131
assert.doesNotMatch(usageText, /^ fling <up\|down\|left\|right>/m);
3232
assert.doesNotMatch(usageText, /^ pinch <scale> \[x\] \[y\]/m);
3333
assert.doesNotMatch(usageText, /^ rotate-gesture <degrees>/m);
34-
assert.match(usageText, /rotate <orientation>/);
34+
assert.match(usageText, /orientation <orientation>/);
3535
assert.match(usageText, /record start \[path\] \| record stop/);
3636
assert.match(usageText, /trace start <path> \| trace stop <path>/);
3737
});

src/cli/parser/__tests__/command-suggestions.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ test('the keyboard dismiss example uses a real keyboard action', () => {
4343
assert.doesNotThrow(() => keyboardCliReader(['dismiss'], baseFlags));
4444
});
4545

46-
// `launch`/`relaunch` (and `tap`) are true aliases normalized before the
47-
// unknown-command check, so they must never appear in the suggestion map —
46+
// `launch`/`relaunch`, `tap`, and `rotate` are true aliases normalized before
47+
// the unknown-command check, so they must never appear in the suggestion map —
4848
// a stale entry there would be dead code masking the alias.
4949
test('true aliases are not listed in the curated suggestion map', () => {
5050
const guesses = new Set(listCommandAliasSuggestionEntries().map(([guess]) => guess));
51-
for (const alias of ['launch', 'relaunch', 'tap']) {
51+
for (const alias of ['launch', 'relaunch', 'tap', 'rotate']) {
5252
assert.ok(!guesses.has(alias), `"${alias}" is a true alias and must not be a suggestion`);
5353
}
5454
});

src/cli/parser/command-suggestions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import { listCliCommandNames } from '../../command-catalog.ts';
1010
* parse as a valid invocation of it; the registry-drift tests in
1111
* `src/cli/parser/__tests__/command-suggestions.test.ts` fail the build on drift.
1212
*
13-
* True aliases (`tap` -> press, `launch`/`relaunch` -> open) are normalized
14-
* case-insensitively in `normalizeCommandAlias` (args.ts) before the
13+
* True aliases (`tap` -> press, `launch`/`relaunch` -> open, `rotate` ->
14+
* orientation) are normalized case-insensitively in `normalizeCommandAlias`
15+
* (args.ts) before the
1516
* unknown-command check runs, so they never reach this map and must not be
1617
* listed here. `start`/`restart` stay suggestion-only: `start` is genuinely
1718
* ambiguous, so a hint beats silently guessing.

0 commit comments

Comments
 (0)