Files
artcraft/frontend/tools/testing/desktop-session.mjs
T

119 lines
6.4 KiB
JavaScript

// Real app UI with isolated, deterministic IPC responses. Run against a dev or
// preview URL. Native HTTP/CORS correctness is tested separately in Rust and in
// the real-webview procedure in docs/desktop-session-regression.md.
import assert from "node:assert/strict";
import { chromium, expect } from "@playwright/test";
import { installBrowserFixture } from "../performance/browser-fixture.mjs";
const url = new URL(process.argv[2] || "http://127.0.0.1:5193");
if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)) {
throw new Error("Use the local development or preview URL printed by the launcher.");
}
const browser = await chromium.launch({ channel: "chrome", headless: true });
try {
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
await context.addInitScript(installBrowserFixture);
await context.addInitScript(installSessionFixture);
await context.route("**/*", (route) => {
const target = route.request().url();
return target.startsWith(url.origin) || target.startsWith("blob:")
? route.continue() : route.abort();
});
const page = await context.newPage();
const errors = [];
page.on("pageerror", (error) => errors.push(error.message));
const home = async () => {
await page.goto(url.href);
await expect(page.getByRole("heading", { name: "What will you craft today?" })).toBeVisible({ timeout: 60000 });
await expect(page.getByRole("button", { name: "42 Credits" })).toBeVisible();
};
const account = async () => {
await page.getByRole("button", { name: "Settings", exact: true }).click();
await page.getByRole("button", { name: "Accounts", exact: true }).click();
await expect(page.getByText("Session Smoke Account", { exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Log Out", exact: true })).toBeVisible();
};
await home();
await account();
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "My Library" }).click();
await expect(page.getByRole("img", { name: "Session smoke image", exact: true })).toBeVisible();
await expect.poll(() => page.getByRole("img", { name: "Session smoke image", exact: true })
.evaluate((image) => image.complete && image.naturalWidth > 0)).toBe(true);
await expect(page.getByText("Log in to browse and use your library.")).toHaveCount(0);
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "Create Image Generate AI images" }).click();
await expect(page.getByRole("img", { name: "Session smoke image", exact: true })).toBeVisible();
await account();
await page.keyboard.press("Escape");
for (const [card, heading] of [
["Create Video Create video from images", "Create Video"],
["Create Audio Generate music and sound effects", "Create Audio"],
]) {
await home();
await page.getByRole("button", { name: card }).click();
await expect(page.getByRole("heading", { name: heading, exact: true }).first()).toBeVisible();
}
await account();
await page.getByRole("button", { name: "Log Out", exact: true }).click();
await expect(page.getByRole("heading", { name: "Create your account" })).toBeVisible();
await page.getByRole("button", { name: "Log in", exact: true }).click();
await page.getByPlaceholder("you@example.com or username").fill("session_smoke");
await page.getByPlaceholder("Min. 8 characters").fill("fixture-password-only");
await page.getByRole("button", { name: "Log in", exact: true }).click();
await expect(page.getByRole("heading", { name: "Welcome back" })).toHaveCount(0, { timeout: 10000 });
await account();
assert.deepEqual(errors, [], "Application runtime errors");
console.log("PASS: credits, Accounts, Library media, deferred image/video/audio pages, logout gate, and sign-in agree.");
} finally {
await browser.close();
}
function installSessionFixture() {
const invoke = window.__TAURI_INTERNALS__.invoke;
let loggedIn = true;
let nextResource = 0;
const responses = new Map();
const user = {
username: "session_smoke", display_name: "Session Smoke Account",
user_token: "u_session_smoke", can_access_studio: true, maybe_feature_flags: [],
};
const image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl6mTQAAAAASUVORK5CYII=";
const media = {
token: "m_session_smoke", media_class: "image", media_type: "png",
maybe_title: "Session smoke image", created_at: new Date().toISOString(),
origin_category: "inference", origin: { origin_category: "inference" },
media_links: { cdn_url: image, maybe_thumbnail_template: image, thumbnail_template: image },
stats: { positive_rating_count: 0, bookmark_count: 0 },
};
window.__TAURI_INTERNALS__.invoke = async (command, args) => {
if (command === "storyteller_get_login_session_command") return loggedIn ? user : null;
if (command === "storyteller_password_login_command") { loggedIn = true; return user; }
if (command === "storyteller_purge_credentials_command") { loggedIn = false; return; }
if (command === "get_app_info_command") return { payload: { storyteller_host: "https://api.storyteller.ai" } };
if (command === "storyteller_get_credits_command") return { payload: { free_credits: 0, monthly_credits: 42, banked_credits: 0, sum_total_credits: 42 } };
if (command === "plugin:http|fetch") {
const request = new URL(args.clientConfig.url);
let body = { success: true, results: [], folders: [], tags: [], active_subscriptions: [] };
if (request.pathname === "/v1/session") body = { success: true, logged_in: loggedIn, user: loggedIn ? user : undefined };
if (request.pathname === "/v1/logout") loggedIn = false;
if (request.pathname === "/v1/media_files/list/user/session_smoke") body = { success: true, results: [media], pagination: { has_next: false } };
responses.set(++nextResource, { url: request.href, body });
return nextResource;
}
if (command === "plugin:http|fetch_send") {
const response = responses.get(args.rid);
return { rid: args.rid, status: 200, statusText: "OK", url: response.url, headers: [["content-type", "application/json"]] };
}
if (command === "plugin:http|fetch_read_body") {
const response = responses.get(args.rid);
args.streamChannel.onmessage([...new TextEncoder().encode(JSON.stringify(response.body)), 0]);
args.streamChannel.onmessage([1]);
responses.delete(args.rid);
return;
}
return invoke(command, args);
};
}