243 lines
8.5 KiB
TypeScript
243 lines
8.5 KiB
TypeScript
/**
|
|
* Field Widget E2E Tests (Playwright)
|
|
*
|
|
* Tests plugin field widgets in the admin UI:
|
|
* - Color picker widget renders for fields with widget: "color:picker"
|
|
* - Widget is interactive (color input, hex input, presets)
|
|
* - Content saves and loads with widget field values
|
|
* - Widget falls back to default renderer when plugin is not active
|
|
* - Manifest includes widget metadata
|
|
*
|
|
* The e2e fixture has the color plugin configured with a "theme_color"
|
|
* field (type: string, widget: "color:picker") on the posts collection.
|
|
*/
|
|
|
|
import { test, expect } from "../fixtures";
|
|
|
|
const CONTENT_EDIT_URL_PATTERN = /\/content\/posts\/[A-Z0-9]+$/;
|
|
|
|
test.describe("Field Widgets", () => {
|
|
test.beforeEach(async ({ admin }) => {
|
|
await admin.devBypassAuth();
|
|
});
|
|
|
|
test.describe("Color Picker Widget Rendering", () => {
|
|
test("renders color picker widget on new post form", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
// The color picker widget should be visible (has data-testid)
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
|
|
// Should have a color input
|
|
const colorInput = admin.page.locator('[data-testid="color-input"]');
|
|
await expect(colorInput).toBeVisible();
|
|
await expect(colorInput).toHaveAttribute("type", "color");
|
|
|
|
// Should have a hex text input
|
|
const hexInput = admin.page.locator('[data-testid="color-hex-input"]');
|
|
await expect(hexInput).toBeVisible();
|
|
|
|
// Should have a preview swatch
|
|
const preview = admin.page.locator('[data-testid="color-preview"]');
|
|
await expect(preview).toBeVisible();
|
|
|
|
// Should have preset color buttons
|
|
const presets = admin.page.locator('[data-testid="color-presets"]');
|
|
await expect(presets).toBeVisible();
|
|
const presetButtons = presets.locator("button");
|
|
await expect(presetButtons).toHaveCount(10);
|
|
});
|
|
|
|
test("shows field label for color widget", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
// The label "Theme Color" should be visible
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
await expect(widget.locator("label")).toContainText("Theme Color");
|
|
});
|
|
|
|
test("other fields render with default editors", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
// Title field should use standard input (not a widget)
|
|
const titleInput = admin.page.locator("#field-title");
|
|
await expect(titleInput).toBeVisible();
|
|
// Should be a plain input, not a color picker widget
|
|
await expect(admin.page.locator('[data-testid="color-picker-widget"]')).toHaveCount(1);
|
|
await expect(
|
|
titleInput.locator("..").locator('[data-testid="color-picker-widget"]'),
|
|
).toHaveCount(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Color Picker Interaction", () => {
|
|
test("can type a hex value", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
const hexInput = admin.page.locator('[data-testid="color-hex-input"]');
|
|
await expect(hexInput).toBeVisible({ timeout: 10000 });
|
|
await hexInput.fill("#ff6600");
|
|
await expect(hexInput).toHaveValue("#ff6600");
|
|
});
|
|
|
|
test("clicking a preset updates the value", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
|
|
// Click the red preset (#ef4444)
|
|
const redPreset = admin.page.locator('[data-testid="color-preset-ef4444"]');
|
|
await expect(redPreset).toBeVisible();
|
|
await redPreset.click();
|
|
|
|
// Hex input should update
|
|
const hexInput = admin.page.locator('[data-testid="color-hex-input"]');
|
|
await expect(hexInput).toHaveValue("#ef4444");
|
|
});
|
|
|
|
test("clicking different presets changes the value", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
const hexInput = admin.page.locator('[data-testid="color-hex-input"]');
|
|
|
|
// Click blue preset
|
|
await admin.page.locator('[data-testid="color-preset-3b82f6"]').click();
|
|
await expect(hexInput).toHaveValue("#3b82f6");
|
|
|
|
// Click green preset
|
|
await admin.page.locator('[data-testid="color-preset-22c55e"]').click();
|
|
await expect(hexInput).toHaveValue("#22c55e");
|
|
});
|
|
});
|
|
|
|
test.describe("Save and Load Widget Values", () => {
|
|
test("saves content with color value and loads it back", async ({ admin }) => {
|
|
// Create a post with a color
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
// Wait for widget to render
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
|
|
// Fill title
|
|
await admin.fillField("title", "Color Widget Test Post");
|
|
|
|
// Set color via hex input
|
|
const hexInput = admin.page.locator('[data-testid="color-hex-input"]');
|
|
await hexInput.fill("#ff6600");
|
|
|
|
// Save
|
|
await admin.clickSave();
|
|
await admin.waitForSaveComplete();
|
|
|
|
// Should redirect to edit page
|
|
await expect(admin.page).toHaveURL(CONTENT_EDIT_URL_PATTERN, {
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Reload the page to verify the value persisted
|
|
await admin.page.reload();
|
|
await admin.waitForLoading();
|
|
|
|
// Wait for widget and check value
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
const reloadedHex = admin.page.locator('[data-testid="color-hex-input"]');
|
|
await expect(reloadedHex).toHaveValue("#ff6600");
|
|
});
|
|
|
|
test("saves content with preset color value", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
|
|
await admin.fillField("title", "Preset Color Post");
|
|
|
|
// Click purple preset
|
|
await admin.page.locator('[data-testid="color-preset-8b5cf6"]').click();
|
|
|
|
await admin.clickSave();
|
|
await admin.waitForSaveComplete();
|
|
|
|
await expect(admin.page).toHaveURL(CONTENT_EDIT_URL_PATTERN, {
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Reload and verify
|
|
await admin.page.reload();
|
|
await admin.waitForLoading();
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
await expect(admin.page.locator('[data-testid="color-hex-input"]')).toHaveValue("#8b5cf6");
|
|
});
|
|
|
|
test("saves content without color value", async ({ admin }) => {
|
|
await admin.goToNewContent("posts");
|
|
await admin.waitForLoading();
|
|
|
|
const widget = admin.page.locator('[data-testid="color-picker-widget"]');
|
|
await expect(widget).toBeVisible({ timeout: 10000 });
|
|
|
|
// Just set title, don't touch color
|
|
await admin.fillField("title", "No Color Post");
|
|
|
|
await admin.clickSave();
|
|
await admin.waitForSaveComplete();
|
|
|
|
// Should save successfully
|
|
await expect(admin.page).toHaveURL(CONTENT_EDIT_URL_PATTERN, {
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe("Manifest API", () => {
|
|
test("manifest includes widget property on theme_color field", async ({ page }) => {
|
|
const res = await page.request.get("/_emdash/api/manifest", {
|
|
headers: { "X-EmDash-Request": "1" },
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
const manifest = body.data;
|
|
|
|
// Check field has widget
|
|
const postFields = manifest.collections.posts.fields;
|
|
expect(postFields.theme_color).toBeDefined();
|
|
expect(postFields.theme_color.widget).toBe("color:picker");
|
|
expect(postFields.theme_color.kind).toBe("string");
|
|
|
|
// Other fields should not have widget
|
|
expect(postFields.title.widget).toBeUndefined();
|
|
});
|
|
|
|
test("manifest includes color plugin with fieldWidgets", async ({ page }) => {
|
|
const res = await page.request.get("/_emdash/api/manifest", {
|
|
headers: { "X-EmDash-Request": "1" },
|
|
});
|
|
const body = await res.json();
|
|
const manifest = body.data;
|
|
|
|
// Check plugin manifest
|
|
expect(manifest.plugins.color).toBeDefined();
|
|
expect(manifest.plugins.color.enabled).toBe(true);
|
|
expect(manifest.plugins.color.fieldWidgets).toBeDefined();
|
|
expect(manifest.plugins.color.fieldWidgets).toHaveLength(1);
|
|
expect(manifest.plugins.color.fieldWidgets[0].name).toBe("picker");
|
|
expect(manifest.plugins.color.fieldWidgets[0].label).toBe("Color Picker");
|
|
expect(manifest.plugins.color.fieldWidgets[0].fieldTypes).toEqual(["string"]);
|
|
});
|
|
});
|
|
});
|