Skip to content

test: cover @muyajs/core migration regression gaps (Phase G) - #4434

Merged
Jocs merged 1 commit into
developfrom
test/regression-gap-fill
Jun 10, 2026
Merged

Jocs merged 1 commit into
developfrom
test/regression-gap-fill

Conversation

@Jocs

@Jocs Jocs commented Jun 10, 2026

Copy link
Copy Markdown
Member

What

Fills the highest-value missing automated tests identified by the @muyajs/core migration regression audit — the recently-fixed bugs and data-loss-adjacent paths that had no engine coverage. All tests assert already-correct behavior (coverage gaps, not failing-first) and pass on current develop.

Gaps covered

Spec Gap Priority
headingLocaleCrash.spec.ts #4424 / #4427 — typing # to create a heading under a non-en locale used to crash on the HeadingCopyLink i18n.t path. No test booted Muya with a non-en locale and hit it. TOP (recently-fixed migration bug)
formatToggle.spec.ts Format.format() toggle-OFF + clear over a real selection (only the apply-side _addFormat was covered) data-loss-adjacent
paragraphContent/backspaceUnwrap.spec.ts Backspace-at-offset-0 cross-block merge + block-quote / list unwrap (all four branches) data-loss
tableCell/backspaceSafety.spec.ts empty-table Backspace → paragraph; previous-cell caret jump data-loss
table/insertRowColumn.spec.ts insertRow / insertColumn shape, align inheritance, caret cell (only removal was tested) heavily-rewritten core

