|
|
@@ -56,6 +56,7 @@ const TAIL_MARKER = 'MWT_TABLES_DONE'
|
|
|
const FILL_MARKER = 'MWT_FILL_C1'
|
|
|
const WIDE_MARKER = 'MWT_WIDE_C01'
|
|
|
const LONG_CELL_MARKER = 'MWT_LONGCELL_F1'
|
|
|
+const SHORT_MARKER = 'MWT_SHORT_C1'
|
|
|
const MARKERS = [FILL_MARKER, WIDE_MARKER, LONG_CELL_MARKER]
|
|
|
/** Golden-facing names, in {@link MARKERS} order. */
|
|
|
const TABLE_NAMES = ['fill', 'wide', 'long-cell']
|
|
|
@@ -76,13 +77,20 @@ const SENTENCE = 'This cell carries one full sentence so the unwrapped table is
|
|
|
const LONG_TOKEN = 'workspace/deepseek-harness/packages/client/ui-primitives/src/markdown/render.tsx/'.repeat(3)
|
|
|
const CJK_SENTENCE = '这个单元格包含一段较长的中文说明,用来验证长内容在窄列宽下按最小可读宽度换行而不是把列压缩到无法阅读。'
|
|
|
|
|
|
-/** The assistant markdown: one 3-column fill, one 12-column wide, one long-cell table. */
|
|
|
+/** The assistant markdown includes fitting and overflowing wide tables. */
|
|
|
function tablesMarkdown(): string {
|
|
|
const wideHeader = [WIDE_MARKER, ...Array.from({ length: 11 }, (_, i) => `C${String(i + 2).padStart(2, '0')}`)]
|
|
|
const wideRow = (row: number): string[] =>
|
|
|
Array.from({ length: 12 }, (_, i) => `v${String(row)}${String(i + 1).padStart(2, '0')}`)
|
|
|
return [
|
|
|
- 'Three markdown tables exercise the wide-table layout rules.',
|
|
|
+ 'Markdown tables exercise the wide-table layout rules.',
|
|
|
+ '',
|
|
|
+ `| ${SHORT_MARKER} | C2 | C3 | C4 |`,
|
|
|
+ '| --- | --- | --- | --- |',
|
|
|
+ '| 1 | 2 | 3 | 4 |',
|
|
|
+ '| 5 | 6 | 7 | 8 |',
|
|
|
+ '',
|
|
|
+ 'The paragraph after the short table stays in place.',
|
|
|
'',
|
|
|
`| ${FILL_MARKER} | Current approach | Proposed approach |`,
|
|
|
'| --- | --- | --- |',
|
|
|
@@ -103,7 +111,7 @@ function tablesMarkdown(): string {
|
|
|
].join('\n')
|
|
|
}
|
|
|
|
|
|
-/** Build one closed, invariant-checked session fixture carrying the three tables. */
|
|
|
+/** Build one closed, invariant-checked session fixture carrying the tables. */
|
|
|
function wideTableFixture(): string {
|
|
|
const session = Session.create(SessionId('markdown-wide-table-source'))
|
|
|
const eventTimeOrigin = new Date().setHours(12, 0, 0, 0)
|
|
|
@@ -257,7 +265,8 @@ describe('web e2e: markdown tables fill the column, wide ones break out and scro
|
|
|
beforeAll(async () => {
|
|
|
scaffold = await launchWebScaffold({})
|
|
|
await seedSession(scaffold, wideTableFixture(), SEED_ID)
|
|
|
- browser = await chromium.launch()
|
|
|
+ // The geometry assertions include the space occupied by native scrollbars.
|
|
|
+ browser = await chromium.launch({ ignoreDefaultArgs: ['--hide-scrollbars'] })
|
|
|
page = await newEnglishPage(browser)
|
|
|
tripwire = watchConsole(page)
|
|
|
await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
|
|
|
@@ -393,6 +402,35 @@ describe('web e2e: markdown tables fill the column, wide ones break out and scro
|
|
|
expect(tripwire.pageErrors).toEqual([])
|
|
|
}, 120_000)
|
|
|
|
|
|
+ it('keeps a fitting wide table and its following paragraph stationary during interaction', async () => {
|
|
|
+ onTestFailed(() => saveFailureShot(page, 'web-e2e-markdown-short-table-height'))
|
|
|
+ await settleAt(1680)
|
|
|
+ const short = page.locator('[class*="tableScroll"]', { hasText: SHORT_MARKER })
|
|
|
+ await short.evaluate(element => element.scrollIntoView({ block: 'center', behavior: 'instant' }))
|
|
|
+ await page.mouse.move(4, 4)
|
|
|
+ await short.evaluate(element => element.blur())
|
|
|
+ expect(await short.evaluate(element => element.classList.contains('md-table-wide'))).toBe(true)
|
|
|
+ expect(await short.evaluate(element => element.scrollWidth - element.clientWidth)).toBeLessThanOrEqual(1)
|
|
|
+ const position = () => short.evaluate((element) => {
|
|
|
+ const following = element.nextElementSibling
|
|
|
+ if (following === null) throw new Error('short table has no following paragraph')
|
|
|
+ return {
|
|
|
+ height: element.getBoundingClientRect().height,
|
|
|
+ followingTop: following.getBoundingClientRect().top,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const resting = await position()
|
|
|
+ await short.hover()
|
|
|
+ await expect.poll(position).toEqual(resting)
|
|
|
+ await page.mouse.move(4, 4)
|
|
|
+ await short.focus()
|
|
|
+ expect(await short.evaluate(element => document.activeElement === element)).toBe(true)
|
|
|
+ await expect.poll(position).toEqual(resting)
|
|
|
+ await short.evaluate(element => element.blur())
|
|
|
+ await expect.poll(position).toEqual(resting)
|
|
|
+ expect(tripwire.pageErrors).toEqual([])
|
|
|
+ }, 120_000)
|
|
|
+
|
|
|
it('keeps the fill/scroll relations under page zoom', async () => {
|
|
|
onTestFailed(() => saveFailureShot(page, 'web-e2e-markdown-wide-table-zoom'))
|
|
|
await sweep()
|