import { defineComponent, shallowRef, nextTick } from "vue"; import { expect, test } from "vite-plus/test"; import { render } from "@vue-tui/testing"; import { Box, Text } from "@vue-tui/runtime"; test("absolute position with top and left offsets", async () => { const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n X\n"); }); test("absolute position with bottom and right offsets", async () => { const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n\n X\n"); }); test("absolute position with percentage offsets", async () => { const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n\n X\n"); }); test("absolute position with percentage bottom and right offsets", async () => { const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n X\n\n"); }); // Ink coerces a STRING position offset to a PERCENT (styles.ts // applyPositionStyles: `typeof value === 'string'` → // setPositionPercent(edge, parseFloat(value))). So a bare-numeric string like // top="50" is 50% of the container height, NOT 50 absolute cells. This mirrors // the "absolute position with percentage offsets" test (top="50%" left="50%") // but with bare-numeric strings. Without the fix vue forwards "50" raw to // setPosition → 50 absolute cells → pushed off-screen. test("absolute position with bare numeric string offsets is a percent (Ink parity)", async () => { const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n\n X\n"); }); test("relative position offsets visual position while keeping flow", async () => { const { lastFrame } = await render( defineComponent(() => () => ( A B )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe(" BA"); }); test("static position ignores offsets", async () => { const { lastFrame } = await render( defineComponent(() => () => ( A B )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("AB"); }); test("static position ignores percentage offsets", async () => { const { lastFrame } = await render( defineComponent(() => () => ( A B )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("AB"); }); test("clears top offset on rerender", async () => { const top = shallowRef(1); const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n X\n"); top.value = undefined; await nextTick(); expect(lastFrame({ trimLines: true })).toBe(" X\n\n"); }); test("clears percentage top and left offsets on rerender", async () => { const top = shallowRef("50%"); const left = shallowRef("50%"); const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n\n X\n"); top.value = undefined; left.value = undefined; await nextTick(); expect(lastFrame({ trimLines: true })).toBe("X\n\n\n"); }); test("clears percentage top and left offsets when props are omitted on rerender", async () => { const showOffsets = shallowRef(true); const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n\n X\n"); showOffsets.value = false; await nextTick(); expect(lastFrame({ trimLines: true })).toBe("X\n\n\n"); }); test("clears bottom and right offsets on rerender", async () => { const bottom = shallowRef(1); const right = shallowRef(1); const { lastFrame } = await render( defineComponent(() => () => ( X )), { columns: 100 }, ); expect(lastFrame({ trimLines: true })).toBe("\n\n X\n"); bottom.value = undefined; right.value = undefined; await nextTick(); expect(lastFrame({ trimLines: true })).toBe("X\n\n\n"); }); // Skipped: absolute position with top and left offsets - concurrent