mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
New lint test_no_raw_goto.py forbids raw `page.goto(...)` in role playwright specs + persona flows (the gotoOnion wrapper in personas/utils is exempt) so navigation retries transient Tor-transport errors over onion. Migrates every raw call across ~75 roles (343 sites) to `gotoOnion(page, ...)` and teaches gotoOnion to detect relative onion URLs (`isOnionCanonical`). gotoOnion is a behaviour-identical drop-in on clearnet (single attempt, same return). Verified: no-raw-goto lint green, eslint 0 errors, make test green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
2.9 KiB
JavaScript
79 lines
2.9 KiB
JavaScript
const { test, expect, request } = require("@playwright/test");
|
|
const { resolveTimeout } = require("./timeouts");
|
|
const { gotoOnion } = require("./personas");
|
|
|
|
async function seedTicketViaApi(baseUrl, adminApiUsername, adminApiPassword, subject) {
|
|
const api = await request.newContext({
|
|
ignoreHTTPSErrors: true,
|
|
extraHTTPHeaders: {
|
|
Authorization: `Basic ${Buffer.from(`${adminApiUsername}:${adminApiPassword}`).toString("base64")}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
const resp = await api.post(`${baseUrl}/api/v1/tickets`, {
|
|
data: {
|
|
title: subject,
|
|
group: "Users",
|
|
customer: adminApiUsername,
|
|
article: {
|
|
subject,
|
|
body: "Seed article for the agent-reply Playwright scenario.",
|
|
type: "note",
|
|
internal: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (resp.status() >= 300) {
|
|
throw new Error(`Seed POST /api/v1/tickets failed: ${resp.status()} ${await resp.text()}`);
|
|
}
|
|
const ticket = await resp.json();
|
|
await api.dispose();
|
|
return ticket;
|
|
}
|
|
|
|
exports.register = function (shared) {
|
|
test("administrator (agent): replies to an API-seeded ticket via the SPA", async ({ page }) => {
|
|
expect(shared.env.adminApiUsername, "ADMIN_API_USERNAME must be set").toBeTruthy();
|
|
expect(shared.env.adminApiPassword, "ADMIN_API_PASSWORD must be set").toBeTruthy();
|
|
|
|
const subject = `playwright-agent-reply-${Date.now()}`;
|
|
const ticket = await seedTicketViaApi(
|
|
shared.env.zammadBaseUrl,
|
|
shared.env.adminApiUsername,
|
|
shared.env.adminApiPassword,
|
|
subject
|
|
);
|
|
|
|
await shared.signInAsApiBot(page);
|
|
await gotoOnion(page, `${shared.env.zammadBaseUrl}/#ticket/zoom/${ticket.id}`, { waitUntil: "domcontentloaded" });
|
|
await expect(page.locator("body")).toContainText(subject, { timeout: resolveTimeout(60_000) });
|
|
|
|
// Clue-tooltip close handler races the backdrop animation and re-opens; nuke the DOM nodes.
|
|
await page.evaluate(() => {
|
|
document.querySelectorAll('.js-modal--clue, .modal-backdrop').forEach((el) => el.remove());
|
|
});
|
|
|
|
const replyText = `agent-reply ${Date.now()}`;
|
|
const replyBody = page.locator('.article-add [contenteditable="true"]').first();
|
|
await replyBody.waitFor({ state: "visible", timeout: resolveTimeout(60_000) });
|
|
await replyBody.evaluate((el, text) => {
|
|
el.focus();
|
|
el.innerText = text;
|
|
el.dispatchEvent(new InputEvent("input", { bubbles: true, data: text }));
|
|
}, replyText);
|
|
|
|
const updateButton = page
|
|
.locator('button.js-submit, button[data-name="submit"]')
|
|
.or(page.getByRole("button", { name: /^(update|aktualisieren)$/i }))
|
|
.first();
|
|
await updateButton.waitFor({ state: "visible", timeout: resolveTimeout(60_000) });
|
|
await updateButton.click();
|
|
|
|
await expect(page.locator("body")).toContainText(replyText, { timeout: resolveTimeout(60_000) });
|
|
|
|
await shared.zammadLogout(page);
|
|
});
|
|
};
|