feat(T07): Warenkorb & Mietanfrage frontend – cart store, drawer, pages, form, tests
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Cart Flow", () => {
|
||||
test("should render warenkorb page with heading", async ({ page }) => {
|
||||
await page.goto("/warenkorb");
|
||||
await expect(page.locator("h1")).toContainText("Warenkorb");
|
||||
});
|
||||
|
||||
test("should show empty state when cart is empty", async ({ page }) => {
|
||||
await page.goto("/warenkorb");
|
||||
await expect(page.getByTestId("warenkorb-empty")).toBeVisible();
|
||||
await expect(page.getByTestId("warenkorb-empty-cta")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should have link to mietkatalog from empty state", async ({ page }) => {
|
||||
await page.goto("/warenkorb");
|
||||
const cta = page.getByTestId("warenkorb-empty-cta");
|
||||
await expect(cta).toHaveAttribute("href", "/mietkatalog");
|
||||
});
|
||||
|
||||
test("should show header cart icon", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.getByTestId("header-cart-button")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should open cart drawer when header cart icon clicked", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("header-cart-button").click();
|
||||
await expect(page.getByTestId("cart-drawer")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should show empty state in cart drawer", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("header-cart-button").click();
|
||||
await expect(page.getByTestId("cart-drawer")).toBeVisible();
|
||||
await expect(page.locator("text=Ihr Warenkorb ist leer")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should close cart drawer on backdrop click", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("header-cart-button").click();
|
||||
await expect(page.getByTestId("cart-drawer")).toBeVisible();
|
||||
await page.getByTestId("cart-drawer-backdrop").click();
|
||||
await expect(page.getByTestId("cart-drawer")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("should have checkout link in cart drawer", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("header-cart-button").click();
|
||||
await expect(page.getByTestId("cart-drawer")).toBeVisible();
|
||||
const checkout = page.getByTestId("cart-drawer-checkout");
|
||||
await expect(checkout).toHaveAttribute("href", "/mietanfrage");
|
||||
});
|
||||
|
||||
test("should have view cart link in cart drawer", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("header-cart-button").click();
|
||||
await expect(page.getByTestId("cart-drawer")).toBeVisible();
|
||||
const viewCart = page.getByTestId("cart-drawer-view-cart");
|
||||
await expect(viewCart).toHaveAttribute("href", "/warenkorb");
|
||||
});
|
||||
|
||||
test("should have mobile cart button", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await expect(page.getByTestId("header-cart-button-mobile")).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Mietanfrage Page", () => {
|
||||
test("should render mietanfrage page with heading", async ({ page }) => {
|
||||
await page.goto("/mietanfrage");
|
||||
await expect(page.locator("h1")).toContainText("Mietanfrage");
|
||||
});
|
||||
|
||||
test("should show empty cart state when no items in cart", async ({ page }) => {
|
||||
await page.goto("/mietanfrage");
|
||||
await expect(page.getByTestId("mietanfrage-empty-cart")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should have link to mietkatalog from empty cart state", async ({ page }) => {
|
||||
await page.goto("/mietanfrage");
|
||||
const cta = page.getByTestId("mietanfrage-empty-cta");
|
||||
await expect(cta).toBeVisible();
|
||||
await expect(cta).toHaveAttribute("href", "/mietkatalog");
|
||||
});
|
||||
|
||||
test("should have breadcrumb with warenkorb link", async ({ page }) => {
|
||||
await page.goto("/mietanfrage");
|
||||
const breadcrumb = page.locator('nav[aria-label="Breadcrumb"]');
|
||||
await expect(breadcrumb).toBeVisible();
|
||||
await expect(breadcrumb.locator('a[href="/warenkorb"]')).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
describe("Cart Store", () => {
|
||||
const storePath = resolve(__dirname, "../../stores/cart.ts");
|
||||
const content = readFileSync(storePath, "utf-8");
|
||||
|
||||
it("should define useCartStore with defineStore", () => {
|
||||
expect(content).toContain("defineStore");
|
||||
expect(content).toContain("useCartStore");
|
||||
});
|
||||
|
||||
it("should have CartItem interface with required fields", () => {
|
||||
expect(content).toContain("equipment_id");
|
||||
expect(content).toContain("name");
|
||||
expect(content).toContain("quantity");
|
||||
expect(content).toContain("rental_price");
|
||||
expect(content).toContain("image_url");
|
||||
});
|
||||
|
||||
it("should have totalCount getter", () => {
|
||||
expect(content).toContain("totalCount");
|
||||
expect(content).toContain("reduce");
|
||||
});
|
||||
|
||||
it("should have isEmpty getter", () => {
|
||||
expect(content).toContain("isEmpty");
|
||||
});
|
||||
|
||||
it("should have hasItems getter", () => {
|
||||
expect(content).toContain("hasItems");
|
||||
});
|
||||
|
||||
it("should have apiItems getter for rental request payload", () => {
|
||||
expect(content).toContain("apiItems");
|
||||
expect(content).toContain("equipment_id");
|
||||
expect(content).toContain("quantity");
|
||||
});
|
||||
|
||||
it("should have addItem action", () => {
|
||||
expect(content).toContain("addItem");
|
||||
expect(content).toContain("existing");
|
||||
expect(content).toContain("quantity += 1");
|
||||
});
|
||||
|
||||
it("should have addItemByFields action", () => {
|
||||
expect(content).toContain("addItemByFields");
|
||||
});
|
||||
|
||||
it("should have removeItem action", () => {
|
||||
expect(content).toContain("removeItem");
|
||||
expect(content).toContain("splice");
|
||||
});
|
||||
|
||||
it("should have updateQuantity action", () => {
|
||||
expect(content).toContain("updateQuantity");
|
||||
expect(content).toContain("quantity <= 0");
|
||||
});
|
||||
|
||||
it("should have incrementQuantity action", () => {
|
||||
expect(content).toContain("incrementQuantity");
|
||||
});
|
||||
|
||||
it("should have decrementQuantity action", () => {
|
||||
expect(content).toContain("decrementQuantity");
|
||||
});
|
||||
|
||||
it("should have clearCart action", () => {
|
||||
expect(content).toContain("clearCart");
|
||||
expect(content).toContain("items = []");
|
||||
});
|
||||
|
||||
it("should have RentalRequestPayload interface", () => {
|
||||
expect(content).toContain("RentalRequestPayload");
|
||||
expect(content).toContain("event_name");
|
||||
expect(content).toContain("date_start");
|
||||
expect(content).toContain("date_end");
|
||||
expect(content).toContain("location");
|
||||
expect(content).toContain("person_count");
|
||||
expect(content).toContain("contact_name");
|
||||
expect(content).toContain("contact_email");
|
||||
expect(content).toContain("contact_phone");
|
||||
expect(content).toContain("contact_street");
|
||||
expect(content).toContain("contact_postalcode");
|
||||
expect(content).toContain("contact_city");
|
||||
expect(content).toContain("message");
|
||||
expect(content).toContain("items");
|
||||
});
|
||||
|
||||
it("should have RentalRequestResponse interface", () => {
|
||||
expect(content).toContain("RentalRequestResponse");
|
||||
expect(content).toContain("reference_number");
|
||||
expect(content).toContain("status");
|
||||
});
|
||||
|
||||
it("should use TypeScript", () => {
|
||||
expect(content).toContain("import type");
|
||||
expect(content).toContain("export interface");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
describe("Mietanfrage Page", () => {
|
||||
const pagePath = resolve(__dirname, "../../pages/mietanfrage.vue");
|
||||
const content = readFileSync(pagePath, "utf-8");
|
||||
|
||||
it("should have page title Mietanfrage", () => {
|
||||
expect(content).toContain("Mietanfrage");
|
||||
expect(content).toContain('data-testid="mietanfrage-title"');
|
||||
});
|
||||
|
||||
it("should use useCart composable", () => {
|
||||
expect(content).toContain("useCart");
|
||||
});
|
||||
|
||||
it("should use useApi composable", () => {
|
||||
expect(content).toContain("useApi");
|
||||
});
|
||||
|
||||
it("should have breadcrumb navigation", () => {
|
||||
expect(content).toContain("Breadcrumb");
|
||||
expect(content).toContain('to="/"');
|
||||
expect(content).toContain('to="/warenkorb"');
|
||||
});
|
||||
|
||||
it("should have empty cart state with redirect to mietkatalog", () => {
|
||||
expect(content).toContain('data-testid="mietanfrage-empty-cart"');
|
||||
expect(content).toContain('to="/mietkatalog"');
|
||||
expect(content).toContain("Ihr Warenkorb ist leer");
|
||||
});
|
||||
|
||||
it("should have cart overview section (read-only)", () => {
|
||||
expect(content).toContain('data-testid="mietanfrage-cart-overview"');
|
||||
expect(content).toContain('data-testid="mietanfrage-cart-item"');
|
||||
});
|
||||
|
||||
it("should have event name field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-event-name"');
|
||||
expect(content).toContain("Veranstaltung");
|
||||
});
|
||||
|
||||
it("should have date start field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-date-start"');
|
||||
expect(content).toContain("Von");
|
||||
});
|
||||
|
||||
it("should have date end field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-date-end"');
|
||||
expect(content).toContain("Bis");
|
||||
});
|
||||
|
||||
it("should have location field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-location"');
|
||||
expect(content).toContain("Veranstaltungsort");
|
||||
});
|
||||
|
||||
it("should have person count field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-person-count"');
|
||||
expect(content).toContain("Personenanzahl");
|
||||
});
|
||||
|
||||
it("should have contact name field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-contact-name"');
|
||||
expect(content).toContain("Name");
|
||||
});
|
||||
|
||||
it("should have contact company field (optional)", () => {
|
||||
expect(content).toContain('data-testid="form-contact-company"');
|
||||
expect(content).toContain("Firma");
|
||||
});
|
||||
|
||||
it("should have contact email field (required)", () => {
|
||||
expect(content).toContain('data-testid="form-contact-email"');
|
||||
expect(content).toContain("E-Mail");
|
||||
});
|
||||
|
||||
it("should have contact phone field (optional)", () => {
|
||||
expect(content).toContain('data-testid="form-contact-phone"');
|
||||
expect(content).toContain("Telefon");
|
||||
});
|
||||
|
||||
it("should have contact street field", () => {
|
||||
expect(content).toContain('data-testid="form-contact-street"');
|
||||
});
|
||||
|
||||
it("should have contact postal code field", () => {
|
||||
expect(content).toContain('data-testid="form-contact-postalcode"');
|
||||
});
|
||||
|
||||
it("should have contact city field", () => {
|
||||
expect(content).toContain('data-testid="form-contact-city"');
|
||||
});
|
||||
|
||||
it("should have message textarea", () => {
|
||||
expect(content).toContain('data-testid="form-message"');
|
||||
expect(content).toContain("Nachricht");
|
||||
});
|
||||
|
||||
it("should have email validation with regex", () => {
|
||||
expect(content).toContain("emailRegex");
|
||||
expect(content).toContain("emailRegex.test");
|
||||
});
|
||||
|
||||
it("should have validateField function", () => {
|
||||
expect(content).toContain("validateField");
|
||||
expect(content).toContain("validateAll");
|
||||
});
|
||||
|
||||
it("should have error display for event_name", () => {
|
||||
expect(content).toContain('data-testid="error-event-name"');
|
||||
});
|
||||
|
||||
it("should have error display for contact_email", () => {
|
||||
expect(content).toContain('data-testid="error-contact-email"');
|
||||
});
|
||||
|
||||
it("should have submit button", () => {
|
||||
expect(content).toContain('data-testid="mietanfrage-submit"');
|
||||
expect(content).toContain("Anfrage senden");
|
||||
});
|
||||
|
||||
it("should have submitting state with spinner", () => {
|
||||
expect(content).toContain("submitting");
|
||||
expect(content).toContain("Wird gesendet");
|
||||
expect(content).toContain("animate-spin");
|
||||
});
|
||||
|
||||
it("should have success state with reference number", () => {
|
||||
expect(content).toContain('data-testid="mietanfrage-success"');
|
||||
expect(content).toContain('data-testid="mietanfrage-reference-number"');
|
||||
expect(content).toContain("Vielen Dank");
|
||||
expect(content).toContain("reference_number");
|
||||
});
|
||||
|
||||
it("should have error state", () => {
|
||||
expect(content).toContain('data-testid="mietanfrage-error"');
|
||||
expect(content).toContain("Fehler");
|
||||
});
|
||||
|
||||
it("should POST to /api/rental-requests", () => {
|
||||
expect(content).toContain("/api/rental-requests");
|
||||
expect(content).toContain("post");
|
||||
});
|
||||
|
||||
it("should clear cart after successful submit", () => {
|
||||
expect(content).toContain("clearCart");
|
||||
expect(content).toContain("success");
|
||||
});
|
||||
|
||||
it("should have back link to warenkorb", () => {
|
||||
expect(content).toContain('data-testid="mietanfrage-back"');
|
||||
expect(content).toContain('to="/warenkorb"');
|
||||
});
|
||||
|
||||
it("should use TypeScript with lang=ts", () => {
|
||||
expect(content).toContain('<script setup lang="ts">');
|
||||
});
|
||||
|
||||
it("should use Tailwind classes, no inline styles", () => {
|
||||
expect(content).not.toContain('style="');
|
||||
});
|
||||
|
||||
it("should use useHead for page title", () => {
|
||||
expect(content).toContain("useHead");
|
||||
});
|
||||
|
||||
it("should import RentalRequestPayload and RentalRequestResponse types", () => {
|
||||
expect(content).toContain("RentalRequestPayload");
|
||||
expect(content).toContain("RentalRequestResponse");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
describe("Warenkorb Page", () => {
|
||||
const pagePath = resolve(__dirname, "../../pages/warenkorb.vue");
|
||||
const content = readFileSync(pagePath, "utf-8");
|
||||
|
||||
it("should have page title Warenkorb", () => {
|
||||
expect(content).toContain("Warenkorb");
|
||||
expect(content).toContain('data-testid="warenkorb-title"');
|
||||
});
|
||||
|
||||
it("should use useCart composable", () => {
|
||||
expect(content).toContain("useCart");
|
||||
});
|
||||
|
||||
it("should have breadcrumb navigation", () => {
|
||||
expect(content).toContain("Breadcrumb");
|
||||
expect(content).toContain('to="/"');
|
||||
});
|
||||
|
||||
it("should have empty state with link to mietkatalog", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-empty"');
|
||||
expect(content).toContain('to="/mietkatalog"');
|
||||
expect(content).toContain("Ihr Warenkorb ist leer");
|
||||
});
|
||||
|
||||
it("should have cart item list with data-testid", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-item"');
|
||||
});
|
||||
|
||||
it("should have item name with link to detail page", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-item-name"');
|
||||
expect(content).toContain("/mietkatalog/");
|
||||
});
|
||||
|
||||
it("should have quantity stepper with increment and decrement", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-item-stepper"');
|
||||
expect(content).toContain('data-testid="warenkorb-item-decrement"');
|
||||
expect(content).toContain('data-testid="warenkorb-item-increment"');
|
||||
expect(content).toContain("decrementQuantity");
|
||||
expect(content).toContain("incrementQuantity");
|
||||
});
|
||||
|
||||
it("should display item quantity", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-item-quantity"');
|
||||
expect(content).toContain("item.quantity");
|
||||
});
|
||||
|
||||
it("should have remove button", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-item-remove"');
|
||||
expect(content).toContain("removeItem");
|
||||
});
|
||||
|
||||
it("should have clear cart button", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-clear"');
|
||||
expect(content).toContain("clearCart");
|
||||
expect(content).toContain("Warenkorb leeren");
|
||||
});
|
||||
|
||||
it("should have checkout link to mietanfrage", () => {
|
||||
expect(content).toContain('data-testid="warenkorb-checkout"');
|
||||
expect(content).toContain('to="/mietanfrage"');
|
||||
expect(content).toContain("Zur Mietanfrage");
|
||||
});
|
||||
|
||||
it("should display total count", () => {
|
||||
expect(content).toContain("totalCount");
|
||||
});
|
||||
|
||||
it("should use TypeScript with lang=ts", () => {
|
||||
expect(content).toContain('<script setup lang="ts">');
|
||||
});
|
||||
|
||||
it("should use Tailwind classes, no inline styles", () => {
|
||||
expect(content).not.toContain('style="');
|
||||
});
|
||||
|
||||
it("should use useHead for page title", () => {
|
||||
expect(content).toContain("useHead");
|
||||
});
|
||||
|
||||
it("should have formatPrice function", () => {
|
||||
expect(content).toContain("formatPrice");
|
||||
expect(content).toContain("Intl.NumberFormat");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user