Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions tests/behavior/tests/sdt/sdt-backspace-inside-trailing.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { test, expect, type SuperDocFixture } from '../../fixtures/superdoc.js';
import { getInlineSdtRange, getInlineSdtSnapshot, type InlineSdtRange } from '../../helpers/sdt.js';

/**
* Backspace with the caret at the inline SDT trailing edge, INSIDE the content
* (the other half of the boundary: inside-edge editing vs the outside-edge
* select-then-delete in sdt-backspace-outside-trailing).
*
* Expected behavior from the word-api parity contract
* sdt/inline-backspace-inside-trailing, Word 16.0
* Editable modes delete a character per press (content shrinks, wrapper kept);
* content-locked modes are blocked (content unchanged). Loads the exact .docx
* fixtures the contract pinned by sha256.
*/

test.use({ config: { toolbar: 'full', showSelection: true } });

const DIR = path.dirname(fileURLToPath(import.meta.url));
const FIXTURE = {
unlocked: path.resolve(DIR, 'fixtures/sd3237-inline-unlocked.docx'),
sdtLocked: path.resolve(DIR, 'fixtures/sd3218-inline-sdtLocked.docx'),
contentLocked: path.resolve(DIR, 'fixtures/sd3237-inline-contentlocked.docx'),
sdtContentLocked: path.resolve(DIR, 'fixtures/sd3218-inline-sdtContentLocked.docx'),
} as const;

async function setupInsideTrailing(superdoc: SuperDocFixture, fixture: string): Promise<InlineSdtRange> {
await superdoc.loadDocument(fixture);
await superdoc.waitForStable();
const sdt = await getInlineSdtRange(superdoc.page);
expect(sdt).not.toBeNull();
await superdoc.setTextSelection(sdt!.end); // last position inside the content
await superdoc.page.evaluate(() => (window as any).editor.view.focus());
await superdoc.waitForStable();
return sdt!;
}

async function pressN(superdoc: SuperDocFixture, key: string, n: number) {
for (let i = 0; i < n; i++) {
await superdoc.press(key);
await superdoc.waitForStable();
}
}

test.describe('SDT Backspace inside the trailing edge - Word parity', () => {
// Contract: sdt/inline-backspace-inside-trailing, Word 16.0

for (const mode of ['unlocked', 'sdtLocked'] as const) {
test(`${mode}: each Backspace deletes one character; wrapper preserved`, async ({ superdoc }) => {
const sdt = await setupInsideTrailing(superdoc, FIXTURE[mode]);
const original = sdt.content;
await pressN(superdoc, 'Backspace', 3);
const s = await getInlineSdtSnapshot(superdoc.page, sdt.id);
expect(s.sdtExists).toBe(true); // wrapper preserved
expect(s.empty).toBe(true); // collapsed caret
expect(s.sdtContent!.length).toBe(original.length - 3); // three chars gone from the end
expect(original.startsWith(s.sdtContent!)).toBe(true); // trailing chars removed, leading prefix kept
});
}

for (const mode of ['contentLocked', 'sdtContentLocked'] as const) {
test(`${mode}: Backspace is blocked; content unchanged`, async ({ superdoc }) => {
const sdt = await setupInsideTrailing(superdoc, FIXTURE[mode]);
const original = sdt.content;
await pressN(superdoc, 'Backspace', 3);
const s = await getInlineSdtSnapshot(superdoc.page, sdt.id);
expect(s.sdtExists).toBe(true);
expect(s.sdtContent).toBe(original); // blocked: nothing deleted
});
}
});
70 changes: 70 additions & 0 deletions tests/behavior/tests/sdt/sdt-delete-inside-leading.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { test, expect, type SuperDocFixture } from '../../fixtures/superdoc.js';
import { getInlineSdtRange, getInlineSdtSnapshot, type InlineSdtRange } from '../../helpers/sdt.js';

/**
* Delete with the caret at the inline SDT leading edge, INSIDE the content -
* the front-of-content mirror of sdt-backspace-inside-trailing.
*
* Expected behavior from the word-api parity contract
* sdt/inline-delete-inside-leading, Word 16.0
* Editable modes delete a character per press (content shrinks from the front,
* wrapper kept); content-locked modes are blocked (content unchanged).
*/

test.use({ config: { toolbar: 'full', showSelection: true } });

const DIR = path.dirname(fileURLToPath(import.meta.url));
const FIXTURE = {
unlocked: path.resolve(DIR, 'fixtures/sd3237-inline-unlocked.docx'),
sdtLocked: path.resolve(DIR, 'fixtures/sd3218-inline-sdtLocked.docx'),
contentLocked: path.resolve(DIR, 'fixtures/sd3237-inline-contentlocked.docx'),
sdtContentLocked: path.resolve(DIR, 'fixtures/sd3218-inline-sdtContentLocked.docx'),
} as const;

async function setupInsideLeading(superdoc: SuperDocFixture, fixture: string): Promise<InlineSdtRange> {
await superdoc.loadDocument(fixture);
await superdoc.waitForStable();
const sdt = await getInlineSdtRange(superdoc.page);
expect(sdt).not.toBeNull();
await superdoc.setTextSelection(sdt!.start); // first position inside the content
await superdoc.page.evaluate(() => (window as any).editor.view.focus());
await superdoc.waitForStable();
return sdt!;
}

async function pressN(superdoc: SuperDocFixture, key: string, n: number) {
for (let i = 0; i < n; i++) {
await superdoc.press(key);
await superdoc.waitForStable();
}
}

test.describe('SDT Delete inside the leading edge - Word parity', () => {
// Contract: sdt/inline-delete-inside-leading, Word 16.0

for (const mode of ['unlocked', 'sdtLocked'] as const) {
test(`${mode}: each Delete removes one character from the front; wrapper preserved`, async ({ superdoc }) => {
const sdt = await setupInsideLeading(superdoc, FIXTURE[mode]);
const original = sdt.content;
await pressN(superdoc, 'Delete', 3);
const s = await getInlineSdtSnapshot(superdoc.page, sdt.id);
expect(s.sdtExists).toBe(true);
expect(s.empty).toBe(true);
expect(s.sdtContent!.length).toBe(original.length - 3); // three chars gone from the front
expect(original.endsWith(s.sdtContent!)).toBe(true); // deleted from the leading edge
});
}

for (const mode of ['contentLocked', 'sdtContentLocked'] as const) {
test(`${mode}: Delete is blocked; content unchanged`, async ({ superdoc }) => {
const sdt = await setupInsideLeading(superdoc, FIXTURE[mode]);
const original = sdt.content;
await pressN(superdoc, 'Delete', 3);
const s = await getInlineSdtSnapshot(superdoc.page, sdt.id);
expect(s.sdtExists).toBe(true);
expect(s.sdtContent).toBe(original); // blocked: nothing deleted
});
}
});
Loading