Files
computer-playbook/roles/web-app-wordpress/files/playwright/addons/wp-discourse.spec.js
kevinveenbirkenbachandClaude Opus 4.8 4c91df5de7 test(e2e): forbid raw page.goto, migrate all specs to Tor-resilient gotoOnion
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>
2026-07-08 20:59:38 +02:00

93 lines
3.7 KiB
JavaScript

const { test, expect } = require("@playwright/test");
const { resolveTimeout } = require("../timeouts");
const { skipUnlessAddonEnabled } = require("../addon-gating");
const { skipUnlessServiceEnabled } = require("../service-gating");
const { gotoOnion } = require("../personas");
const shared = require("../_shared");
test("addon wp-discourse: WordPress bridge is provisioned for and reaches the partner Discourse", async ({ browser }) => {
skipUnlessAddonEnabled("wp-discourse");
skipUnlessServiceEnabled("discourse");
test.setTimeout(resolveTimeout(120_000));
const wpHost = new URL(shared.env.wpBaseUrl).host;
const discourseHost = new URL(shared.env.discourseBaseUrl).host;
expect(
discourseHost,
"the partner Discourse host must be distinct from the WordPress host"
).not.toBe(wpHost);
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
await shared.wpAdminLoginViaOidc(
page,
shared.env.wpBaseUrl,
shared.env.adminUsername,
shared.env.adminPassword
);
await gotoOnion(page,
`${shared.env.wpBaseUrl}/wp-admin/admin.php?page=wp_discourse_options&tab=connection`,
{ waitUntil: "domcontentloaded", timeout: resolveTimeout(60_000) }
);
const settingsSurface = page
.locator(
"form#wp-discourse-form, .wp-discourse-options, #wpbody-content .wrap, .nav-tab-wrapper"
)
.filter({ hasText: /discourse/i })
.first();
await expect(
settingsSurface,
"Expected the WP Discourse settings surface in wp-admin"
).toBeVisible({ timeout: resolveTimeout(30_000) });
const urlField = page
.locator(
'input[name="discourse_connect[url]"], input[id*="discourse_connect"][id*="url"], input[name*="discourse_connect"][name*="url"]'
)
.first();
await expect(
urlField,
"the WP Discourse connection tab must render the provisioned Discourse base-URL field — its absence means the bridge config never landed"
).toBeVisible({ timeout: resolveTimeout(30_000) });
const configuredUrl = (await urlField.inputValue().catch(() => "")) || "";
expect(
/^https?:\/\//i.test(configuredUrl),
"WordPress must have the partner Discourse base URL provisioned in the wp-discourse plugin (proves the bridge config landed)"
).toBe(true);
const configuredHost = new URL(configuredUrl).host;
expect(
configuredHost,
"the provisioned Discourse connect URL must point at the partner Discourse host, not the WordPress host"
).toBe(discourseHost);
expect(configuredHost).not.toBe(wpHost);
const sessionResp = await shared.discourseApiRequest(
context.request,
"/session/current.json"
);
expect(
sessionResp.ok(),
`WordPress's provisioned Discourse API credentials must authenticate against the PARTNER Discourse host ${discourseHost} (GET /session/current.json). A non-OK response means the bridge cannot reach/authenticate to the partner — the coupling failed.`
).toBe(true);
expect(
new URL(sessionResp.url()).host,
"the authenticated Discourse session round-trip must be served by the partner instance, not WordPress"
).toBe(discourseHost);
const sessionBody = await sessionResp.json();
const sessionUsername = sessionBody?.current_user?.username || "";
expect(
sessionUsername.toLowerCase(),
"the partner Discourse must resolve the bridge's provisioned publishing user (publish-username 'system'), proving the WP->Discourse API key is the real coupling"
).toBe((shared.env.discourseApiUsername || "system").toLowerCase());
} finally {
await page.close().catch(() => {});
await context.close().catch(() => {});
}
});