test(web): cover the cursor replay's remaining branches
CI's coverage gate caught `ansi.ts` at 95.31% branches. My local check had scoped coverage to that one file, which measures a different test set than the gate does — the gate is the authority and it was right. Three branches. Two are now pinned, each verified in a real terminal first: `ab\r` + U+0301 + `x` shows `xb` — the redraw leaves the cursor at column 0, so a combining mark has no cell to attach to and the terminal shows nothing for it. It also revealed that the mark was kept when `cursor` was 0, which is fixed. `ab\rX\x1b[31m` then a plain line shows `Xb` unstyled and the next line RED: the mirror of the reset case, where the scan ends styled while the last cell is not, so the convergence has to OPEN the run at the line end for it to reach the following line. The third was `?? ''` on a `String.split` result, which always yields at least one element — removed rather than tested, since no input can reach it.
This commit is contained in:
@@ -9,6 +9,8 @@ import { parseAnsiLines } from '../src/ansi.ts'
|
||||
|
||||
const ESC = '\u001b'
|
||||
const BS = '\u0008'
|
||||
/** A combining acute accent: zero-width, so it takes no terminal column. */
|
||||
const ACCENT = '\u0301'
|
||||
|
||||
/** Paint `text` with the SGR `codes`, then reset. */
|
||||
function sgr(codes: string, text: string): string {
|
||||
@@ -318,6 +320,27 @@ describe('parseAnsiLines: line-end state and column widths', () => {
|
||||
expect(onlySpan('e\u0301x\rYZ')).toEqual({ text: 'YZ', style: undefined })
|
||||
})
|
||||
|
||||
it('drops a combining mark left with no cell to attach to by a redraw', () => {
|
||||
// Verified in a real terminal: `ab` then CR then U+0301 then `x` shows `xb`.
|
||||
// The redraw puts the cursor at column 0, so the mark has no preceding cell
|
||||
// and the terminal shows nothing for it rather than a lone accent.
|
||||
expect(onlySpan(`ab\r${ACCENT}x`)).toEqual({ text: 'xb', style: undefined })
|
||||
// A mark with no movement on its line never reaches the replay at all: it
|
||||
// is width business, not a cursor move, so it stays as authored.
|
||||
expect(onlySpan(`${ACCENT}abc`)).toEqual({ text: `${ACCENT}abc`, style: undefined })
|
||||
})
|
||||
|
||||
it('carries a colour opened after the last write onto the next line', () => {
|
||||
// The mirror of the reset case, verified in a real terminal: `ab` CR `X` then
|
||||
// `\x1b[31m` with nothing after it shows `Xb` UNSTYLED and the next line red.
|
||||
// The scan ends styled while the last cell is not, so the convergence has to
|
||||
// open the run at the line end for it to reach the following line.
|
||||
expect(parseAnsiLines(`ab\rX${ESC}[31m\nnext`)).toEqual([
|
||||
[{ text: 'Xb', style: undefined }],
|
||||
[{ text: 'next', style: { color: 'var(--dsw-alias-state-error-primary)' } }],
|
||||
])
|
||||
})
|
||||
|
||||
it('blanks a wide character\'s spacer once its lead cell is overwritten', () => {
|
||||
// Verified in a real terminal: `中x` redrawn with `A` shows `A x` — the wide
|
||||
// glyph's second cell becomes a blank rather than closing the gap, so the
|
||||
|
||||
Reference in New Issue
Block a user