Skip to content

test: fourteen specs assert a default because the fixture never reaches the branch #454

Description

@IgorShevchik

Environment

main. Found by a review sweep over #452, which was itself the second instance of this in one file.

Description

#420 was the date column asserting Invalid Date. #450 was the status column asserting a default badge colour. Two in one file is not two accidents — it is a pattern, and a sweep of the snapshot corpus found fourteen more.

The shape is always the same: a case is added for a prop or slot, the fixture never satisfies its precondition, the snapshot records whatever the fallback renders, and the case now reads as coverage while asserting nothing. Nothing fails when it stops being true.

Tier 1 — worth fixing individually

1. test/components/Empty.spec.ts:32 — a prop the component does not have

['with avatar', { props: { avatar: { src: 'https://github.com/bitrix24.png' } } }],

EmptyProps (src/runtime/components/Empty.vue:11-52) has no avatar, and the string appears zero times in the component. It falls through to the root element and the snapshot pins it:

test/components/__snapshots__/Empty.spec.ts.snap:51   avatar="[object Object]"

Exactly #420's shape, and worse — there is no feature to fix the fixture against. Either Empty grows an avatar prop (it already has icon and a leading slot) or the case goes.

2. test/components/Countdown.spec.ts:22NaN pinned into an SVG attribute

The useCircle cases pass no seconds, so the default 0 applies and fullDashArray computes 0/0:

test/components/__snapshots__/Countdown.spec.ts.snap:42   stroke-dasharray="NaN 283"

It is the only dasharray value in either snapshot file — no snapshot renders a valid ring. ['with seconds', …] has the value but not useCircle; the two never meet. Fix is { useCircle: circle, seconds: 100 }, matching the unit test at Countdown.spec.ts:374-382 that already proves '283 283'.

3. The descriptionKey cluster — five specs setting a prop to its own default

spec items used carry description? component default
DropdownMenu.spec.ts:125 items no 'description'
InputMenu.spec.ts:52 items no 'description'
Listbox.spec.ts:48 items no 'description'
Select.spec.ts:53 items no 'description'
SelectMenu.spec.ts:52 items no 'description'

All five pass descriptionKey: 'description' — the value the prop already has — on items that lack the field. Doubly inert, and confirmed by snapshot identity: InputMenu > with descriptionKey is byte-identical to with items, with name, with selectedIcon and with size md.

In every one of the five, the right fixture (itemsWithDescription) is defined one line above and simply is not passed. Two siblings already do it correctly and are the model: CommandPalette.spec.ts:133 and ContextMenu.spec.ts:123. Both changes are needed — pass the with-description items and use a non-default key.

4. test/components/NavigationMenu.spec.ts:118 — a slot no item selects

The dynamic custom slot fires only for an item carrying slot: 'custom'. This is the only item-based spec whose fixture has no slot: key at all — every sibling has one. "Custom slot" appears nowhere in the snapshot, which is identical to four other cases.

Tier 2 — one-line fixes, better as a single sweep PR

  • Table.spec.ts:251 — the expanded slot renders only for an expanded row; the case passes no columns and no expansion, so the snapshot is byte-identical to with data.
  • Table.spec.ts:241, :244 — every virtualization case renders zero data rows under happy-dom, so with row pinning and virtualization is byte-identical to with virtualize and the pinning assertion is vacuous.
  • Table.spec.ts:144-153column.getIsSorted() is false in every snapshot; both sorted branches are unreachable. (And the asc branch resolves to the same icon as unsorted, so even adding a sort test could only distinguish desc.)
  • Table.spec.ts:187-222 — the whole actions items array is dead: every label appears 0 times across the snapshot file, because the menu is never opened.
  • Six named-slot cases whose precondition is never set, each byte-identical to a sibling: Alert.spec.ts:32 (close slot needs the close prop), CommandPalette.spec.ts:151 (empty needs no results), Progress.spec.ts:32 (status needs status/modelValue), Tabs.spec.ts:54 (the slot: 'custom' item is not the active tab), ContextMenu.spec.ts:137 (the item is inside a submenu that never opens), ChatMessages.spec.ts:41,:43.
  • DescriptionList.spec.ts:83with default slot passes no items, so it equals with empty items.
  • Stepper.spec.ts:28defaultValue: 'Address' picks index 0, already the active step. 'Shipping' would prove it. (Timeline.spec.ts:47 picks index 1 and is fine.)

Adjacent — a component defect, not a test one

src/runtime/components/Calendar.vue:267 omits nextMonth/nextYear/prevMonth/prevYear from omittedProps (which does list viewControl), so they are forwarded to the picker root and serialise as attributes:

test/components/__snapshots__/Calendar.spec.ts.snap:2518   nextmonth="[object Object]"

Unlike Empty's, these props work — this is a genuine leak, pinned as expected output. Worth splitting into its own issue against the component.

The scale problem underneath

Of the 35 renderEach snapshot entries in Table.spec.ts, only two render the custom cell functions at all — every other entry uses columns auto-derived from the data keys. The entire columns array's coverage rests on those two entries, which is why a dead branch there survives so easily.

That generalises: a renderEach matrix produces a lot of snapshots cheaply, and the cheapness is the problem — nothing tells you which of them are distinguishable from each other.

Suggested guard

A spec that groups snapshot entries by identical body and fails on collisions would have caught most of the above, since the symptom is nearly always "this case is byte-identical to a sibling". test/utils/ already holds cross-cutting invariant specs of this kind (docs-logical-properties.spec.ts, component-count-claims.spec.ts), so there is a pattern to follow.

It would need an allowlist for legitimately-identical pairs, which is itself useful: the allowlist becomes the record of which cases are known not to assert anything.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtesting

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions