Files
computer-playbook/roles/web-app-akaunting/files/playwright/test-seaweedfs.js
kevinveenbirkenbachandClaude Opus 4.8 6f9142027d fix(e2e): skip seaweedfs filer check on onion targets
When an app rides onion, Playwright routes through the node Tor SOCKS
proxy, which cannot resolve the clearnet-internal SeaweedFS filer UI
domain (net::ERR_SOCKS_CONNECTION_FAILED). Skip the filer storage check
on onion targets in all consumer specs — same pattern already proven in
the nextcloud spec. Clearnet runs are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:34:59 +02:00

105 lines
4.9 KiB
JavaScript

const { test, expect } = require("@playwright/test");
const { resolveTimeout, isOnionTarget } = require("./timeouts");
const { skipUnlessServiceEnabled } = require("./service-gating");
const {
runSeaweedfsStorageCheck,
performKeycloakLoginForm,
decodeDotenvQuotedValue,
normalizeBaseUrl,
} = require("./personas");
const PNG_1x1 = Buffer.from(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"base64",
);
const baseUrl = normalizeBaseUrl(process.env.AKAUNTING_BASE_URL || "");
const canonicalDomain = decodeDotenvQuotedValue(process.env.CANONICAL_DOMAIN || "");
const adminUsername = decodeDotenvQuotedValue(process.env.ADMIN_USERNAME);
const adminPassword = decodeDotenvQuotedValue(process.env.ADMIN_PASSWORD);
const akauntingAdminEmail = decodeDotenvQuotedValue(process.env.AKAUNTING_ADMIN_EMAIL);
const akauntingAdminPassword = decodeDotenvQuotedValue(process.env.AKAUNTING_ADMIN_PASSWORD);
test.use({ ignoreHTTPSErrors: true });
test("seaweedfs: an uploaded Akaunting company logo is stored in the SeaweedFS bucket", async ({ page, browser }) => {
test.skip(isOnionTarget(), "SeaweedFS filer UI is not a Tor surface on an onion node (headless backend)");
skipUnlessServiceEnabled("seaweedfs");
test.setTimeout(resolveTimeout(180_000));
expect(baseUrl, "AKAUNTING_BASE_URL must be set").toBeTruthy();
expect(canonicalDomain, "CANONICAL_DOMAIN must be set").toBeTruthy();
expect(adminUsername).toBeTruthy();
expect(adminPassword).toBeTruthy();
expect(akauntingAdminEmail, "AKAUNTING_ADMIN_EMAIL must be set").toBeTruthy();
expect(akauntingAdminPassword, "AKAUNTING_ADMIN_PASSWORD must be set").toBeTruthy();
const expectedBase = baseUrl.replace(/\/$/, "");
await runSeaweedfsStorageCheck(page, browser, {
label: "an Akaunting company logo upload",
action: async (appPage) => {
await appPage.context().clearCookies();
await appPage.goto(`${expectedBase}/`, { waitUntil: "domcontentloaded" });
if (appPage.url().includes("openid-connect/auth")) {
await performKeycloakLoginForm(appPage, adminUsername, adminPassword);
await expect.poll(() => appPage.url(), { timeout: resolveTimeout(90_000) }).toContain(expectedBase);
}
await appPage.goto(`${expectedBase}/auth/login`, { waitUntil: "domcontentloaded" });
await appPage.waitForSelector('#app input[name="email"]', { state: "visible", timeout: resolveTimeout(60_000) });
await appPage
.waitForFunction(
() => {
const el = document.querySelector("#app");
const f = document.querySelector("#app form, form#auth");
return Boolean((f && f.__vue__) || window.Vue || (el && el.__vue__));
},
{ timeout: resolveTimeout(30_000) },
)
.catch(() => {});
await appPage.locator('#app input[name="email"]').first().fill(akauntingAdminEmail);
await appPage.locator('#app input[name="password"]').first().fill(akauntingAdminPassword);
const loginResp = appPage.waitForResponse(
(r) => /\/auth\/login$/.test(r.url()) && r.request().method() === "POST",
{ timeout: resolveTimeout(60_000) },
);
await appPage
.getByRole("button", { name: /login|sign in|enter/i })
.or(appPage.locator('#app button[type="submit"]'))
.first()
.click({ timeout: resolveTimeout(30_000) });
await loginResp.catch(() => {});
await expect
.poll(() => appPage.url(), {
timeout: resolveTimeout(90_000),
message: "expected Akaunting native login to authenticate and leave /auth/login",
})
.not.toContain("/auth/login");
await appPage.waitForLoadState("networkidle", { timeout: resolveTimeout(30_000) }).catch(() => {});
await appPage.goto(`${expectedBase}/1/settings/company`, { waitUntil: "domcontentloaded" });
await expect
.poll(() => appPage.url(), { timeout: resolveTimeout(90_000), message: "expected the Akaunting company settings page" })
.toContain("settings/company");
const marker = `infinito-storage-check-${Date.now()}.png`;
const fileInput = appPage.locator('input[type="file"]').first();
await fileInput.waitFor({ state: "attached", timeout: resolveTimeout(60_000) });
await fileInput.setInputFiles({ name: marker, mimeType: "image/png", buffer: PNG_1x1 });
const saveAction = appPage
.getByRole("button", { name: /^(save|update)$/i })
.or(appPage.locator("button[type='submit'], #index-more-actions button.button-submit"))
.first();
await expect(
saveAction,
"the Akaunting company settings form must expose a Save action to persist the attached logo",
).toBeVisible({ timeout: resolveTimeout(60_000) });
await saveAction.click({ timeout: resolveTimeout(30_000) });
await appPage.waitForLoadState("networkidle", { timeout: resolveTimeout(90_000) }).catch(() => {});
},
});
});