feat: add MCP server with resources, tools, auth, and Traefik routing
- Add backend/app/mcp_server.py with FastMCP server (hms-cms) - Resources: design-rules, page-structure, sync-status - Tools: trigger_sync, get_sync_log, set_rentman_token, read_file, write_file, create_page, deploy, git_commit, git_status - Bearer token auth via MCP_AUTH_TOKEN env var - Mount MCP at /mcp with streamable HTTP transport - Integrate session manager in FastAPI lifespan - Add Traefik labels for external /mcp route on hms.media-on.de - Add git, docker.io, curl to backend Dockerfile - Mount repo root + docker socket in backend container - Add mcp>=1.0.0 to requirements.txt
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Mietkatalog Page", () => {
|
||||
test("should render SSR HTML with Mietkatalog heading", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
await expect(page.locator("h1")).toContainText("Mietkatalog");
|
||||
});
|
||||
|
||||
test("should have search input visible", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const searchInput = page.getByTestId("equipment-search");
|
||||
await expect(searchInput).toBeVisible();
|
||||
});
|
||||
|
||||
test("should have sort dropdown with name options", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const sortSelect = page.getByTestId("sort-select");
|
||||
await expect(sortSelect).toBeVisible();
|
||||
await expect(sortSelect.locator("option")).toHaveCount(2);
|
||||
});
|
||||
|
||||
test("should have category filter chips", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const chips = page.getByTestId("category-chips");
|
||||
await expect(chips).toBeVisible();
|
||||
});
|
||||
|
||||
test("should have reset filters button", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const resetBtn = page.getByTestId("reset-filters");
|
||||
await expect(resetBtn).toBeVisible();
|
||||
});
|
||||
|
||||
test("should filter equipment by search input", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const searchInput = page.getByTestId("equipment-search");
|
||||
await searchInput.fill("Lautsprecher");
|
||||
await page.waitForTimeout(500);
|
||||
const grid = page.getByTestId("equipment-grid");
|
||||
await expect(grid).toBeVisible();
|
||||
});
|
||||
|
||||
test("should reset all filters when reset button clicked", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const searchInput = page.getByTestId("equipment-search");
|
||||
await searchInput.fill("Test");
|
||||
const resetBtn = page.getByTestId("reset-filters");
|
||||
await resetBtn.click();
|
||||
await expect(searchInput).toHaveValue("");
|
||||
});
|
||||
|
||||
test("should show result count", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const resultCount = page.getByTestId("result-count");
|
||||
await expect(resultCount).toBeVisible();
|
||||
});
|
||||
|
||||
test("should display equipment cards or empty state", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const grid = page.getByTestId("equipment-grid");
|
||||
const emptyState = page.getByText("Keine Artikel gefunden");
|
||||
const isVisible = await grid.isVisible().catch(() => false) || await emptyState.isVisible().catch(() => false);
|
||||
expect(isVisible).toBeTruthy();
|
||||
});
|
||||
|
||||
test("should navigate to detail page on card click", async ({ page }) => {
|
||||
await page.goto("/mietkatalog");
|
||||
const card = page.getByTestId("equipment-card").first();
|
||||
if (await card.isVisible()) {
|
||||
const link = card.locator("a[href*='/mietkatalog/']").first();
|
||||
await link.click();
|
||||
await page.waitForURL(/\/mietkatalog\/\d+/);
|
||||
await expect(page).toHaveURL(/\/mietkatalog\/\d+/);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user