mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
02e48162c4
Ports 16 diverged Playwright e2e specs verbatim from langgraph-python and adds 3 previously-missing specs (chat-customization-css, prebuilt-sidebar, reasoning-custom). All 19 files are byte-identical to LGP, mirroring the same approach the recent ADK parity push used for the demo pages. Why this matters even though D5 is the gold standard: the per-package Playwright suites (`pnpm test:e2e`) are the local dev validation loop. Without parity here, a contributor editing google-adk's CopilotChat surface has no local check that matches what langgraph-python ships, and tiny divergences between the two surfaces (missing testids, stale selectors, wrong assertion shapes) silently accumulate until they surface as D5 regressions in CI.
116 lines
4.3 KiB
TypeScript
116 lines
4.3 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
|
|
// Each custom slot wraps the default in a `SlotMarker` that emits
|
|
// `data-slot-label="<slot-path>"`. That attribute is the canonical
|
|
// signal that a slot override wired through end-to-end (the welcome
|
|
// screen + disclaimer also expose dedicated `data-testid` attributes
|
|
// for ergonomics).
|
|
const SLOT_LABEL_ASSISTANT = '[data-slot-label="MessageView.AssistantMessage"]';
|
|
|
|
test.describe("Chat Slots", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/demos/chat-slots");
|
|
});
|
|
|
|
test("custom welcome screen slot renders on first load", async ({ page }) => {
|
|
// The custom welcomeScreen slot replaces the default welcome. Both its
|
|
// own testid and the nested welcomeMessage sub-slot's testid prove the
|
|
// override wired through end-to-end. Asserting both catches accidental
|
|
// fallback to the default CopilotChat welcome.
|
|
const welcome = page.locator('[data-testid="custom-welcome-screen"]');
|
|
await expect(welcome).toBeVisible();
|
|
|
|
await expect(
|
|
welcome.locator('[data-testid="custom-welcome-message"]'),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("both suggestion pills render with verbatim titles", async ({
|
|
page,
|
|
}) => {
|
|
// useConfigureSuggestions registers exactly two pills with available: "always".
|
|
// Both should be visible immediately on the welcome screen.
|
|
await expect(
|
|
page
|
|
.locator('[data-testid="copilot-suggestion"]')
|
|
.filter({ hasText: "Write a sonnet" }),
|
|
).toBeVisible({ timeout: 15000 });
|
|
|
|
await expect(
|
|
page
|
|
.locator('[data-testid="copilot-suggestion"]')
|
|
.filter({ hasText: "Tell me a joke" }),
|
|
).toBeVisible({ timeout: 15000 });
|
|
});
|
|
|
|
test('clicking "Tell me a joke" shows the custom assistant message slot', async ({
|
|
page,
|
|
}) => {
|
|
// Click the suggestion pill — this sends "Tell me a short joke." The
|
|
// assistant responds with text (neutral agent, no tools), and its
|
|
// bubble must be wrapped in the CustomAssistantMessage SlotMarker.
|
|
await page
|
|
.locator('[data-testid="copilot-suggestion"]')
|
|
.filter({ hasText: "Tell me a joke" })
|
|
.first()
|
|
.click();
|
|
|
|
// The MessageView.AssistantMessage slot-marker wraps every assistant
|
|
// bubble; its presence proves the slot override took effect rather
|
|
// than the default CopilotChatAssistantMessage rendering bare.
|
|
await expect(page.locator(SLOT_LABEL_ASSISTANT).first()).toBeVisible({
|
|
timeout: 45000,
|
|
});
|
|
});
|
|
|
|
test("custom disclaimer slot renders after the first user message", async ({
|
|
page,
|
|
}) => {
|
|
// Type and send via the send button — Enter-on-textarea was intermittently
|
|
// dropping the submit on this deployment. We assert the disclaimer +
|
|
// custom assistant wrapper appear once we transition out of the welcome
|
|
// state.
|
|
const input = page.getByPlaceholder("Type a message");
|
|
await input.fill("Hello");
|
|
await page.locator('[data-testid="copilot-send-button"]').first().click();
|
|
|
|
// Assistant replies and is wrapped in the custom slot.
|
|
await expect(page.locator(SLOT_LABEL_ASSISTANT).first()).toBeVisible({
|
|
timeout: 45000,
|
|
});
|
|
|
|
// The custom disclaimer slot lives below the input on the post-welcome
|
|
// chat view. The welcome-screen state hides it; once the assistant
|
|
// responds the welcome is gone and the disclaimer should be visible.
|
|
await expect(page.locator('[data-testid="custom-disclaimer"]')).toBeVisible(
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("second assistant turn is also wrapped in the custom slot", async ({
|
|
page,
|
|
}) => {
|
|
const input = page.getByPlaceholder("Type a message");
|
|
const sendBtn = () =>
|
|
page.locator('[data-testid="copilot-send-button"]').first();
|
|
|
|
// Turn 1
|
|
await input.fill("Hi");
|
|
await sendBtn().click();
|
|
await expect(page.locator(SLOT_LABEL_ASSISTANT).first()).toBeVisible({
|
|
timeout: 45000,
|
|
});
|
|
|
|
// Turn 2 — the slot should wrap every assistant turn, not just the first.
|
|
await input.fill("Say something short");
|
|
await sendBtn().click();
|
|
|
|
// Expect at least two custom-wrapped assistant messages.
|
|
await expect
|
|
.poll(async () => await page.locator(SLOT_LABEL_ASSISTANT).count(), {
|
|
timeout: 45000,
|
|
})
|
|
.toBeGreaterThanOrEqual(2);
|
|
});
|
|
});
|