Loading...
Loading...
Compare original and translation side by side
<div x-show="displayErrorMessage" class="message error">.message.error#messages// WRONG — matches hidden Alpine x-show elements throughout DOM
await expect(page.locator('.message.success')).toContainText('Added to cart');
await expect(page.locator('.message-error')).toContainText('Error');
// RIGHT — scoped to the visible messages container
await expect(page.locator('#messages .message.success')).toContainText('Added to cart');
await expect(page.locator('#messages .message-error, #messages .message.error')).toContainText('Error');.message.message.error.message.successdiv.message.message.message.notice#maincontent#messages<div x-show="displayErrorMessage" class="message error">.message.error#messages// 错误写法 —— 匹配DOM中所有隐藏的Alpine x-show元素
await expect(page.locator('.message.success')).toContainText('Added to cart');
await expect(page.locator('.message-error')).toContainText('Error');
// 正确写法 —— 限定在可见的消息容器内
await expect(page.locator('#messages .message.success')).toContainText('Added to cart');
await expect(page.locator('#messages .message-error, #messages .message.error')).toContainText('Error');.message.message.error.message.successdiv.message.message.message.notice#maincontent#messagesgetByRole()sr-onlygetByLabel()getByText()page.locator('#maincontent').getByText(...)getByPlaceholder()getByAltText()getByTestId()data-testidaria-*[aria-label="pagination"][aria-current="page"]#messages .message.success:visible:visiblegetByRole()sr-onlygetByLabel()getByText()page.locator('#maincontent').getByText(...)getByPlaceholder()getByAltText()getByTestId()data-testidaria-*[aria-label="pagination"][aria-current="page"]#messages .message.success:visible:visible| Pattern | Problem | Solution |
|---|---|---|
| Strict mode: multiple matches | Scope to unique container ( |
| Element not initialized until visible | |
| Elements don't exist in DOM until condition true | Click the trigger first, then query children |
| Alpine clears value after form submit | Don't assert input value post-submit; verify via success message |
| Cart badge updates asynchronously | Use web-first assertions with timeout: |
| Hidden until hover | |
| Alpine form reveal | Fields hidden until checkbox checked | |
| May submit Alpine-bound form unexpectedly | Prefer explicit |
| 模式 | 问题 | 解决方案 |
|---|---|---|
| 严格模式下匹配多个元素 | 限定在唯一容器( |
| 元素在可见前未初始化 | 交互前调用 |
| 元素在条件为真前不存在于DOM中 | 先点击触发元素,再查询子元素 |
输入框上的 | Alpine在表单提交后清除值 | 提交后不要断言输入框值;通过成功消息验证 |
| 购物车徽章异步更新 | 使用带超时的web-first断言: |
| 悬停前隐藏 | 点击子元素前先对父元素执行 |
| Alpine表单显示 | 字段在复选框勾选前隐藏 | 勾选复选框后调用 |
输入框上的 | 可能意外提交绑定Alpine的表单 | 优先使用显式的 |
// DO — auto-retries // DON'T — no retry
await expect(loc).toBeVisible(); // expect(await loc.isVisible()).toBe(true);
await expect(loc).toContainText('X'); // expect(await loc.textContent()).toContain('X');waitForTimeout()// Cart count updates asynchronously via Alpine x-text
await expect(page.locator('#menu-cart-icon span[x-text="summaryCount"]'))
.not.toHaveText('0', { timeout: 15_000 });// 推荐写法 —— 自动重试 // 不推荐写法 —— 无重试机制
await expect(loc).toBeVisible(); // expect(await loc.isVisible()).toBe(true);
await expect(loc).toContainText('X'); // expect(await loc.textContent()).toContain('X');waitForTimeout()// 购物车数量通过Alpine x-text异步更新
await expect(page.locator('#menu-cart-icon span[x-text="summaryCount"]'))
.not.toHaveText('0', { timeout: 15_000 });| Element | Hyvä Selector | Luma Selector |
|---|---|---|
| Pagination nav | | |
| Page link | | |
| Active page | | |
| Filter button | | |
| Cart icon badge | | |
| Account menu | | |
| Success message | | |
| Error message | | |
| Main menu | | |
| Footer nav | | |
| Product image | | |
| Add to Cart (card) | | |
| 元素 | Hyvä选择器 | Luma选择器 |
|---|---|---|
| 分页导航 | | |
| 页码链接 | | |
| 当前页码 | | |
| 筛选按钮 | | |
| 购物车图标徽章 | | |
| 账户菜单 | | |
| 成功消息 | | |
| 错误消息 | | |
| 主导航 | | |
| 页脚导航 | | |
| 商品图片 | | |
| 添加到购物车(卡片) | | |
references/references/