Files
workspace/code/g3soft-libs/packages/ui/e2e-erp/verify-fix.spec.ts
T
2026-10-12 22:25:41 +08:00

203 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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('第二次改动')
})
})