Skip to content

Commit 3efeff1

Browse files
test: add E2E tests for callout and collapsible blocks (#222) (#229)
Co-authored-by: Ona <no-reply@ona.com>
1 parent e030f9e commit 3efeff1

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { test, expect } from "./fixtures/auth";
2+
import { navigateToEditorPage } from "./fixtures/editor-helpers";
3+
4+
/**
5+
* Helper: insert a block via the slash command menu.
6+
* Types `/<command>`, waits for the matching option, and clicks it.
7+
*/
8+
async function insertViaSlashCommand(
9+
page: import("@playwright/test").Page,
10+
commandLabel: string
11+
) {
12+
const editor = page.locator('[contenteditable="true"]');
13+
await editor.click();
14+
await page.keyboard.press("End");
15+
await page.keyboard.press("Enter");
16+
await page.keyboard.type("/");
17+
18+
const option = page
19+
.locator('[role="option"]')
20+
.filter({ hasText: commandLabel });
21+
await expect(option).toBeVisible({ timeout: 3_000 });
22+
await option.click();
23+
}
24+
25+
test.describe("Callout block", () => {
26+
test.beforeEach(async ({ authenticatedPage: page }) => {
27+
await navigateToEditorPage(page);
28+
});
29+
30+
test("insert callout via /callout and verify distinct styling", async ({
31+
authenticatedPage: page,
32+
}) => {
33+
const editor = page.locator('[contenteditable="true"]');
34+
await expect(editor).toBeVisible({ timeout: 10_000 });
35+
36+
await insertViaSlashCommand(page, "Callout");
37+
38+
// The callout renders as a div with border-l-2 and an emoji span
39+
const callout = editor.locator(".callout-emoji").first();
40+
await expect(callout).toBeVisible({ timeout: 3_000 });
41+
42+
// Verify the callout container has distinct styling (border-l-2 class)
43+
// that differentiates it from a code block
44+
const calloutContainer = callout.locator("..");
45+
await expect(calloutContainer).toHaveClass(/border-l-2/);
46+
await expect(calloutContainer).toHaveClass(/bg-muted/);
47+
48+
// The emoji should be the default 💡
49+
await expect(callout).toHaveText("💡");
50+
51+
// Verify it does NOT look like a code block — code blocks use <code>
52+
// elements, callouts use a div with border-l styling
53+
const calloutTag = await calloutContainer.evaluate((el) =>
54+
el.tagName.toLowerCase()
55+
);
56+
expect(calloutTag).toBe("div");
57+
});
58+
});
59+
60+
test.describe("Collapsible/Toggle block", () => {
61+
test.beforeEach(async ({ authenticatedPage: page }) => {
62+
await navigateToEditorPage(page);
63+
});
64+
65+
test("insert toggle via /toggle and verify expand/collapse", async ({
66+
authenticatedPage: page,
67+
}) => {
68+
const editor = page.locator('[contenteditable="true"]');
69+
await expect(editor).toBeVisible({ timeout: 10_000 });
70+
71+
await insertViaSlashCommand(page, "Toggle");
72+
73+
// The collapsible renders as a <details> element with a <summary>
74+
const details = editor.locator("details").first();
75+
await expect(details).toBeVisible({ timeout: 3_000 });
76+
77+
// It should start open (the plugin sets open=true by default)
78+
await expect(details).toHaveAttribute("open", "");
79+
80+
// The content area should be visible when open
81+
const content = details.locator("summary + div");
82+
await expect(content).toBeVisible();
83+
84+
// Click the chevron toggle button to collapse
85+
const chevron = details.locator(".collapsible-toggle").first();
86+
await expect(chevron).toBeVisible();
87+
await chevron.click();
88+
89+
// After clicking, the details element should no longer have the open attribute
90+
await expect(details).not.toHaveAttribute("open", "", { timeout: 2_000 });
91+
92+
// The content should be hidden when collapsed
93+
await expect(content).not.toBeVisible();
94+
95+
// Click again to re-expand
96+
await chevron.click();
97+
await expect(details).toHaveAttribute("open", "", { timeout: 2_000 });
98+
await expect(content).toBeVisible();
99+
});
100+
101+
test("type content inside collapsible content area and verify persistence", async ({
102+
authenticatedPage: page,
103+
}) => {
104+
const editor = page.locator('[contenteditable="true"]');
105+
await expect(editor).toBeVisible({ timeout: 10_000 });
106+
107+
await insertViaSlashCommand(page, "Toggle");
108+
109+
const details = editor.locator("details").first();
110+
await expect(details).toBeVisible({ timeout: 3_000 });
111+
112+
// The content area is the div after the summary, containing a paragraph
113+
const contentArea = details.locator("summary + div");
114+
await expect(contentArea).toBeVisible();
115+
116+
// The default content paragraph has "Toggle content" — click into it
117+
const contentParagraph = contentArea.locator("p").first();
118+
await expect(contentParagraph).toBeVisible();
119+
await contentParagraph.click();
120+
121+
// Triple-click to select just the paragraph text, then type replacement
122+
await contentParagraph.click({ clickCount: 3 });
123+
await page.keyboard.type("My custom toggle content");
124+
125+
// Verify the typed text appears in the content area
126+
await expect(contentArea).toContainText("My custom toggle content");
127+
128+
// Collapse and re-expand to verify content persists
129+
const chevron = details.locator(".collapsible-toggle").first();
130+
await chevron.click();
131+
await expect(details).not.toHaveAttribute("open", "", { timeout: 2_000 });
132+
133+
await chevron.click();
134+
await expect(details).toHaveAttribute("open", "", { timeout: 2_000 });
135+
await expect(contentArea).toContainText("My custom toggle content");
136+
});
137+
});

0 commit comments

Comments
 (0)