ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Handsontable Playwright E2E 页面对象与选择器实战指南(Page Objects, Selectors Fixtures)

Handsontable Playwright E2E 页面对象与选择器实战指南(Page Objects, Selectors  Fixtures) 前端UI组件【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址https://gitcode.com/gh_mirrors/ha/handsontable点击查看免费下载Handsontable 仓库的 Playwright E2E 测试套件tests/e2e/采用一套统一的编写约定用**页面对象Page Object Model**承载选择器与交互逻辑用data-testid在 fixture 中为每个单元格打上稳定挂接点并坚持web-first 条件等待绝不使用固定 sleep。本文以 page-objects.md 为骨架结合仓库中真实运行的页面对象、fixture 与测试配置源码系统讲解这套约定的每一条规则、落地写法与底层原理帮助你编写出稳定、可读、可维护的 Handsontable 功能测试。规范示例先读这两个文件整个约定以一对标准答案文件为基准动手写任何 spec 之前应首先通读它们规范测试tests/e2e/grid.spec.ts —— 展示了 spec 层的写法只表达意图renders the seeded data、edits a cell through the editor、adds a row不出现任何裸选择器。配套页面对象tests/fixtures/pages/GridPage.ts —— 选择器、交互步骤、查询方法的全部实现都集中在这里。// tests/e2e/grid.spec.ts节选 import { test, expect } from ../fixtures/test; import { GridPage } from ../fixtures/pages/GridPage; test.describe(grid demo, () { let grid: GridPage; test.beforeEach(async ({ page, theme, bundle }) { grid new GridPage(page, theme, bundle); await grid.goto(); }); test(renders the seeded data, async () { await grid.expectCell(0, 0, A1); await grid.expectCell(1, 1, B2); await grid.expectCell(4, 2, C5); }); test(edits a cell through the editor, async () { await grid.editCell(0, 0, edited); await grid.expectCell(0, 0, edited); }); });注意两个关键点spec 从 tests/fixtures/test.ts 导入test/expect而非直接从playwright/test从而自动获得theme与bundle两个矩阵参数页面对象在beforeEach中每次测试都新建一个实例保证隔离性。Page Object Model 规则页面对象把一个页面或一个组件包装起来对外只暴露意图级方法。四条规则是硬性的一个对象对应一个表面surface—— 一个网格、一个右键菜单、一个筛选对话框各建一个页面对象对象之间可以组合例如GridPage的方法可以返回一个ContextMenu页面对象而不是把菜单的逻辑塞进网格对象里。Locator 作为字段、惰性解析—— 在构造函数中直接赋值page.getByTestId(...)这类 Locator 字段绝不在构造函数里await。Locator 本身是惰性的真正的 DOM 查询发生在后续动作/断言时构造函数只需同步建立引用。方法要么是动作、要么是查询不做断言—— 唯一的例外是极薄的expectX辅助方法它内部只包装一条 web-first 断言以提升可读性例如GridPage.expectCell内部就是await expect(this.cell(row, col)).toHaveText(text)。真正的断言必须留在 spec 里这样失败时错误信息直接指向测试用例而不是埋在页面对象深处。禁止page.waitForTimeout—— 页面对象如果需要等待就等待一个条件DOM 可见、文本匹配而不是等待一段时长。规范示例即 GridPage.ts 的骨架export class GridPage { readonly grid: Locator; constructor(readonly page: Page) { this.grid page.getByTestId(grid); } async goto() { await this.page.goto(/tests/fixtures/demo/grid.html); await expect(this.cell(0, 0)).toBeVisible(); // DOM 条件而非 sleep 或自定义就绪标志 } cell(row: number, col: number) { return this.page.getByTestId(cell-${row}-${col}); } }仓库中的真实实现比这个骨架更完整还体现了两条进阶约定构造函数同时接收theme与bundle来自 tests/fixtures/test.ts 声明的TestOptionsgoto()把两者拼进 URL 查询参数grid与addRowButton作为只读字段在构造函数中一次赋值constructor(page: Page, theme main, bundle umd) { this.page page; this.theme theme; this.bundle bundle; this.grid page.getByTestId(grid); this.addRowButton page.getByTestId(add-row); } async goto(): Promisevoid { await this.page.goto(/tests/fixtures/demo/grid.html?theme${this.theme}bundle${this.bundle}); await expect(this.cell(0, 0)).toBeVisible(); }GridPage还提供了selectCell、pressKeys、typeIntoSelected、rowCount、openContextMenu、clickContextMenuItem以及一组剪贴板辅助方法writeClipboardText、writeClipboardHtml、clipboardText、pasteHookCalls全部遵循动作或查询、不做断言的原则。你可以把GridPage当作自己编写页面对象时的最小完整模板。通过>const testIdRenderer function (instance, td, row, col, prop, value, cellProperties) { Handsontable.renderers.TextRenderer.apply(this, arguments); td.setAttribute(data-testid, cell-${row}-${col}); };然后把它配置为整表的渲染器const hot new Handsontable(container, { data: [ [A1, B1, C1], [A2, B2, C2], [A3, B3, C3], [A4, B4, C4], [A5, B5, C5], ], colHeaders: true, rowHeaders: true, contextMenu: true, renderer: testIdRenderer, licenseKey: non-commercial-and-evaluation, });这里的row/col是视觉索引visual indices——即测试在思考时所用的坐标例如第 0 行第 0 列就是左上角单元格A1与数据源内部索引无关。这正是测试只关心用户看到的网格这一原则的体现。同时把data-testid放在容器、工具栏按钮以及测试驱动的任何菜单/对话框上。例如grid.html中div idtoolbar button>rowLocator(): Locator { return this.page.locator(.ht_master .htCore tbody tr); } async rowCount(): Promisenumber { return this.rowLocator().count(); }spec中的adds a row用例利用它做数量断言await expect(grid.rowLocator()).toHaveCount(before 1)。即便如此优先添加 test id 而不是依赖这些选择器。结构性 CSS 依赖 Walkontable 的 DOM 结构一旦内部渲染结构变化就会失效test id 则是对测试契约的显式声明。编辑单元格的完整流程页面对象把双击打开编辑器 → 输入 → 回车提交封装成一个意图方法page-objects.md 中的示例与 GridPage.ts 的实现一致async editCell(row: number, col: number, value: string) { await this.cell(row, col).dblclick(); const editor this.page.locator(.handsontableInput); await expect(editor).toBeVisible(); await editor.fill(value); await editor.press(Enter); }注意其中的等待哲学双击后断言编辑器可见await expect(editor).toBeVisible()再fill而不是双击后 sleep 200ms。fill与press本身也是 Playwright 的自动等待动作整条链路没有任何固定时长。这是 web-first 等待在页面对象内部的具体形态。配套的查询侧方法同样遵循查询不做断言/** Assert a cell shows the expected text (web-first, auto-retrying). */ async expectCell(row: number, col: number, text: string): Promisevoid { await expect(this.cell(row, col)).toHaveText(text); }sanitizer、clipboard、fill-handle等几十个 spec 都建立在点击/键盘动作 精确断言单元格状态这一模式之上——执行动作然后断言确切的单元格/overlay/选区状态而不是断言它渲染出来了。Fixtures 与 demo serverdemo 页面与静态服务器demo 页面位于 tests/fixtures/demo/grid.html是核心参考挂载一个从构建产物handsontable/dist加载的真实网格。注意它加载的是已构建的 Handsontable而不是源码因此运行前需要先构建在handsontable/下执行npm run build产物为dist/handsontable.js与dist/handsontable.full.min.js。静态服务器是 tests/support/static-server.mjs —— 一个零依赖仅 Node 内置模块的静态文件服务器服务仓库根目录其注释明确写着cwd 是tests/时向上解析一级这样页面才能引用到handsontable/dist与handsontable/styles下的资源。它在启动时还会做一次预检如果tests/node_modules/hyperformula缺失formulas fixture 需要会在启动阶段给出可操作的报错而不是在跑测试途中裸 404。服务器由 Playwright 的webServer配置自动拉起见 tests/playwright.config.ts 中的webServer: { command: node support/static-server.mjs, port: PORT, reuseExistingServer: !process.env.CI, ... }PORT由HOT_TEST_PORT环境变量解析默认 8123。licenseKey 必须配置fixture 中始终传入licenseKey: non-commercial-and-evaluation这样许可证横幅/水印永远不会遮挡测试页面。这一点在grid.html中直接可见也是所有 fixture 的统一要求。wrapper 功能测试对于框架 wrapperReact / Angular / Vue的功能测试不驱动静态 demo而是驱动 wrapper 的示例应用examples/next/visual-tests/framework/demo或专门构建的 app因为要测的正是 jsdom 看不到的框架集成 bug。页面对象与 test-id 规则完全一致另加框架特有约定React StrictMode 双重挂载、Angular NgZone、Vue 响应式等详见 wrappers.md 与 SKILL.md。矩阵参数如何贯通theme × bundle 的完整链路页面对象把theme与bundle拼进 fixture URL这一设计背后是一条完整的配置链路理解它有助于你正确编写自己的页面对象声明参数tests/fixtures/test.ts 通过base.extendTestOptions声明theme: [main, { option: true }]与bundle: [umd, { option: true }]并要求所有 spec 从该文件导入test。定义项目矩阵tests/playwright.config.ts 用E2E_THEMES [main, horizon, classic]×E2E_BUNDLESumd→dist/handsontable.jsfull-min→dist/handsontable.full.min.js扁平化出六个项目e2e-main、e2e-main-min、e2e-horizon……与 Puppeteer 旧套件的测试腿一一对应。fixture 白名单映射grid.html中的内联脚本把?theme/?bundle查询参数通过固定白名单映射到字面量{ main: main, horizon: horizon, classic: classic }未知值直接throw——既防注入XSS又保证项目配置里的拼写错误会以红一条腿的形式暴露而不是静默地测试了错误的文件。主题样式表通过document.write写入head以确保首帧前就位bundle 通过拆分闭合标签的document.write注入script src/handsontable/dist/...。因此编写一个新页面对象时构造函数接收(page, theme, bundle)、goto()拼参数、fixture 按白名单映射三者缺一不可。本地运行与调试针对你新写或改动的 spec在tests/目录下只跑默认主题的快速冒烟这是提交前门的标准动作cd tests npx playwright test --projecte2e-main e2e/your-spec.spec.ts完整矩阵六条腿属于 CI本地若想跑全部腿去掉--project过滤即可。运行前检查 8123 端口是否已被占用lsof -nP -i :8123 | grep LISTEN。因为webServer使用了reuseExistingServerPlaywright 会直接挂到任何已监听 8123 的进程上——比如另一个 worktree 的静态服务器——从而静默测试一套并非你构建的handsontable/dist。若被占用且不是你的进程改用独立端口HOT_TEST_PORT8131 npx playwright test --projecte2e-main e2e/your-spec.spec.ts该变量由 tests/playwright.config.ts 读取非法值会直接抛错而不是回落到 8123。跑全腿前请确认两个 bundle 都已重新构建npm --prefix handsontable run build后检查handsontable/dist/handsontable.js与handsontable/dist/handsontable.full.min.js两个文件的时间戳build:umd只写前一个-min腿会停留在旧代码上。与整体测试纪律的关系页面对象与 test-id 约定只是 Handsontable Playwright E2E 体系的一部分与其配套的还有两条铁律值得了解详见 SKILL.md只用 web-first 等待await expect(locator).toBeVisible()禁止sleep/waitForTimeout/networkidle/ 自定义就绪标志每个断言都要await。相关 lint 规则在tests/.eslintrc.cjs中同时约束 spec 与页面对象。GridPage.goto()用首格可见作为就绪条件正是这一纪律的样板。确定性清单页面对象内部的六种隐藏等待形态浏览器内的setTimeout、无{ polling }的waitForFunction、以scrollTop收尾的滚动方法、跨往返读日志取.at(-1)等在 determinism.md 中逐条列出每条都有真实迁移中的度量数据支撑。一句话总结本指南spec 只讲意图选择器与交互收敛进页面对象挂接一律走data-testid由 fixture 渲染器统一打标等待只发生在条件上。照此编写DOM 变动时你只需要改动一个文件而不是改写整组测试。赞分享前端UI组件【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址https://gitcode.com/gh_mirrors/ha/handsontable点击查看免费下载相关推荐Sanity Studio E2E 实践中的 Playwright 页面对象模型POM结构、组合模式与 Fixtures 集成Sanity Studio E2E 实践中的 Playwright 页面对象模型POM结构、组合模式与 Fixtures 集成 本篇以 Sanity 仓库CMS前端Super Productivity E2E 测试实战指南Playwright 测试体系、页面对象与运行方法论Super Productivity E2E 测试实战指南Playwright 测试体系、页面对象与运行方法论 本篇指南聚焦 Super Productivi前端桌面应用移动开发Handsontable Playwright E2E 测试编写实战从 Page Object 到零 flake 的真实浏览器测试Handsontable Playwright E2E 测试编写实战从 Page Object 到零 flake 的真实浏览器测试 Handsontable前端UI组件上一篇AppSmith 快速上手拖拽搭界面、连数据库从零发布你的第一个内部应用下一篇Redux Thunk与文件元数据提取状态与进度创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表