Update GeoIntel project files
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-25 23:43:08 +02:00
parent 9db6cca2b1
commit d5ea270329
155 changed files with 37970 additions and 825 deletions
+524
View File
@@ -0,0 +1,524 @@
import { expect, test, type Page } from "@playwright/test";
const browserErrors = new WeakMap<Page, string[]>();
test.beforeEach(async ({ page }) => {
const errors: string[] = [];
browserErrors.set(page, errors);
page.on("console", (message) => {
if (message.type() === "error") errors.push(message.text());
});
page.on("pageerror", (error) => errors.push(error.message));
});
test.afterEach(async ({ page }) => {
expect(browserErrors.get(page) ?? []).toEqual([]);
});
const openSettings = async (
page: import("@playwright/test").Page,
projectName: string,
) => {
if (["mobile-chromium", "tablet-chromium"].includes(projectName)) {
await page
.getByRole("navigation", { name: "Mobiele navigatie" })
.getByRole("button", { name: "Instellingen" })
.click();
return;
}
await page.getByRole("button", { name: "Instellingen openen" }).click();
};
test("dashboard en Quick Launch vormen de primaire startflow", async ({
page,
context,
}, testInfo) => {
test.skip(
testInfo.project.name !== "desktop-chromium",
"De desktopflow wordt eenmaal uitgevoerd",
);
await page.goto("/");
await expect(page.getByRole("main")).toBeVisible();
await expect(
page.getByRole("navigation", { name: "Dashboardnavigatie" }),
).toBeVisible();
await expect(page.getByLabel(/graden.*Mol/)).toBeVisible();
await expect(
page.getByRole("complementary", {
name: "Favorieten en deckstatus",
}),
).toBeVisible();
await expect(page.getByText("Live inzicht", { exact: true })).toBeVisible();
await page.locator(".dashboard-insights-panel > summary").click();
await expect(
page.getByRole("region", { name: "Servicepanelen" }),
).toBeVisible();
const refreshResponse = page.waitForResponse(
(response) =>
response.url().endsWith("/api/discovery/sync") &&
response.request().method() === "POST",
);
await page
.getByRole("button", { name: "Statussen vernieuwen" })
.first()
.click();
expect((await refreshResponse).status()).toBe(200);
await page.keyboard.press("Control+K");
const search = page.getByLabel("Zoek apps, favorieten of Google");
await expect(search).toBeFocused();
await search.fill("Google");
const favoriteResults = page
.locator(".result-group")
.filter({ has: page.getByRole("heading", { name: "Favorieten" }) });
await expect(
favoriteResults.locator(".app-icon-image, .app-brand-icon"),
).toHaveCount(1);
await search.fill("Plex");
await expect(
page.getByRole("option", { name: /Plex Media Server/ }),
).toBeVisible();
const googlePopupPromise = context.waitForEvent("page");
await page.getByRole("option", { name: /Zoeken op Google naar/ }).click();
const googlePopup = await googlePopupPromise;
expect(googlePopup.url()).toContain("google.com/search");
await googlePopup.close();
});
test("dashboardcategorieën filteren de zichtbare snelkoppelingen", async ({
page,
request,
}, testInfo) => {
test.skip(
testInfo.project.name !== "desktop-chromium",
"De desktopflow wordt eenmaal uitgevoerd",
);
const dashboard = await (await request.get("/api/dashboard")).json();
const [firstApp, secondApp] = dashboard.apps;
const [firstCategory, secondCategory] = dashboard.categories;
expect(firstApp).toBeTruthy();
expect(secondApp).toBeTruthy();
expect(firstCategory).toBeTruthy();
expect(secondCategory).toBeTruthy();
await request.patch(`/api/apps/${firstApp.id}`, {
data: { visible: true, categoryId: firstCategory.id },
});
await request.patch(`/api/apps/${secondApp.id}`, {
data: { visible: true, categoryId: secondCategory.id },
});
await page.goto("/");
const categoryButton = page
.locator(".category-tabs button")
.filter({ hasText: firstCategory.name })
.first();
await categoryButton.click();
await expect(categoryButton).toHaveClass(/active/);
await expect(page.locator(".dashboard-groups .app-section")).toHaveCount(1);
await expect(page.locator(".dashboard-groups .app-section h2")).toContainText(
firstCategory.name,
);
});
test("compact appbeheer opent een toegankelijke detail-drawer", async ({
page,
}, testInfo) => {
test.skip(
testInfo.project.name !== "desktop-chromium",
"De desktopflow wordt eenmaal uitgevoerd",
);
await page.goto("/");
await openSettings(page, testInfo.project.name);
await expect(
page.getByRole("heading", { name: "App Beheer", exact: true }),
).toBeVisible();
await page.getByLabel("Apps zoeken").fill("Plex");
const row = page.getByRole("article").filter({ hasText: "Plex" });
await expect(row).toBeVisible();
const visibilitySwitch = row.getByRole("switch");
const knob = visibilitySwitch.locator(".visibility-toggle-track > span");
const initialVisibility = await visibilitySwitch.getAttribute("aria-checked");
const initialKnob = await knob.boundingBox();
await visibilitySwitch.click();
await expect(visibilitySwitch).toHaveAttribute(
"aria-checked",
initialVisibility === "true" ? "false" : "true",
);
await expect
.poll(async () => {
const movedKnob = await knob.boundingBox();
return Math.abs((movedKnob?.x ?? 0) - (initialKnob?.x ?? 0));
})
.toBeGreaterThan(18);
await visibilitySwitch.click();
await row.getByRole("button", { name: /Bewerken/ }).click();
const drawer = page.getByRole("dialog", { name: /Plex/ });
await expect(drawer).toBeVisible();
await expect(drawer.getByLabel("Weergavenaam")).toHaveValue(/Plex/);
await expect(
drawer.getByText("Automatisch ontdekt", { exact: true }),
).toBeVisible();
await drawer.getByRole("button", { name: "Sluiten" }).click();
await expect(drawer).toBeHidden();
await expect(row.getByRole("button", { name: /Bewerken/ })).toBeFocused();
});
test("weergavevoorkeuren en geavanceerde read-only status blijven werken", async ({
page,
request,
}, testInfo) => {
test.skip(
testInfo.project.name !== "desktop-chromium",
"De desktopflow wordt eenmaal uitgevoerd",
);
await request.patch("/api/preferences", {
data: { reducedMotion: false, pollingIntervalMs: 30000 },
});
await page.goto("/");
await openSettings(page, testInfo.project.name);
const settingsNavigation = page.getByRole("navigation", {
name: "Instellingensecties",
});
await settingsNavigation.getByRole("button", { name: "Weergave" }).click();
await page.getByRole("button", { name: /Haven/ }).click();
await expect(page.locator("html")).toHaveAttribute("data-theme", "harbor");
await page.locator(".appearance-advanced > summary").click();
await page.getByRole("switch", { name: "Verminderde beweging" }).click();
await page.getByLabel("Verversingsinterval").selectOption("60000");
await expect(page.locator("html")).toHaveAttribute(
"data-reduced-motion",
"true",
);
await page.reload();
await expect(page.locator("html")).toHaveAttribute("data-theme", "harbor");
await expect(page.locator("html")).toHaveAttribute(
"data-reduced-motion",
"true",
);
await openSettings(page, testInfo.project.name);
await page.getByLabel("Instellingen zoeken").fill("diagnostiek");
await expect(
page
.getByRole("navigation", { name: "Instellingensecties" })
.getByRole("button", { name: "Algemeen" }),
).toBeVisible();
await expect(
page
.getByRole("navigation", { name: "Instellingensecties" })
.getByRole("button", { name: "Apps" }),
).toHaveCount(0);
await page
.getByRole("navigation", { name: "Instellingensecties" })
.getByRole("button", { name: "Algemeen" })
.click();
await expect(
page.getByRole("region", { name: "Paneelstudio" }),
).toBeVisible();
await page.getByRole("button", { name: "Systeemstatus" }).click();
await page.getByRole("button", { name: "Diagnostiek uitvoeren" }).click();
await expect(page.locator(".diagnostics-result")).toContainText("Database");
});
test("achtergrondpresets en een geüploade wallpaper wijzigen de zichtbare canvas", async ({
page,
request,
}, testInfo) => {
test.skip(
testInfo.project.name !== "desktop-chromium",
"De visuele achtergrondflow wordt eenmaal uitgevoerd",
);
await request.delete("/api/wallpaper");
await request.patch("/api/preferences", {
data: {
backgroundStyle: "aurora",
wallpaperFit: "contain",
wallpaperPositionX: 50,
wallpaperPositionY: 50,
wallpaperZoom: 100,
},
});
await page.goto("/");
const selectBackground = async (
name: "Aurora" | "Blauwdruk" | "Minimaal",
) => {
await openSettings(page, testInfo.project.name);
await page
.getByRole("navigation", { name: "Instellingensecties" })
.getByRole("button", { name: "Weergave" })
.click();
await page.locator(".appearance-advanced > summary").click();
await page.getByRole("button", { name: new RegExp(`^${name}`) }).click();
await expect(page.locator("html")).toHaveAttribute(
"data-background",
{ Aurora: "aurora", Blauwdruk: "grid", Minimaal: "minimal" }[name],
);
await page
.locator(".settings-topbar nav")
.getByRole("button", { name: "Dashboard" })
.click();
return page.locator(".dashboard-canvas").evaluate((element) => ({
image: getComputedStyle(element).backgroundImage,
size: getComputedStyle(element).backgroundSize,
}));
};
const aurora = await selectBackground("Aurora");
const grid = await selectBackground("Blauwdruk");
const minimal = await selectBackground("Minimaal");
expect(new Set([aurora.image, grid.image, minimal.image]).size).toBe(3);
expect(grid.size).toContain("40px");
await openSettings(page, testInfo.project.name);
await page
.getByRole("navigation", { name: "Instellingensecties" })
.getByRole("button", { name: "Weergave" })
.click();
await page.locator(".appearance-advanced > summary").click();
await page
.locator('.wallpaper-settings input[type="file"]')
.setInputFiles("public/dockdeck-icon.png");
await expect(
page.locator(".wallpaper-editor-preview .wallpaper-preview-image"),
).toBeVisible();
await page.getByRole("button", { name: "Vullend" }).click();
await page.getByLabel("Horizontale wallpaperfocus").fill("32");
await page.getByLabel("Verticale wallpaperfocus").fill("68");
await page.getByLabel("Wallpaperzoom").fill("120");
await page.getByRole("button", { name: "Toepassen" }).click();
await page
.locator(".settings-topbar nav")
.getByRole("button", { name: "Dashboard" })
.click();
await expect(page.locator(".dashboard")).toHaveClass(/has-wallpaper/);
const wallpaperStage = page.locator(".dashboard-wallpaper-stage");
const wallpaperImage = wallpaperStage.locator(".dashboard-wallpaper-image");
const wallpaperAmbient = wallpaperStage.locator(
".dashboard-wallpaper-ambient",
);
await expect(wallpaperStage).toBeVisible();
await expect(wallpaperImage).toHaveAttribute("src", /\/api\/wallpaper/);
await expect(wallpaperAmbient).toHaveAttribute("src", /\/api\/wallpaper/);
await expect
.poll(() =>
wallpaperImage.evaluate((element) => ({
fit: getComputedStyle(element).objectFit,
position: getComputedStyle(element).objectPosition,
transform: getComputedStyle(element).transform,
})),
)
.toMatchObject({
fit: "cover",
position: "32% 68%",
});
const stageBeforeScroll = await wallpaperStage.boundingBox();
const canvasBackground = await page
.locator(".dashboard-canvas")
.evaluate((element) => ({
color: getComputedStyle(element).backgroundColor,
image: getComputedStyle(element).backgroundImage,
}));
expect(stageBeforeScroll?.x).toBe(0);
expect(stageBeforeScroll?.width).toBe(testInfo.project.use.viewport!.width);
expect(stageBeforeScroll?.y).toBe(72);
expect(stageBeforeScroll?.height).toBe(
testInfo.project.use.viewport!.height - 72,
);
expect(canvasBackground.image).toBe("none");
expect(canvasBackground.color).toBe("rgba(0, 0, 0, 0)");
await page.evaluate(() => window.scrollTo(0, 900));
await page.waitForTimeout(100);
const stageAfterScroll = await wallpaperStage.boundingBox();
expect(stageBeforeScroll?.height).toBeLessThanOrEqual(
testInfo.project.use.viewport!.height,
);
expect(stageAfterScroll?.y).toBeLessThanOrEqual(stageBeforeScroll!.y);
expect(stageAfterScroll?.y).toBeGreaterThanOrEqual(71);
await request.delete("/api/wallpaper");
await request.patch("/api/preferences", {
data: {
backgroundStyle: "aurora",
wallpaperFit: "contain",
wallpaperPositionX: 50,
wallpaperPositionY: 50,
wallpaperZoom: 100,
},
});
});
test("dashboard en instellingen blijven bruikbaar zonder horizontale overflow", async ({
page,
}, testInfo) => {
await page.goto("/");
const compact = ["mobile-chromium", "tablet-chromium"].includes(
testInfo.project.name,
);
await expect(
page.getByRole("navigation", {
name: compact ? "Mobiele navigatie" : "Dashboardnavigatie",
}),
).toBeVisible();
await expect(
page.getByLabel(
testInfo.project.name === "mobile-chromium"
? "Zoek apps en favorieten mobiel"
: "Zoek apps, favorieten of Google",
),
).toBeVisible();
if (testInfo.project.name === "ultrawide-chromium") {
const widgetDock = page.locator(".dashboard-insights-panel");
await expect(widgetDock).toHaveAttribute("open", "");
const dockBox = await widgetDock.boundingBox();
const groupsBox = await page.locator(".dashboard-groups").boundingBox();
expect(dockBox).not.toBeNull();
expect(groupsBox).not.toBeNull();
expect(dockBox!.x).toBeGreaterThan(groupsBox!.x);
}
await openSettings(page, testInfo.project.name);
await expect(
page.getByRole("heading", { name: "App Beheer", exact: true }),
).toBeVisible();
const overflow = await page.evaluate(
() => document.documentElement.scrollWidth - window.innerWidth,
);
expect(overflow).toBeLessThanOrEqual(1);
});
test("legt de finale Stitch-compositie op de canonieke viewports vast", async ({
page,
request,
}, testInfo) => {
const dashboardResponse = await request.get("/api/dashboard");
const dashboard = (await dashboardResponse.json()) as {
apps: Array<{ id: string; technicalName: string }>;
categories: Array<{ id: string; name: string }>;
favorites: Array<{ id: string }>;
};
let smartHome = dashboard.categories.find(
(category) => category.name === "Smart Home",
);
if (!smartHome) {
const response = await request.post("/api/categories", {
data: { name: "Smart Home", icon: "House", sortOrder: 20 },
});
smartHome = (await response.json()) as { id: string; name: string };
}
const media = dashboard.categories.find(
(category) => category.name === "Media",
);
const tools = dashboard.categories.find(
(category) => category.name === "Tools",
);
for (const app of dashboard.apps) {
const isHome = /home/i.test(app.technicalName);
const isMedia = /plex|immich/i.test(app.technicalName);
await request.patch(`/api/apps/${app.id}`, {
data: {
visible: !/paperless/i.test(app.technicalName),
categoryId: isHome
? smartHome.id
: isMedia
? (media?.id ?? null)
: (tools?.id ?? null),
},
});
}
if (dashboard.favorites.length === 0) {
for (const [index, favorite] of [
["Google", "https://google.com"],
["YouTube", "https://youtube.com"],
["GitHub", "https://github.com"],
].entries()) {
await request.post("/api/favorites", {
data: {
name: favorite[0],
url: favorite[1],
icon: null,
categoryId: null,
sortOrder: index,
},
});
}
}
await request.patch("/api/preferences", { data: { theme: "dark" } });
await page.goto("/");
await expect(page.getByRole("main")).toBeVisible();
const inferredAppIcons = page.locator(".app-tile .app-brand-icon");
expect(await inferredAppIcons.count()).toBeGreaterThan(0);
await expect(page.locator(".app-monogram")).toHaveCount(0);
const screenshotRoot = "docs/design/dockdeck-canon-v1/implementation";
if (testInfo.project.name === "desktop-chromium") {
await page.screenshot({
path: `${screenshotRoot}/dashboard-dark-desktop-1440x900.png`,
});
await page.keyboard.press("Control+K");
await page.getByLabel("Zoek apps, favorieten of Google").fill("Plex");
await page.screenshot({
path: `${screenshotRoot}/quick-launch-dark-desktop-1440x900.png`,
});
await page.keyboard.press("Escape");
await openSettings(page, testInfo.project.name);
await page.screenshot({
path: `${screenshotRoot}/settings-apps-dark-desktop-1440x900.png`,
});
await page
.getByRole("button", { name: /Bewerken/ })
.first()
.click();
await page.screenshot({
path: `${screenshotRoot}/app-drawer-dark-desktop-1440x900.png`,
});
await page
.getByRole("dialog")
.getByRole("button", { name: "Sluiten", exact: true })
.click();
await request.patch("/api/preferences", { data: { theme: "light" } });
await page.goto("/");
await page.screenshot({
path: `${screenshotRoot}/dashboard-light-desktop-1440x900.png`,
});
await page.keyboard.press("Control+K");
await page.getByLabel("Zoek apps, favorieten of Google").fill("Plex");
await page.screenshot({
path: `${screenshotRoot}/quick-launch-light-desktop-1440x900.png`,
});
await page.keyboard.press("Escape");
await openSettings(page, testInfo.project.name);
await page.screenshot({
path: `${screenshotRoot}/settings-apps-light-desktop-1440x900.png`,
});
} else if (testInfo.project.name === "mobile-chromium") {
await page.screenshot({
path: `${screenshotRoot}/dashboard-dark-mobile-390x844.png`,
});
await openSettings(page, testInfo.project.name);
await page
.getByRole("button", { name: /Bewerken/ })
.first()
.click();
await page.screenshot({
path: `${screenshotRoot}/app-drawer-mobile-390x844.png`,
});
} else if (testInfo.project.name === "tablet-chromium") {
await page.screenshot({
path: `${screenshotRoot}/dashboard-dark-tablet-768x1024.png`,
});
} else {
await page.screenshot({
path: `${screenshotRoot}/dashboard-dark-ultrawide-3440x1440.png`,
});
}
});