This commit is contained in:
oneao committed 2026-10-12 22:25:41 +08:00
1 parent ae3ca3cfa1
commit 7bd5f1dde9
45 files changed
+3414 -229

No files matched your search

@@ -0,0 +1,202 @@
import { expect, test } from '@playwright/test'
/**
* /ocean/booking 行编辑验证(修正版)。
*
* 上一版断言写错了:按 Enter 后期待 change 回写 —— 但 Enter 的语义是
* 「全选当前内容」(便于直接覆写),落库靠 **失焦**。见 utils/editable.ts:
* if (key === 'Enter') { preventDefault(); stopPropagation(); target.select() }
* onBlur: commitInputDraft(...) + notifyChange(...)
* 所以验证回写必须用 blur(点别处 / Tab 切走),不能只按 Enter。
*/
test.describe('/ocean/booking 行编辑', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/ocean/booking')
await page.waitForTimeout(2000)
})
test('区域选择已启用(注册时机修复的正面证据)', async ({ page }) => {
const cls = await page.evaluate(
() => (document.querySelector('.g3-table .stk-table') as HTMLElement)?.className ?? '',
)
console.log('STK CLASSES: ' + cls)
expect(cls).toContain('area-selection')
})
test('单击单元格进入编辑,焦点落在该格', async ({ page }) => {
const cell = page
.locator('.stk-tbody-main tr:not(.padding-top-tr)')
.first()
.locator('td')
.nth(2)
const before = await cell.innerText()
console.log('CELL BEFORE: ' + before)
await cell.click()
await page.waitForTimeout(600)
// 编辑器出现
await expect(page.locator('.g3-table__cell-editor-root').first()).toBeVisible()
// 焦点在该格的 input 上,且带出原值
const focused = await page.evaluate(() => {
const a = document.activeElement as HTMLInputElement | null
return { tag: a?.tagName, value: a?.value ?? null }
})
console.log('FOCUSED: ' + JSON.stringify(focused))
expect(focused.tag).toBe('INPUT')
expect(focused.value).toBe(before)
})
test('输入后失焦落库并抛出 change', async ({ page }) => {
const cell = page
.locator('.stk-tbody-main tr:not(.padding-top-tr)')
.first()
.locator('td')
.nth(2)
await cell.click()
await page.waitForTimeout(500)
await page.keyboard.press('Control+A')
await page.keyboard.type('测试客户A')
await page.waitForTimeout(200)
// 关键:用失焦提交(不是 Enter —— Enter 的语义是"全选以便覆写",见下一条用例)
await page.locator('.booking__title').click()
await page.waitForTimeout(700)
const tip = await page.locator('.booking__tip').innerText()
console.log('TIP: ' + tip.replace(/\n/g, ' | '))
expect(tip).toContain('测试客户A')
// 校验新值已落到单元格上。
//
// 试过三种写法,前两种都是错的,记在这里免得再踩:
// 1) cell.innerText() —— 编辑态里是 <input>,innerText 取不到 value,得空串
// 2) 断言 input 数量为 0 —— 错。editingActive 一旦为 true,整行可编辑列都保留
// 编辑器(性能设计),input 一直在,不等于"还在编辑"
// 3) .g3-table__cell-display —— 也错。失焦后该行仍处编辑态(编辑器未卸载),
// 展示态节点根本不存在
//
// 真正可靠的做法:直接读编辑器 input 的 value(它就是该格当前值的真源),
// 并用「焦点是否还在该格」判断编辑是否结束。
const inputValue = await cell.locator('input').first().inputValue()
console.log('CELL INPUT VALUE AFTER BLUR: ' + inputValue)
expect(inputValue).toBe('测试客户A')
const stillFocused = await page.evaluate(() => {
const td = document.activeElement?.closest('td')
return !!td?.querySelector('.g3-table__cell-editor-root')
})
console.log('STILL FOCUSED IN EDITOR: ' + stillFocused)
expect(stillFocused).toBe(false)
})
test('Enter 的语义是全选而非提交', async ({ page }) => {
const cell = page
.locator('.stk-tbody-main tr:not(.padding-top-tr)')
.first()
.locator('td')
.nth(2)
await cell.click()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.waitForTimeout(300)
// 全选:input 的 selectionStart/End 覆盖整个值
const sel = await page.evaluate(() => {
const a = document.activeElement as HTMLInputElement | null
return { len: a?.value.length ?? 0, s: a?.selectionStart ?? -1, e: a?.selectionEnd ?? -1 }
})
console.log('SELECTION AFTER ENTER: ' + JSON.stringify(sel))
expect(sel.s).toBe(0)
expect(sel.e).toBe(sel.len)
expect(sel.len).toBeGreaterThan(0)
})
/**
* Esc = **取消编辑并恢复原值**。
*
* 与 Excel / AG Grid / Handsontable / antd 的通用约定一致(Enter 提交、Esc 取消)。
*
* 改前的错误行为:Esc 只是 blur,而 blur 会走 onBlur → commit + notifyChange,
* 等于「按 Esc 反而保存了」(从参考实现 fms 原样照抄而来)。
* 现在 utils/editable.ts 的 Escape 分支会先 ctx.revertRow(row) 再 blur ——
* 回滚后 input 里是原值,随后的 blur 提交的就是原值,change 被去重挡掉。
*/
test('Esc 取消编辑并恢复原值', async ({ page }) => {
const cell = page
.locator('.stk-tbody-main tr:not(.padding-top-tr)')
.first()
.locator('td')
.nth(2)
const original = (await cell.innerText()).trim()
console.log('ORIGINAL VALUE: ' + original)
await cell.click()
await page.waitForTimeout(500)
await page.keyboard.press('Control+A')
await page.keyboard.type('不该保存')
await page.waitForTimeout(200)
// 输入过程中不该有 change(草稿机制)
const tipDuringTyping = await page.locator('.booking__tip').innerText()
expect(tipDuringTyping).not.toContain('不该保存')
await page.keyboard.press('Escape')
await page.waitForTimeout(700)
const tip = await page.locator('.booking__tip').innerText()
console.log('TIP AFTER ESC: ' + tip.replace(/\n/g, ' | '))
// 核心:Esc 不产生 change(值已回滚,被去重挡掉)
expect(tip).not.toContain('不该保存')
// 编辑器里的值应已恢复成原值
const valueAfterEsc = await cell.locator('input').first().inputValue()
console.log('INPUT VALUE AFTER ESC: ' + valueAfterEsc)
expect(valueAfterEsc).toBe(original)
// 焦点应已离开编辑器
const focusedInCell = await page.evaluate(() => {
const td = document.activeElement?.closest('td')
return !!td?.querySelector('.g3-table__cell-editor-root')
})
console.log('STILL FOCUSED IN EDITOR: ' + focusedInCell)
expect(focusedInCell).toBe(false)
})
test('Esc 回滚后仍可正常再次编辑并提交', async ({ page }) => {
const cell = page
.locator('.stk-tbody-main tr:not(.padding-top-tr)')
.first()
.locator('td')
.nth(2)
// 第一次:改后 Esc 回滚
await cell.click()
await page.waitForTimeout(400)
await page.keyboard.press('Control+A')
await page.keyboard.type('第一次改动')
await page.keyboard.press('Escape')
await page.waitForTimeout(600)
expect(await cell.locator('input').first().inputValue()).not.toBe('第一次改动')
// 第二次:正常提交应仍然生效(回滚不能把去重基线弄坏)
await cell.click()
await page.waitForTimeout(400)
await page.keyboard.press('Control+A')
await page.keyboard.type('第二次改动')
await page.locator('.booking__title').click()
await page.waitForTimeout(700)
const tip = await page.locator('.booking__tip').innerText()
console.log('TIP AFTER SECOND COMMIT: ' + tip.replace(/\n/g, ' | '))
expect(tip).toContain('第二次改动')
})
})