بنقرة واحدة
page-load-assertion
is_page_loaded() 四种验证模式与调用层级。触发:页面断言、is_page_loaded、page loaded、页面加载验证。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
is_page_loaded() 四种验证模式与调用层级。触发:页面断言、is_page_loaded、page loaded、页面加载验证。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Incrementally add regression test points to an existing PageObject. Trigger: add test point, add regression point, add test point, increase coverage, extend page methods.
POM layering and context sharing decision tree. Trigger: architecture, layering, POM, Page Object, base class design, add new role.
Browser viewport / headless / timeout layered configuration. Trigger: viewport, browser config, navigation timeout, headless, slow_mo, incognito.
Under a shared page, a test case that leaves the start page must return via the UI and assert. Trigger: round-trip closure, case round trip, reset timeout, scope shared page.
AST knowledge graph: change review, exploration, debugging, refactoring. Trigger: knowledge graph, code review, impact analysis, blast radius, refactoring.
One-click generation of PageObject + matching tests. Trigger: new page, generate page, gen page, create page, add page object.
| name | page-load-assertion |
| description | is_page_loaded() 四种验证模式与调用层级。触发:页面断言、is_page_loaded、page loaded、页面加载验证。 |
页面"正常"的判断不是检查 HTTP 状态码,而是验证用户能看到的关键元素确实渲染出来了。通过每个 PageObject 的 is_page_loaded() 方法统一实现。
is_page_loaded() 的四种验证模式按严格程度从低到高:
检查该页面独有的标志性容器是否可见。适用于结构简单、一个元素就能区分的页面:
PAGE_CONTAINER = "css=.feature-container" # P3
def is_page_loaded(self) -> bool:
return self.is_visible(self.PAGE_CONTAINER)
容器 + 内容 双重验证,确保"页面骨架到位 + 数据渲染完成":
PAGE_CONTAINER = "css=.feature-container" # P3
PAGE_TITLE = "text=欢迎使用" # P1
def is_page_loaded(self) -> bool:
return self.is_visible(self.PAGE_CONTAINER) and self.is_visible(self.PAGE_TITLE)
在结构和内容基础上,还验证当前 UI 状态(如某个 tab 处于 active)。适用于承担"起点状态判断"语义的页面:
PAGE_TITLE = "css=span.page-title" # P3
TAB_BAR = "css=ul.tab-bar" # P3
DEFAULT_TAB_ACTIVE = "css=ul.tab-bar li.tab.active >> text=默认Tab" # P3+P1
def is_page_loaded(self) -> bool:
return (
self.is_visible(self.PAGE_TITLE)
and self.is_visible(self.TAB_BAR)
and self.is_visible(self.DEFAULT_TAB_ACTIVE)
)
注意:状态类 selector(.active / aria-selected 等)必须配 text 锁定,否则切换 tab 后 .active 跟随新激活对象漂移。
把验证逻辑提取为有业务含义的方法名,便于 setup fixture 中单独调用:
def is_page_loaded(self) -> bool:
return self.is_login_success()
def is_login_success(self) -> bool:
return self.is_visible(self.SUCCESS_FLAG) and self.is_visible(self.NAV_LIST)
新页面需要 is_page_loaded()
│
├─ 页面结构简单,一个独有容器即可区分?
│ └─ 是 → 模式 A(单元素)
│
├─ 需要确认内容也渲染到位?
│ └─ 是 → 模式 B(容器 + 内容,推荐默认)
│
├─ 该页面是复位 fixture 的起点,需要判断 UI 状态?
│ └─ 是 → 模式 C(多元素 + 激活态)
│
├─ 验证逻辑在 setup 中也需要单独调用?
│ └─ 是 → 模式 D(委托给语义方法)
│
└─ 容器可能存在但内容未渲染(SPA 子 Tab 切换 / 异步加载)?
└─ 是 → 模式 E(JS evaluate 检查子元素数量)
| 原则 | 说明 |
|---|---|
| 选该页面独有的元素 | 不选通用 header/footer,选只有这个页面才有的容器或文本 |
| 优先选结构性容器 | 如 .feature-container、.detail-wrapper,页面骨架不易变 |
| 文本元素锁定业务语义 | 如 text=欢迎使用,确认内容渲染到位 |
| 状态类 selector 配 text | .active 会随操作漂移,必须加 >> text=具体文本 锁定 |
| 不选动态数据 | 具体数字、用户名等因数据变化导致断言不稳定 |
核心原则:优先选模板级文本(页面框架固定标题),禁止选数据级内容(动态计数、日期)。多视图场景中每个断言目标须同时满足「模板级 + 视图独占」。
完整规则与反例/正例 → assertion-patterns.md "断言目标稳定性"章节。
SPA 应用中,容器 div 可能存在于 DOM 且 is_visible 返回 true,但内部组件未挂载(白屏)。此时模式 A/B 的 is_visible(容器) 会误判为正常。
页面内容区有三种状态,必须分别处理:
页面内容区状态
│
├─ 白屏(渲染失败)
│ 内容区子元素数 = 0,组件未挂载
│ → 判定:❌ 测试失败,报"内容区域未渲染,疑似白屏"
│
├─ 空数据(无业务数据但渲染正常)
│ 内容区子元素数 > 0,但无业务数据项
│ 通常有空状态提示(图片 + "暂无数据"/"还没有收藏题哦"等文案)
│ → 判定:✅ 渲染正常,页面确实没有业务数据
│
└─ 有数据(正常渲染)
内容区子元素数 > 0,且有业务数据项
→ 判定:✅ 渲染正常
实现模板:
# 定位符
CONTENT_CONTAINER = "css=<内容主容器>" # 页面骨架容器
EMPTY_STATE = "css=<空状态提示元素>" # 空状态组件(图片+文案)
DATA_ITEM = "css=<业务数据项>" # 单个数据条目
def get_content_render_state(self) -> str:
"""返回内容区渲染状态:'blank' / 'empty' / 'loaded'。"""
return self.page.evaluate("""() => {
const container = document.querySelector('<内容主容器选择器>');
if (!container) return 'blank';
const contentArea = container.querySelector('<内容区域选择器>');
if (!contentArea || contentArea.children.length === 0) return 'blank';
return 'loaded';
}""")
def is_content_rendered(self) -> bool:
"""白屏检测:容器存在但内容区无子元素 → False。
空数据和有数据都算渲染成功 → True。"""
return self.get_content_render_state() != 'blank'
def has_data_items(self) -> bool:
"""是否有业务数据(排除空状态)。"""
return self.get_element_count(self.DATA_ITEM) > 0
用例中的三态断言:
# 第一层:渲染检测(白屏 vs 已渲染)
is_rendered = page_obj.is_content_rendered()
if not is_rendered:
assert False, "内容区域未渲染,疑似白屏"
# 第二层(可选):空数据 vs 有数据
# 根据业务预期决定是否需要进一步区分
if page_obj.has_data_items():
# 有数据 → 可以进一步验证数据内容
pass
else:
# 空数据 → 记录到报告,不视为失败
allure.attach("该Tab当前无业务数据(空状态)", ...)
⚠️ 陷阱:
| 错误做法 | 问题 | 正确做法 |
|---|---|---|
innerText.length > N 判断白屏 | 空状态提示文案可能很短,被误判为白屏 | children.length > 0 |
| 空数据等同于失败 | 无收藏/无错题是合理的业务状态 | 白屏才失败,空数据只记录 |
只检查容器 is_visible | 容器 div 可能存在但子组件未挂载 | JS evaluate 检查子元素 |
适用场景:
assert home.is_page_loaded(), "首页加载失败"if not self.home.is_page_loaded(): click_nav_home(); assert ...assert target.is_page_loaded(), "<页面>加载失败"is_page_loaded()——返回 bool,放在类的最后一个方法is_page_loaded() 内部只用 is_visible() 组合,不抛异常