diff --git a/packages/web/.gitignore b/packages/web/.gitignore
new file mode 100644
index 0000000..aaa9103
--- /dev/null
+++ b/packages/web/.gitignore
@@ -0,0 +1,2 @@
+test-results/
+playwright-report/
diff --git a/packages/web/e2e/sidebar.spec.ts b/packages/web/e2e/sidebar.spec.ts
new file mode 100644
index 0000000..b0eb6e0
--- /dev/null
+++ b/packages/web/e2e/sidebar.spec.ts
@@ -0,0 +1,29 @@
+import { expect, test } from "@playwright/test";
+
+const CONFIG_KEY = "openconcho:config";
+const CONFIG_VALUE = JSON.stringify({ baseUrl: "http://localhost:9999", token: "" });
+
+test.describe("Sidebar", () => {
+ test.beforeEach(async ({ context }) => {
+ await context.addInitScript(
+ ([key, value]) => {
+ window.localStorage.setItem(key, value);
+ },
+ [CONFIG_KEY, CONFIG_VALUE],
+ );
+ });
+
+ test("renders the sidebar nav on the dashboard route", async ({ page }) => {
+ await page.goto("/");
+ await expect(page.getByRole("complementary")).toBeVisible();
+ await expect(page.getByRole("link", { name: /dashboard/i })).toBeVisible();
+ await expect(page.getByRole("link", { name: /workspaces/i })).toBeVisible();
+ await expect(page.getByRole("link", { name: /settings/i })).toBeVisible();
+ });
+
+ test("renders the sidebar nav on the settings route", async ({ page }) => {
+ await page.goto("/settings");
+ await expect(page.getByRole("complementary")).toBeVisible();
+ await expect(page.getByRole("link", { name: /dashboard/i })).toBeVisible();
+ });
+});
diff --git a/packages/web/package.json b/packages/web/package.json
index a56bdea..00056b4 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -10,12 +10,10 @@
"lint": "biome check src/",
"lint:fix": "biome check --write src/",
"test": "vitest run --passWithNoTests",
+ "test:e2e": "playwright test",
"generate:api": "openapi-typescript openapi.json -o src/api/schema.d.ts"
},
"dependencies": {
- "@tauri-apps/api": "^2",
- "@tauri-apps/plugin-http": "^2",
- "@tauri-apps/plugin-shell": "^2",
"@fontsource/dm-mono": "^5.2.7",
"@fontsource/dm-sans": "^5.2.8",
"@radix-ui/react-collapsible": "^1.1.12",
@@ -27,6 +25,9 @@
"@tailwindcss/vite": "^4.2.4",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-router": "^1.120.3",
+ "@tauri-apps/api": "^2",
+ "@tauri-apps/plugin-http": "^2",
+ "@tauri-apps/plugin-shell": "^2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.38.0",
@@ -42,6 +43,7 @@
"zod": "catalog:"
},
"devDependencies": {
+ "@playwright/test": "^1.59.1",
"@tanstack/router-plugin": "^1.120.3",
"@testing-library/jest-dom": "catalog:",
"@testing-library/react": "catalog:",
diff --git a/packages/web/playwright.config.ts b/packages/web/playwright.config.ts
new file mode 100644
index 0000000..350ca99
--- /dev/null
+++ b/packages/web/playwright.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig, devices } from "@playwright/test";
+
+export default defineConfig({
+ testDir: "./e2e",
+ fullyParallel: true,
+ reporter: "list",
+ use: {
+ baseURL: "http://localhost:5173",
+ },
+ projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
+ webServer: {
+ command: "pnpm dev",
+ url: "http://localhost:5173",
+ reuseExistingServer: !process.env.CI,
+ timeout: 60_000,
+ },
+});
diff --git a/packages/web/src/routes/__root.tsx b/packages/web/src/routes/__root.tsx
index 07b902f..8dce51e 100644
--- a/packages/web/src/routes/__root.tsx
+++ b/packages/web/src/routes/__root.tsx
@@ -1,4 +1,4 @@
-import { createRootRoute, Outlet, redirect, useRouter } from "@tanstack/react-router";
+import { createRootRoute, Outlet, redirect } from "@tanstack/react-router";
import { useEffect } from "react";
import { Sidebar } from "@/components/layout/Sidebar";
import { loadConfig } from "@/lib/config";
@@ -7,17 +7,10 @@ import { applyTheme, getStoredTheme } from "@/lib/theme";
const SETTINGS_PATH = "/settings";
function RootLayout() {
- const router = useRouter();
- const isSettings = router.state.location.pathname === SETTINGS_PATH;
-
useEffect(() => {
applyTheme(getStoredTheme());
}, []);
- if (isSettings) {
- return