Files
vue-tui/packages/runtime-tests/integration/composables/use-screen-reader.test.tsx
T
Yunfei He 0b61ff2bf4 refactor(runtime)!: public-API audit follow-ups — align to Ink, record decisions (#163)
* refactor(runtime)!: rename AnimationOptions to UseAnimationOptions

Align the useAnimation options type with VueUse's UseXOptions convention, matching its sibling composable options bags (UseInputOptions / UsePasteOptions / UseFocusOptions) and the already-correct UseAnimationReturn. Hard rename, no deprecated alias — done while the package is pre-1.0 (0.0.x), so no stability break.

Recorded under "Public composable naming follows Vue conventions" in .agents/docs/ink-divergences.md. Surfaced by the public-API audit.

BREAKING CHANGE: the exported type AnimationOptions is renamed to UseAnimationOptions; update `import { type AnimationOptions }` to `import { type UseAnimationOptions }`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(runtime)!: tighten public API to Ink + record aria decision & alignment principle

Public-API audit follow-ups. Where vue-tui had drifted from Ink with no real Vue reason, align to Ink; reduce speculative surface; and record decisions in .agents/docs/ink-divergences.md.

- renderToString: drop the public `isScreenReaderEnabled` option (Ink's public renderToString is layout-only). The SR-capable variant moves to `@vue-tui/runtime/internal` as `renderToStringWithScreenReader` for the accessibility test suite; SR output is unchanged.

- useTerminalSize -> useWindowSize: drop the invented name + alias, align to Ink's `useWindowSize`. The reactive ref return shape is unchanged (shallowRef divergence still applies).

- DevState/DevErrorInfo: move from the public barrel to `@vue-tui/runtime/internal` (internal HMR types, no public consumer; Ink exposes no HMR types).

- docs(divergences): add a standing "Why align to Ink — and when not to" principle (alignment is a means to reduce bugs, not an end; Vue idiom + reasonableness outrank parity); record the aria-props camelCase decision with its run-verified type-safety boundary; stamp the rawMode-default and measureElement-$el entries with their KEEP decisions.

BREAKING CHANGE: removed public exports `useTerminalSize`, `DevState`, `DevErrorInfo`, and `renderToString`'s `isScreenReaderEnabled` option. Use `useWindowSize`; import HMR types from `@vue-tui/runtime/internal`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(runtime)!: move renderScreenReaderOutput to /internal-only

The screen-reader linearizer (ported from Ink's internal
`renderNodeToScreenReaderOutput`) was exported from the public barrel, but it
was never usefully public: its only parameter type `TuiNode` and the
node-construction primitives needed to build one are not public, so a public
consumer could not name or construct the argument. Ink keeps its counterpart
module-internal; we match that.

`renderScreenReaderOutput` + `ScreenReaderOptions` now live only in
`@vue-tui/runtime/internal` (already re-exported there). The live SR machinery
(render, the internal renderToStringWithScreenReader, the <Static> channel)
imports from the source module and is unaffected; public SR output is reached
via the mount `isScreenReaderEnabled` option.

public-api.test.ts: drop it from the public-members list; add a runtime guard
(absent from public, present on /internal) plus a compile-time @ts-expect-error
guard that the `ScreenReaderOptions` type cannot be re-added to the public
barrel.

Docs: new .agents/docs/accessibility-api.md (aria + SR design) and
api-contract.md (public surface = exports + their user-consumable types;
/internal is not the contract); resolve the open item and cross-link from
ink-divergences.md.

BREAKING CHANGE: renderScreenReaderOutput and ScreenReaderOptions are no longer
exported from @vue-tui/runtime; import from @vue-tui/runtime/internal if needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(runtime-tests): snapshot the exact public value-export set

Upgrade public-api.test.ts from "documented members present + targeted
negatives" to an exhaustive snapshot of the exact runtime value-export surface
of `@vue-tui/runtime`: adding, removing, or renaming any value export now fails
the test, so every public-surface change must be a deliberate edit to the list.

Type-only exports are erased at runtime and cannot be enumerated, so the type
surface stays guarded individually (the `@ts-expect-error` ScreenReaderOptions
guard); api-contract.md is updated to state this boundary precisely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 01:40:57 +08:00

36 lines
1.5 KiB
TypeScript

import { defineComponent } from "vue";
import { expect, test } from "vite-plus/test";
import { render } from "@vue-tui/testing";
import { Text, useIsScreenReaderEnabled } from "@vue-tui/runtime";
import { renderToStringWithScreenReader as renderToString } from "@vue-tui/runtime/internal";
// NOTE: tests that auto-detect SR via the process-GLOBAL env var
// `INK_SCREEN_READER` live in use-screen-reader-env.sequential.test.tsx (the
// repo's process-global convention). The tests below never mutate that global.
test("useIsScreenReaderEnabled returns false by default", async () => {
let result = false;
const App = defineComponent(() => {
result = useIsScreenReaderEnabled();
return () => <Text>sr test</Text>;
});
await render(App);
expect(result).toBe(false);
});
// B28: the existing test only covers the `false` default. When SR IS enabled,
// useIsScreenReaderEnabled() must return `true`. It reads ctx.isScreenReaderEnabled
// (useIsScreenReaderEnabled.ts:8), which renderToString seeds from the
// isScreenReaderEnabled option (render-to-string.ts:61-64,179).
test("useIsScreenReaderEnabled returns true when SR is enabled (renderToString option)", () => {
let result: boolean | undefined;
const App = defineComponent(() => {
result = useIsScreenReaderEnabled();
return () => <Text>sr enabled</Text>;
});
const output = renderToString(App, { isScreenReaderEnabled: true });
// Composable observed the enabled flag, and the SR text still rendered.
expect(result).toBe(true);
expect(output).toBe("sr enabled");
});