TOP-priority detail (#4424/#4427)

The new spec boots Muya with the bundled zh-CN locale, drives the real # → heading conversion input path (the path that builds HeadingCopyLink and calls muya.i18n.t('Copy anchor link to this heading')), and asserts:

  • no throw + the heading actually converts,
  • the copy-anchor affordance renders with the translated label (resolves per-locale: en vs zh-CN),
  • heading-copy-link still emits on activation,
  • I18n.t stays crash-safe and falls back to the raw key for a key missing from a non-en locale (guards the optional-chaining fix directly).

Scope

Engine-only (packages/muya). 5 new spec files, 33 test cases. No production code changed.

Verification

pnpm -C packages/muya lint && lint:types && check-circular && test && test:spec all green (656 unit + 1347 conformance + the 33 new tests).

🤖 Generated with Claude Code

Fill the highest-value missing automated tests from the @muyajs/core
migration regression audit — the recently-fixed bugs and data-loss-adjacent
paths that had no engine coverage.

- headingLocaleCrash.spec.ts (#4424/#4427 TOP priority): boots Muya under a
  non-en (zh-CN) locale and types `#` to create a heading, exercising the
  HeadingCopyLink `i18n.t` path that used to crash. Asserts no throw, the
  translated copy-anchor label resolves per-locale, the affordance still emits
  `heading-copy-link`, and `I18n.t` stays crash-safe (raw-key fall-through)
  for a key missing from a non-en locale.
- formatToggle.spec.ts: the public `Format.format()` toggle-OFF and `clear`
  paths over a real boot (strong/em/del/u/mark/link/inline-code) — only the
  apply-side `_addFormat` was previously covered.
- backspaceUnwrap.spec.ts (data-loss): Backspace-at-offset-0 cross-block
  surgery — paragraph merge + caret join point, and block-quote / list unwrap
  across all four branches (only/first/middle), asserting no item is dropped.
- tableCell backspaceSafety.spec.ts (data-loss): empty-table Backspace →
  paragraph replacement, and previous-cell caret jump without destructive merge.
- table insertRowColumn.spec.ts: insertRow/insertColumn shape, per-column
  alignment inheritance, delimiter round-trip, and returned caret cell — only
  the removal paths were previously unit-tested.

All assert already-correct behavior (coverage gaps, not failing-first) and pass
on current develop. Gates: lint, lint:types, check-circular, test, test:spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Copilot AI review requested due to automatic review settings June 10, 2026 04:53
@Jocs
Jocs merged commit 52ec2b1 into develop Jun 10, 2026
8 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds targeted Vitest coverage in packages/muya to close high-value regression gaps found during the @muyajs/core migration audit—especially data-loss-adjacent backspace/formatting paths and a previously fixed non-en locale heading crash (#4424/#4427). No production code changes are included.

Changes:

  • Add regression spec booting Muya under zh-CN and exercising the real # → heading conversion path (incl. HeadingCopyLink i18n + event emission).
  • Add engine-boot tests covering inline format toggle-OFF + clear over real selections.
  • Add table/paragraph backspace and table insert row/column behavior tests to guard document-shape, caret placement, and alignment preservation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/muya/src/block/gfm/table/tests/insertRowColumn.spec.ts Adds coverage for Table.insertRow / insertColumn shape + alignment + caret cell return.
packages/muya/src/block/content/tableCell/tests/backspaceSafety.spec.ts Guards table-cell Backspace branches (empty table replace + previous-cell caret jump).
packages/muya/src/block/content/paragraphContent/tests/backspaceUnwrap.spec.ts Covers paragraph backspace-at-offset-0 merge/unwrap branches across blockquote/list contexts.
packages/muya/src/block/base/tests/formatToggle.spec.ts Covers Format.format() toggle-off and clear paths on booted content.
packages/muya/src/tests/headingLocaleCrash.spec.ts Adds non-en locale regression coverage for heading conversion + HeadingCopyLink i18n + event emission.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +43 to +47
afterEach(() => {
while (bootedHosts.length) {
const host = bootedHosts.pop()!;
host.remove();
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #4436: afterEach now calls window.getSelection()?.removeAllRanges() so a stale Range can no longer point into a removed host node and break a later test's setCursor.

Comment on lines +108 to +112
it('renders the copy-anchor affordance with the zh-CN translated label', () => {
const muya = bootMuya('\n', zhCN);
typeHeading(muya, '# Heading');

const affordance = copyLinkAffordance(muya);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #4436: this test is now async and await flush()es after typeHeading() before querying the affordance, so the next-frame conversion + HeadingCopyLink render is awaited rather than raced.

Comment on lines +123 to +131
it('resolves the affordance label per-locale (en vs zh-CN) for the same heading', () => {
const enMuya = bootMuya('\n', en);
typeHeading(enMuya, '# Heading');
const enLabel = copyLinkAffordance(enMuya)!.getAttribute('aria-label');

const zhMuya = bootMuya('\n', zhCN);
typeHeading(zhMuya, '# Heading');
const zhLabel = copyLinkAffordance(zhMuya)!.getAttribute('aria-label');

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #4436: the per-locale (en vs zh-CN) test is now async and await flush()es after each typeHeading() before reading the aria-label, so both conversions are flushed before the DOM is queried.

Comment on lines +137 to +145
it('still emits heading-copy-link when the zh-CN affordance is activated', () => {
const muya = bootMuya('\n', zhCN);
typeHeading(muya, '# Heading');

const handler = vi.fn();
muya.on('heading-copy-link', handler);
copyLinkAffordance(muya)!.dispatchEvent(
new MouseEvent('click', { bubbles: true, cancelable: true }),
);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #4436: this test is now async and await flush()es after typeHeading() before dispatching the click, so the affordance is guaranteed to exist (the conversion flush has landed) when the click fires.

Comment on lines +161 to +172
it('every shipped locale defines the heading copy-anchor key (no raw-key fall-through)', () => {
// The crash's second half was the missing locale key. Lock it in for
// every shipped locale so a future locale addition can't silently
// reintroduce the raw-key fall-through (and, under a non-en locale, the
// crash before the optional-chaining guard).
for (const locale of [en, zhCN]) {
const muya = bootMuya('\n', locale);
typeHeading(muya, '# Heading');
const label = copyLinkAffordance(muya)!.getAttribute('aria-label');
expect(label).toBe(locale.resource['Copy anchor link to this heading']);
}
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #4436 on both counts: the test now loops all nine shipped locales (de, en, es, fr, ja, ko, pt, zh-CN, zh-TW) instead of just [en, zhCN], so it lives up to its name — typing # is proven crash-safe under every shipped locale. It is also now async and await flush()es after each typeHeading() before reading the aria-label.

Comment on lines +105 to +117
it('lands the caret at the join point (end of the former first paragraph)', () => {
const muya = bootMuya('alpha\n\nbeta\n');
const beta = contentByText(muya, 'beta');

backspaceAtStart(muya, beta);

// After the merge the active block is `alpha`'s content; the caret sits
// at offset 5 (the original `alpha` length), where the two joined.
const merged = contentByText(muya, 'alphabeta');
const cursor = merged.getCursor();
expect(cursor).not.toBeNull();
expect(cursor!.start.offset).toBe(5);
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #4436: the caret/join-point test is now async and await flush()es before reading the merged tree, so contentByText('alphabeta') / getCursor() observe the post-merge state instead of racing the next-frame json1 flush.

Jocs added a commit that referenced this pull request Jun 10, 2026
… review) (#4436)

Post-merge Copilot review on #4434 flagged real timing-dependence in two
regression specs now on develop. Harden both so they deterministically
exercise their bug guards:

headingLocaleCrash.spec.ts
- afterEach now clears the document-global Selection
  (window.getSelection()?.removeAllRanges()) so a stale Range can't point
  into a removed host node and break a later test's setCursor.
- Every test that queries the HeadingCopyLink affordance / its aria-label /
  dispatches its click now `await flush()` after typeHeading(), since the
  paragraph->atx-heading conversion and the attachment render land on the
  next animation frame.
- The "every shipped locale" test now actually loops all nine shipped
  locales (de, en, es, fr, ja, ko, pt, zh-CN, zh-TW) instead of just
  [en, zhCN], so typing `#` is proven crash-safe under every locale — the
  whole point of the guard.

backspaceUnwrap.spec.ts
- The caret/join-point assertion now `await flush()` before reading the
  merged tree, since the merge op flushes on the next frame; without it
  contentByText('alphabeta')/getCursor() could observe the pre-merge state.

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
@Jocs
Jocs deleted the test/regression-gap-fill branch June 10, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants