import { expect, test } from '@playwright/test' /** * Drawer 四个方向的几何回归。 * * 漏测教训:之前只验了默认的 `right`,导致「上下方向的抽屉宽度不是 100%」一直没被发现。 * 根因是容器用 flex 的 justify-content / align-items 做吸附 —— 两条轴各管一个方向, * 上下面板在主轴(水平)方向没有约束,宽度缩到了内容宽。 * 现在改为按方向定边(上下 `left/right: 0`、左右 `top/bottom: 0`),与 fms 一致。 * * 断言以 `.g3-drawer__wrap`(定位容器)为参照,而不是写死视口尺寸: * 视口高度受 `devices['Desktop Chrome']` 与滚动条影响,写死会得到假失败。 * 这类断言必须放 E2E:真实布局结果在 happy-dom 里量不出来。 */ /** 打开指定方向的抽屉,返回面板与定位容器的几何 */ async function openDrawer(page: import('@playwright/test').Page, testId: string) { await page.getByTestId(testId).click() const drawer = page.locator('.g3-drawer') await expect(drawer).toBeVisible() // 等入场动画结束(transform 归零),否则量到的是滑入途中的位置 await page.waitForTimeout(450) const panel = (await drawer.boundingBox())! const wrap = (await page.locator('.g3-drawer__wrap').boundingBox())! return { drawer, panel, wrap } } test.describe('Drawer 方向与尺寸', () => { test('右侧:贴右边缘、纵向撑满,宽度取 width', async ({ page }) => { await page.goto('/') const { panel, wrap } = await openDrawer(page, 'open-drawer') expect(Math.round(panel.x + panel.width)).toBeCloseTo(Math.round(wrap.x + wrap.width), 0) expect(Math.round(panel.height)).toBeCloseTo(Math.round(wrap.height), 0) expect(Math.round(panel.width)).toBe(378) }) test('左侧:贴左边缘、纵向撑满', async ({ page }) => { await page.goto('/') const { panel, wrap } = await openDrawer(page, 'open-drawer-left') expect(Math.round(panel.x)).toBeCloseTo(Math.round(wrap.x), 0) expect(Math.round(panel.height)).toBeCloseTo(Math.round(wrap.height), 0) expect(Math.round(panel.width)).toBe(378) }) test('上方:贴顶、横向撑满(回归:宽度曾是内容宽)', async ({ page }) => { await page.goto('/') const { panel, wrap } = await openDrawer(page, 'open-drawer-top') expect(Math.round(panel.y)).toBeCloseTo(Math.round(wrap.y), 0) expect(Math.round(panel.width)).toBeCloseTo(Math.round(wrap.width), 0) expect(Math.round(panel.height)).toBe(378) }) test('下方:贴底、横向撑满(回归:宽度曾是内容宽)', async ({ page }) => { await page.goto('/') const { panel, wrap } = await openDrawer(page, 'open-drawer-bottom') expect(Math.round(panel.y + panel.height)).toBeCloseTo(Math.round(wrap.y + wrap.height), 0) expect(Math.round(panel.width)).toBeCloseTo(Math.round(wrap.width), 0) expect(Math.round(panel.height)).toBe(378) }) test('四个方向都能撑满对应轴,且面板不被自身遮罩压住', async ({ page }) => { await page.goto('/') for (const [testId, name, axis] of [ ['open-drawer', '右', 'height'], ['open-drawer-left', '左', 'height'], ['open-drawer-top', '上', 'width'], ['open-drawer-bottom', '下', 'width'], ] as const) { const { drawer, panel, wrap } = await openDrawer(page, testId) if (axis === 'height') { expect(Math.round(panel.height), `${name}侧抽屉未撑满高度`).toBeCloseTo( Math.round(wrap.height), 0, ) } else { expect(Math.round(panel.width), `${name}侧抽屉未撑满宽度`).toBeCloseTo( Math.round(wrap.width), 0, ) } const hit = await page.evaluate( ([x, y]) => { const el = document.elementFromPoint(x as number, y as number) return el ? String(el.className || el.tagName) : null }, [panel.x + panel.width / 2, panel.y + Math.min(20, panel.height / 2)], ) expect(hit, `${name}侧抽屉面板被遮挡`).toContain('g3-drawer') await page.keyboard.press('Escape') await expect(drawer).toBeHidden() } }) test('面板尺寸由组件自己决定,不受宿主页面全局样式污染', async ({ page }) => { await page.goto('/') // 模拟用户页面的「全局元素选择器」:面板本体是
,header / footer 同理 await page.addStyleTag({ content: ` section { margin: 16px; padding: 24px; border: 2px solid red; } header { margin: 12px; padding: 20px; } footer { margin: 12px; padding: 20px; } `, }) const { panel, wrap } = await openDrawer(page, 'open-drawer') const styles = await page.$eval('.g3-drawer', (el) => { const cs = getComputedStyle(el) return { margin: [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft], padding: [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft], borderWidth: cs.borderTopWidth, } }) expect(styles.margin).toEqual(['0px', '0px', '0px', '0px']) expect(styles.padding).toEqual(['0px', '0px', '0px', '0px']) expect(styles.borderWidth).toBe('0px') // 仍然撑满:宿主 margin 不该从高度里扣掉 16px expect(Math.round(panel.height)).toBeCloseTo(Math.round(wrap.height), 0) }) })