import { expect, test } from '@playwright/test' /** * 表单类组件的真实布局回归。 * * 曾经踩过的坑: * - textarea 的 footer(计数 + 拖拽手柄)在文档流里,被挤到框外; * - 日期时间面板上下排列而不是「左日期 / 右时间」; * - 日期面板宽度跟随输入框被拉伸,日历格子间距被撑散; * - 非法日期(2026-02-30)被原生解析静默顺延成 3 月 2 日。 */ test.describe('Input / textarea', () => { test('拖拽手柄落在输入框内部右下角(不再被挤到框外)', async ({ page }) => { await page.goto('/') const root = page.locator('[data-testid="textarea-count"]') const box = (await root.boundingBox())! const handle = (await root.locator('.g3-input__resize-handle').boundingBox())! expect(handle.x + handle.width).toBeLessThanOrEqual(box.x + box.width + 1) expect(handle.y + handle.height).toBeLessThanOrEqual(box.y + box.height + 1) expect(handle.y).toBeGreaterThan(box.y + box.height / 2) }) test('字数统计与手柄同处一行,且有可访问语义', async ({ page }) => { await page.goto('/') const root = page.locator('[data-testid="textarea-count"]') await expect(root.locator('.g3-input__count')).toHaveText(/\d+ \/ 100/) await expect(root.locator('.g3-input__resize-handle')).toHaveAttribute('role', 'slider') await expect(root.locator('.g3-input__resize-handle')).toHaveAttribute('aria-orientation', 'vertical') }) test('number 输入实时净化,只保留一个小数点', async ({ page }) => { await page.goto('/') const input = page.locator('[data-testid="input-number"] input') await input.fill('12.3.4') await expect(input).toHaveValue('12.34') }) test('password 可切换明文', async ({ page }) => { await page.goto('/') const root = page.locator('[data-testid="input-password"]') await expect(root.locator('input')).toHaveAttribute('type', 'password') await root.locator('.g3-input__password-toggle').click() await expect(root.locator('input')).toHaveAttribute('type', 'text') }) }) test.describe('DatePicker', () => { test('日期时间面板呈「左日历 / 右时间」,且日历定宽不被拉伸', async ({ page }) => { await page.goto('/') const trigger = page.locator('[data-testid="date-picker"] .g3-date-picker__trigger') const triggerWidth = (await trigger.boundingBox())!.width await trigger.click() const panel = page.locator('.g3-date-picker__panel') await expect(panel).toBeVisible() const calendar = panel.locator('.g3-calendar__main') const time = panel.locator('.g3-calendar__time') await expect(calendar).toBeVisible() await expect(time).toBeVisible() const calendarBox = (await calendar.boundingBox())! const timeBox = (await time.boundingBox())! // 日历在左、时间列在右,同一行(纵向重叠) expect(timeBox.x).toBeGreaterThan(calendarBox.x + calendarBox.width - 1) expect(timeBox.y).toBeLessThan(calendarBox.y + calendarBox.height) // 日历定宽,面板不跟随输入框拉宽 expect(calendarBox.width).toBeLessThan(260) const panelBox = (await panel.boundingBox())! expect(panelBox.width).toBeLessThan(triggerWidth) }) test('面板内的确定按钮只有点选后才能用', async ({ page }) => { await page.goto('/') await page.locator('[data-testid="date-picker"] .g3-date-picker__trigger').click() const confirm = page.locator('.g3-calendar__footer button') await expect(confirm).toBeEnabled() }) test('非法日期不会被接受(不再静默顺延)', async ({ page }) => { await page.goto('/') const input = page.locator('[data-testid="date-picker"] .g3-date-picker__input') await input.click() await input.fill('2026-02-30') await input.press('Enter') // 回退到原值,不能变成 2026-03-02 await expect(input).not.toHaveValue('2026-03-02') await expect(page.locator('[data-testid="date-picker"]')).toHaveClass(/is-invalid/) }) }) test.describe('RangePicker', () => { test('区间面板打开且两端都有值', async ({ page }) => { await page.goto('/') const root = page.locator('[data-testid="range-picker"]') await expect(root.locator('input').first()).toHaveValue('2026-10-01') await expect(root.locator('input').nth(1)).toHaveValue('2026-10-08') await root.locator('.g3-range-picker__trigger').click() await expect(page.locator('.g3-range-picker__panel')).toBeVisible() }) })