122 lines
5.1 KiB
TypeScript
122 lines
5.1 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
|
|
/**
|
|
* 弹层层级回归 —— 这类问题单测覆盖不到(需要真实层叠上下文与命中测试)。
|
|
*
|
|
* 曾经踩过的坑:
|
|
* 1. 档位顺序写反(popup < modal)→ 弹窗/抽屉内的下拉被遮罩盖住;
|
|
* 2. 受控打开 / 挂载即展开的浮层根本没分配 z-index;
|
|
* 3. Drawer 的 wrap 是 fixed 但没 z-index → 整个抽屉被自己的遮罩压住。
|
|
*
|
|
* 注意:playground 里有两个「挂载即展开」的常驻 Popover,
|
|
* 所以定位浮层时要用 :has-text / filter 精确命中,不能直接 `.g3-popover__popup`。
|
|
*/
|
|
|
|
/** 读取视口内某点最上层的元素类名(判断「谁盖住谁」的唯一可靠方式) */
|
|
async function topElementAt(page: import('@playwright/test').Page, x: number, y: number) {
|
|
return page.evaluate(
|
|
([px, py]) => {
|
|
const el = document.elementFromPoint(px as number, py as number)
|
|
if (!el) return null
|
|
return typeof el.className === 'string' ? el.className : el.tagName
|
|
},
|
|
[x, y],
|
|
)
|
|
}
|
|
|
|
test.describe('浮层层级', () => {
|
|
test('挂载即展开的浮层同样有层级,且同档按打开顺序递增', async ({ page }) => {
|
|
await page.goto('/')
|
|
|
|
const popupA = page.locator('.g3-popover__popup', { hasText: '浮层 A' })
|
|
const popupB = page.locator('.g3-popover__popup', { hasText: '浮层 B' })
|
|
await expect(popupA).toBeVisible()
|
|
await expect(popupB).toBeVisible()
|
|
|
|
const zA = Number(await popupA.evaluate((el) => (el as HTMLElement).style.zIndex))
|
|
const zB = Number(await popupB.evaluate((el) => (el as HTMLElement).style.zIndex))
|
|
|
|
expect(zA).toBeGreaterThan(3000)
|
|
expect(zB).toBeGreaterThan(zA)
|
|
})
|
|
|
|
test('抽屉面板不会被自身遮罩盖住,点遮罩仍可关闭', async ({ page }) => {
|
|
await page.goto('/')
|
|
await page.getByTestId('open-drawer').click()
|
|
|
|
const drawer = page.locator('.g3-drawer')
|
|
await expect(drawer).toBeVisible()
|
|
// 等入场动画结束(transform 归零)再做命中测试,否则面板还在视口外
|
|
await page.waitForTimeout(450)
|
|
|
|
const panelBox = (await drawer.boundingBox())!
|
|
const inside = await topElementAt(
|
|
page,
|
|
panelBox.x + panelBox.width / 2,
|
|
panelBox.y + panelBox.height / 2,
|
|
)
|
|
expect(inside).not.toBeNull()
|
|
expect(inside).toContain('g3-drawer')
|
|
|
|
// 遮罩与 wrap 同级(同一实例共用层级)
|
|
const maskZ = Number(await page.$eval('.g3-drawer__mask', (el) => (el as HTMLElement).style.zIndex))
|
|
const wrapZ = Number(await page.$eval('.g3-drawer__wrap', (el) => (el as HTMLElement).style.zIndex))
|
|
expect(wrapZ).toBe(maskZ)
|
|
|
|
// 点面板外(遮罩区域)关闭
|
|
const maskBox = (await page.locator('.g3-drawer__mask').boundingBox())!
|
|
await page.mouse.click(maskBox.x + 40, maskBox.y + maskBox.height / 2)
|
|
await expect(drawer).toBeHidden()
|
|
})
|
|
|
|
test('弹窗内的下拉浮层高于遮罩,且可正常点选', async ({ page }) => {
|
|
await page.goto('/')
|
|
await page.getByTestId('open-modal').click()
|
|
await expect(page.locator('.g3-modal')).toBeVisible()
|
|
|
|
const select = page.getByTestId('modal-select')
|
|
await select.locator('.g3-select__trigger').click()
|
|
|
|
const popup = page.locator('.g3-popover__popup').filter({ has: page.locator('.g3-select__dropdown') })
|
|
await expect(popup).toBeVisible()
|
|
|
|
const modalZ = Number(await page.$eval('.g3-modal__mask', (el) => (el as HTMLElement).style.zIndex))
|
|
const popupZ = Number(await popup.evaluate((el) => (el as HTMLElement).style.zIndex))
|
|
expect(popupZ).toBeGreaterThan(modalZ)
|
|
|
|
// 浮层区域最上层必须是选项本体,而不是遮罩
|
|
const optionBox = (await page.locator('.g3-select__option').first().boundingBox())!
|
|
const hit = await topElementAt(page, optionBox.x + 10, optionBox.y + optionBox.height / 2)
|
|
expect(hit).toContain('g3-select__option')
|
|
})
|
|
|
|
test('反馈类提示盖在浮层与弹窗之上', async ({ page }) => {
|
|
await page.goto('/')
|
|
await page.getByTestId('open-modal').click()
|
|
const select = page.getByTestId('modal-select')
|
|
await select.locator('.g3-select__trigger').click()
|
|
await expect(
|
|
page.locator('.g3-popover__popup').filter({ has: page.locator('.g3-select__dropdown') }),
|
|
).toBeVisible()
|
|
|
|
// 触发按钮此时在弹窗遮罩之后:真实用户点不到它(这正是层级正确的表现),
|
|
// 所以直接派发 click 到元素上,本用例只验证「提示出现后压在最上层」
|
|
await page.getByTestId('toast-default').dispatchEvent('click')
|
|
await expect(page.locator('.g3-message').first()).toBeVisible()
|
|
|
|
const messageZ = Number(
|
|
await page.$eval('.g3-message__container', (el) => (el as HTMLElement).style.zIndex),
|
|
)
|
|
const popupZ = Number(
|
|
await page
|
|
.locator('.g3-popover__popup')
|
|
.filter({ has: page.locator('.g3-select__dropdown') })
|
|
.evaluate((el) => (el as HTMLElement).style.zIndex),
|
|
)
|
|
const modalZ = Number(await page.$eval('.g3-modal__mask', (el) => (el as HTMLElement).style.zIndex))
|
|
|
|
expect(messageZ).toBeGreaterThan(popupZ)
|
|
expect(popupZ).toBeGreaterThan(modalZ)
|
|
})
|
|
})
|