# S03: Text System + Font Loading — UAT

**Milestone:** M002
**Written:** 2026-03-26T06:00:46.863Z

# S03: Text System + Font Loading — UAT

**Milestone:** M002
**Written:** 2026-03-26

## UAT Type

- UAT mode: mixed (artifact-driven for unit tests + live-runtime for interactive text features)
- Why this mode is sufficient: fontService logic is thoroughly tested (24 unit tests). The canvas integration follows the established pattern from S02 which was already validated. Interactive features (text tool, property controls, convert-to-paths) would need runtime verification in a browser.

## Preconditions

- `cd app && npm install` has been run (opentype.js v1.3.4 installed)
- Font files present: `app/public/fonts/{Roboto-Regular,OpenSans-Regular,Lato-Regular}.ttf`
- For interactive testing: `cd app && npm run dev` (Vite dev server on port 5173)

## Smoke Test

Run `cd app && npx tsc --noEmit && npx vitest run --reporter=verbose` — TypeScript compiles cleanly, all 95 tests pass including 24 fontService tests.

## Test Cases

### 1. Font registry returns bundled fonts

1. Import `getAvailableFonts` from `fontService.ts`
2. Call `getAvailableFonts()`
3. **Expected:** Returns array of at least 3 entries, each with `family` and `file` properties. Must include Roboto, Open Sans, and Lato.

### 2. Font loading and caching

1. Call `loadFont('/fonts/Roboto-Regular.ttf')`
2. Verify it resolves with an object that has `charToGlyph` and `unitsPerEm` properties
3. Call `isFontCached('/fonts/Roboto-Regular.ttf')`
4. **Expected:** Returns `true` — font is cached after first load. Second `loadFont` call does not trigger another fetch.

### 3. Text-to-path conversion produces valid SVG data

1. Load Roboto font via `loadFontByFamily('Roboto')`
2. Call `textToPathData('Hello', 'Roboto', 24, 0)`
3. **Expected:** Returns `{ d: string, width: number, height: number }` where `d` starts with 'M', contains 'Z' closepath commands, and width/height are positive numbers.

### 4. Letter spacing affects path width

1. Call `textToPathData('AB', 'Roboto', 24, 0)` — record width as W0
2. Call `textToPathData('AB', 'Roboto', 24, 5)` — record width as W1
3. Call `textToPathData('AB', 'Roboto', 24, -2)` — record width as W2
4. **Expected:** W1 > W0 > W2 (positive spacing widens, negative narrows)

### 5. Y coordinates are canvas-compatible (positive)

1. Call `textToPathData('Test', 'Roboto', 48, 0)`
2. Parse all numeric values after Y-position commands in the returned `d` string
3. **Expected:** All Y coordinates are ≥ 0 (canvas coordinate system, not font Y-up)

### 6. Text tool creates a text object on canvas

1. Open the app in browser, navigate to Design Canvas (View 2)
2. Click the text tool ('T') in the toolbar
3. Click on the canvas stage
4. **Expected:** A text object appears at the click position with default text "Text", font family "Roboto", font size 24, black fill, transparent stroke.

### 7. Text property controls render for text objects

1. Select a text object on the canvas
2. Inspect the ShapeProperties panel
3. **Expected:** Shows textarea for text content, font family dropdown (Roboto, Open Sans, Lato), font size input (8-200), letter spacing input (-5 to 20), line height input (0.5 to 3), fill color picker, and stroke controls.

### 8. Font family change updates text rendering

1. Select a text object
2. Change font family dropdown from "Roboto" to "Lato"
3. **Expected:** Text on canvas re-renders in Lato font. Font family dropdown shows "Lato" selected.

### 9. Text property edits persist through undo/redo

1. Select a text object, change font size from 24 to 48
2. Press Ctrl+Z (undo)
3. **Expected:** Font size reverts to 24
4. Press Ctrl+Shift+Z (redo)
5. **Expected:** Font size returns to 48

### 10. Convert to Paths replaces text with image

1. Select a text object with text "Hello" in Roboto
2. Click "Convert to Paths" button in ShapeProperties
3. Confirmation dialog appears — click OK
4. **Expected:** Text object is replaced with an image object at the same position. The image renders the text as vector paths (SVG). The object type in ObjectPanel changes from 'T' to the image icon. Text property controls disappear, replaced by standard image controls.

### 11. Convert to Paths — cancel does nothing

1. Select a text object
2. Click "Convert to Paths" button
3. Confirmation dialog appears — click Cancel
4. **Expected:** Nothing changes. Text object remains as-is with all its properties.

### 12. Text objects appear in ObjectPanel with correct icon

1. Add a text object, a rectangle, and an image to the canvas
2. Inspect the ObjectPanel (layers list)
3. **Expected:** Text object shows 'T' icon, rectangle shows its icon, image shows its icon. All three are listed with correct names.

### 13. Text objects work with alignment tools

1. Add two text objects at different positions
2. Select both (multi-select)
3. Click "Align Left" in AlignmentBar
4. **Expected:** Both text objects align to the leftmost x position. Alignment works identically to how it works for shapes.
