New apps Added
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Shared helpers for Playwright E2E tests.
|
||||
*
|
||||
* Usage:
|
||||
* import { setup, ws, panelSR, cleanup } from "./e2e-helpers.mjs";
|
||||
* const { browser, page } = await setup({ mobile: false });
|
||||
*/
|
||||
import { chromium } from "playwright";
|
||||
|
||||
const HA = "http://homeassistant-dev:8123";
|
||||
|
||||
export async function getRefreshToken() {
|
||||
const http = await import("http");
|
||||
function post(path, body, contentType = "application/json") {
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = typeof body === "string" ? body : JSON.stringify(body);
|
||||
const opts = {
|
||||
hostname: "localhost", port: 8125, path, method: "POST",
|
||||
headers: { "Content-Type": contentType, "Content-Length": Buffer.byteLength(data) },
|
||||
};
|
||||
const req = http.request(opts, (res) => {
|
||||
let d = "";
|
||||
res.on("data", (c) => (d += c));
|
||||
res.on("end", () => resolve(JSON.parse(d)));
|
||||
});
|
||||
req.on("error", reject);
|
||||
req.write(data);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
const flow = await post("/auth/login_flow", { client_id: `${HA}/`, handler: ["homeassistant", null], redirect_uri: `${HA}/` });
|
||||
const auth = await post(`/auth/login_flow/${flow.flow_id}`, { client_id: `${HA}/`, username: "dev", password: "dev" });
|
||||
const tokens = await post("/auth/token", `grant_type=authorization_code&code=${auth.result}&client_id=${HA}/`, "application/x-www-form-urlencoded");
|
||||
return tokens.refresh_token;
|
||||
}
|
||||
|
||||
export async function setup({ mobile = false, timezone = undefined } = {}) {
|
||||
const refreshToken = process.env.HA_REFRESH_TOKEN || await getRefreshToken();
|
||||
const browser = await chromium.connect("ws://localhost:3000");
|
||||
const ctxOpts = {
|
||||
viewport: mobile ? { width: 375, height: 812 } : { width: 1280, height: 900 },
|
||||
locale: "en-US",
|
||||
colorScheme: "dark",
|
||||
...(mobile ? { isMobile: true } : {}),
|
||||
...(timezone ? { timezoneId: timezone } : {}),
|
||||
};
|
||||
const ctx = await browser.newContext(ctxOpts);
|
||||
const page = await ctx.newPage();
|
||||
|
||||
await page.goto(HA);
|
||||
await page.waitForTimeout(1000);
|
||||
await page.evaluate((args) => {
|
||||
localStorage.setItem("hassTokens", JSON.stringify({
|
||||
hassUrl: args.ha, clientId: args.ha + "/",
|
||||
refresh_token: args.r, access_token: "",
|
||||
token_type: "Bearer", expires_in: 1800, expires: 0,
|
||||
}));
|
||||
}, { ha: HA, r: refreshToken });
|
||||
await page.goto(HA + "/maintenance-supporter");
|
||||
await page.waitForTimeout(7000);
|
||||
|
||||
return { browser, ctx, page };
|
||||
}
|
||||
|
||||
export async function ws(page, cmd) {
|
||||
return page.evaluate(async (c) => {
|
||||
const ha = document.querySelector("home-assistant");
|
||||
if (!ha?.hass?.connection) throw new Error("no hass connection");
|
||||
try {
|
||||
return await ha.hass.connection.sendMessagePromise(c);
|
||||
} catch (e) {
|
||||
// Surface backend errors as readable JSON
|
||||
throw new Error("WS error: " + JSON.stringify(e));
|
||||
}
|
||||
}, cmd);
|
||||
}
|
||||
|
||||
export function panelSR(page) {
|
||||
return page.evaluate(() => {
|
||||
const ha = document.querySelector("home-assistant");
|
||||
const main = ha?.shadowRoot?.querySelector("home-assistant-main");
|
||||
const drawer = main?.shadowRoot?.querySelector("ha-drawer");
|
||||
const resolver = drawer?.querySelector("partial-panel-resolver");
|
||||
const custom = resolver?.querySelector("ha-panel-custom");
|
||||
const panel = custom?.querySelector("maintenance-supporter-panel");
|
||||
return !!panel?.shadowRoot;
|
||||
});
|
||||
}
|
||||
|
||||
const PANEL_EVAL = "window._ha=document.querySelector('home-assistant');window._main=_ha.shadowRoot.querySelector('home-assistant-main');window._drawer=_main.shadowRoot.querySelector('ha-drawer');window._resolver=_drawer.querySelector('partial-panel-resolver');window._custom=_resolver.querySelector('ha-panel-custom');window._panel=_custom.querySelector('maintenance-supporter-panel');window._sr=_panel.shadowRoot;";
|
||||
|
||||
export async function openEditDialog(page, entryId, task) {
|
||||
await page.evaluate(({ eid, task, fn }) => {
|
||||
eval(fn);
|
||||
window._sr.querySelector("maintenance-task-dialog").openEdit(eid, task);
|
||||
}, { eid: entryId, task, fn: PANEL_EVAL });
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
|
||||
export async function readDialogSelects(page) {
|
||||
return page.evaluate(({ fn }) => {
|
||||
eval(fn);
|
||||
const dlgSr = window._sr?.querySelector("maintenance-task-dialog")?.shadowRoot;
|
||||
if (!dlgSr) return [];
|
||||
return [...dlgSr.querySelectorAll("select")].map((sel) => {
|
||||
const label = sel.closest(".select-row")?.querySelector("label")?.textContent?.trim() || "unknown";
|
||||
const selected = [...sel.options].find((o) => o.selected);
|
||||
return { label, value: sel.value, selectedValue: selected?.value, match: sel.value === selected?.value };
|
||||
});
|
||||
}, { fn: PANEL_EVAL });
|
||||
}
|
||||
|
||||
export async function readDialogFields(page) {
|
||||
return page.evaluate(({ fn }) => {
|
||||
eval(fn);
|
||||
const dlgSr = window._sr?.querySelector("maintenance-task-dialog")?.shadowRoot;
|
||||
if (!dlgSr) return [];
|
||||
return [...dlgSr.querySelectorAll("ha-textfield")].map((f) => ({
|
||||
label: f.getAttribute("label"), type: f.getAttribute("type"), value: f.value,
|
||||
}));
|
||||
}, { fn: PANEL_EVAL });
|
||||
}
|
||||
|
||||
export async function cleanup(browser, ctx) {
|
||||
try { await ctx.close(); } catch { /* ignore */ }
|
||||
try { await browser.close(); } catch { /* ignore */ }
|
||||
}
|
||||
Reference in New Issue
Block a user