open-design/apps/daemon/tests/design-systems-cli-help.test.ts
Chris Seifert af997b7cf5
feat: rename editable design systems from Settings + od CLI (#2812)
* feat: rename editable design systems from Settings + od CLI

Editable (user-created) design systems can already be renamed via
PATCH /api/design-systems/:id, but the capability was not surfaced
in the UI or CLI.

- Settings -> Design Systems: editable cards show a hover-reveal pencil
  next to the name that opens a rename modal; built-in cards stay
  read-only. Reuses common.rename/save/cancel (no new i18n keys).
- CLI: 'od design-systems rename <id> --title <new> [--json]', backed by
  a unit-tested pure arg parser (design-system-rename-args.ts).

Both surfaces call the existing PATCH endpoint.

* Route od design-systems --help and -h to the rename-aware usage

The dispatcher only special-cased the `help` subcommand, so
`od design-systems --help` and `-h` fell through to the generic library
list, which advertises only `list` and `show`. That left `rename` off the
main discovery path even though this PR ships it.

Pulled the usage text and the help-arg check into a small pure module so
`help`, `--help`, and `-h` all render the same rename-aware usage, and added
a test that asserts the flag forms route to help and that the text lists
rename. The pure module keeps the assertion off process.exit / console.log.

* Reject --title flag-as-value and keep the rename modal open on failure

Two rename edge cases from review.

CLI: parseDesignSystemRenameArgs took the next token after --title
unconditionally, so `rename user:acme --title --json` parsed the title as
"--json" and could rename the system to a flag name instead of failing usage
validation. A separate --title value must now be a real token; a leading dash
means the user uses the --title=<value> form. Malformed inputs return null,
which the CLI surfaces as a usage error.

Web: commitRename closed the modal unconditionally, but updateDesignSystemDraft
returns null on any non-OK response or fetch failure, so a transient error
dropped the typed title with no feedback. The modal now stays open with the
title intact and shows an inline error on failure, matching the existing import
error pattern in this component. Added tests for the flag-as-value rejection
and for the failed-update modal state.

* Gate the rename completion on the active modal session

commitRename mutated the shared modal state after awaiting the PATCH, so a
slow rename for system A could resolve after the user cancelled and opened a
rename for system B, then close B's modal or show A's failure inside B's
dialog.

A monotonic session token (bumped whenever the modal opens or closes) is now
captured before the request and rechecked after it resolves. A stale
completion skips all modal-state updates. The list update for a successful
rename still applies, since that reflects a real server-side change regardless
of which modal is open. Added a regression test that opens a second rename
before the first PATCH settles and confirms the newer modal is untouched.

* Localize the rename-failed error instead of hardcoding English

The inline rename error was hardcoded English on a Settings surface that
otherwise runs through useT(), so non-English users saw English while the
rest of the panel was localized.

Added settings.designSystemRenameFailed to the typed dictionary and all 19
locale files, and the modal now reads it through t(). The translations are
adapted from each locale's existing settings.rescanFailed string ("X failed.
Check the daemon and try again."), swapping the verb to rename, so the daemon
and retry wording matches what those locales already ship.
2026-05-25 03:13:35 +00:00

27 lines
1,005 B
TypeScript

import { describe, expect, it } from 'vitest';
import {
DESIGN_SYSTEMS_USAGE,
isDesignSystemsHelpArg,
} from '../src/design-systems-cli-help.js';
describe('od design-systems help surface', () => {
it('routes help, --help, and -h to the usage text', () => {
expect(isDesignSystemsHelpArg('help')).toBe(true);
expect(isDesignSystemsHelpArg('--help')).toBe(true);
expect(isDesignSystemsHelpArg('-h')).toBe(true);
});
it('does not treat subcommands or a missing arg as a help request', () => {
expect(isDesignSystemsHelpArg('list')).toBe(false);
expect(isDesignSystemsHelpArg('show')).toBe(false);
expect(isDesignSystemsHelpArg('rename')).toBe(false);
expect(isDesignSystemsHelpArg(undefined)).toBe(false);
});
it('advertises rename alongside list and show so the surface cannot drift', () => {
expect(DESIGN_SYSTEMS_USAGE).toContain('list');
expect(DESIGN_SYSTEMS_USAGE).toContain('show');
expect(DESIGN_SYSTEMS_USAGE).toContain('rename');
});
});