// Every user action, performed the way a person would with JavaScript on // (double-click, checkbox clicks) or off (the contract's submit controls // and edit link). Each one returns once the server has answered. import { expect, type Page, type Locator, type TestInfo } from '@playwright/test'; export const jsOn = (ti: TestInfo) => ti.project.name === 'js-on'; export const todos = (page: Page) => page.locator('.todo-list li'); // Runs fn and waits for what it causes: a page load with JS off, the POST // response with JS on. async function settle(page: Page, ti: TestInfo, fn: () => Promise) { if (jsOn(ti)) { await Promise.all([page.waitForResponse((r) => r.request().method() === 'POST'), fn()]); } else { await Promise.all([page.waitForEvent('load'), fn()]); } } export async function addTodo(page: Page, ti: TestInfo, title: string) { const input = page.locator('.new-todo'); await input.fill(title); await settle(page, ti, () => input.press('Enter')); } export async function addDefaultTodos(page: Page, ti: TestInfo, titles: string[]) { for (const t of titles) await addTodo(page, ti, t); await expect(todos(page)).toHaveCount(titles.length); } export async function toggle(page: Page, ti: TestInfo, item: Locator) { const control = jsOn(ti) ? item.locator('.toggle') : item.locator('[data-action="toggle"]'); await settle(page, ti, () => control.click()); } export async function toggleAll(page: Page, ti: TestInfo) { const control = jsOn(ti) ? page.locator('label[for="toggle-all"]') : page.locator('[data-action="toggle-all"]'); await settle(page, ti, () => control.click()); } export async function destroy(page: Page, ti: TestInfo, item: Locator) { await item.hover(); await settle(page, ti, () => item.locator('.destroy').click()); } export async function clearCompleted(page: Page, ti: TestInfo) { await settle(page, ti, () => page.locator('.clear-completed').click()); } // Opens the item's editor: double-click with JS, the edit link without. // Returns the edit input. export async function startEditing(page: Page, ti: TestInfo, index: number): Promise { const item = todos(page).nth(index); if (jsOn(ti)) { await item.locator('label').dblclick(); } else { await Promise.all([page.waitForEvent('load'), item.locator('a[data-action="edit"]').click()]); } const input = todos(page).nth(index).locator('.edit'); await expect(input).toBeVisible(); return input; } export async function submitEdit(page: Page, ti: TestInfo, input: Locator, title: string) { await input.fill(title); await settle(page, ti, () => input.press('Enter')); } export async function gotoFilter(page: Page, name: 'All' | 'Active' | 'Completed') { const path = { All: '/', Active: '/active', Completed: '/completed' }[name]; await page.locator('.filters a', { hasText: name }).click(); await expect(page).toHaveURL((u) => u.pathname === path); }