|
| 1 | +import path from 'path'; |
| 2 | +import { fileURLToPath } from 'url'; |
| 3 | +import { test, expect, type SuperDocFixture } from '../../fixtures/superdoc.js'; |
| 4 | +import { getInlineSdtRange, getInlineSdtSnapshot, selectionScope, type SelectionScope } from '../../helpers/sdt.js'; |
| 5 | + |
| 6 | +/** |
| 7 | + * Shift+Right extending a selection across the inline SDT leading boundary. |
| 8 | + * Word parity contract sdt/inline-shift-right-boundary, Word 16.0 (lock-invariant). |
| 9 | + * |
| 10 | + * The parity fact: selection crosses into the control character-by-character |
| 11 | + * (the scope progresses outside-cc -> cc-and-beyond), it never snaps to the |
| 12 | + * whole control as a unit (whole-content-control). Lock-invariant. |
| 13 | + * |
| 14 | + * Consumes the parity axis helper selectionScope(). |
| 15 | + */ |
| 16 | + |
| 17 | +test.use({ config: { toolbar: 'full', showSelection: true } }); |
| 18 | + |
| 19 | +const DIR = path.dirname(fileURLToPath(import.meta.url)); |
| 20 | +const FIXTURE = { |
| 21 | + unlocked: path.resolve(DIR, 'fixtures/sd3237-inline-unlocked.docx'), |
| 22 | + contentLocked: path.resolve(DIR, 'fixtures/sd3237-inline-contentlocked.docx'), |
| 23 | +} as const; |
| 24 | + |
| 25 | +test.describe('SDT Shift+Right across the leading boundary - Word parity', () => { |
| 26 | + for (const mode of ['unlocked', 'contentLocked'] as const) { |
| 27 | + test(`${mode}: Shift+Right crosses into the SDT character-by-character, not atomically`, async ({ superdoc }) => { |
| 28 | + await superdoc.loadDocument(FIXTURE[mode]); |
| 29 | + await superdoc.waitForStable(); |
| 30 | + const sdt = await getInlineSdtRange(superdoc.page); |
| 31 | + expect(sdt).not.toBeNull(); |
| 32 | + |
| 33 | + // Anchor in the text just before the SDT (mirrors the contract's |
| 34 | + // outside-leading placement); a collapsed caret exactly on the node |
| 35 | + // boundary steps into the node instead of extending. |
| 36 | + await superdoc.setTextSelection(sdt!.pos - 2); |
| 37 | + await superdoc.page.evaluate(() => (window as any).editor.view.focus()); |
| 38 | + await superdoc.waitForStable(); |
| 39 | + |
| 40 | + const scopes: SelectionScope[] = []; |
| 41 | + for (let i = 0; i < 6; i++) { |
| 42 | + await superdoc.press('Shift+ArrowRight'); |
| 43 | + await superdoc.waitForStable(); |
| 44 | + scopes.push(selectionScope(await getInlineSdtSnapshot(superdoc.page, sdt!.id), sdt!)); |
| 45 | + } |
| 46 | + |
| 47 | + // Crossed into the content (overlaps + extends past the leading edge)... |
| 48 | + expect(scopes).toContain('cc-and-beyond'); |
| 49 | + // ...but never selected the whole control as a unit - character-granular. |
| 50 | + expect(scopes).not.toContain('whole-content-control'); |
| 51 | + // non-destructive |
| 52 | + expect((await getInlineSdtSnapshot(superdoc.page, sdt!.id)).sdtExists).toBe(true); |
| 53 | + }); |
| 54 | + } |
| 55 | +}); |
0 commit comments