Skip to content

Commit 5d3b8eb

Browse files
test: add tests for custom style
1 parent 1db3b33 commit 5d3b8eb

3 files changed

Lines changed: 289 additions & 0 deletions

File tree

18.4 KB
Loading
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import { test, expect, type Page } from '@playwright/test';
2+
3+
import {
4+
editorLocator,
5+
getSerializedHtml,
6+
gotoVisualRegression,
7+
setEditorHtml,
8+
} from '../helpers/visual-regression';
9+
import { toolbarButton } from '../helpers/toolbar';
10+
11+
function textColorButton(page: Page) {
12+
return page.locator('[data-testid="toolbar-text-color"]');
13+
}
14+
15+
function bgColorButton(page: Page) {
16+
return page.locator('[data-testid="toolbar-bg-color"]');
17+
}
18+
19+
function colorSwatch(page: Page, color: string) {
20+
return page.locator(
21+
`[data-testid="toolbar-color-swatch-${color.replace('#', '')}"]`
22+
);
23+
}
24+
25+
function colorSwatchClear(page: Page) {
26+
return page.locator('[data-testid="toolbar-color-swatch-clear"]');
27+
}
28+
29+
async function applyTextColor(page: Page, color: string) {
30+
await textColorButton(page).click();
31+
await colorSwatch(page, color).click();
32+
}
33+
34+
async function clearTextColor(page: Page) {
35+
await textColorButton(page).click();
36+
await colorSwatchClear(page).click();
37+
}
38+
39+
async function applyBgColor(page: Page, color: string) {
40+
await bgColorButton(page).click();
41+
await colorSwatch(page, color).click();
42+
}
43+
44+
async function clearBgColor(page: Page) {
45+
await bgColorButton(page).click();
46+
await colorSwatchClear(page).click();
47+
}
48+
49+
const ROUND_TRIP_CASES: { name: string; input: string; expected: string }[] = [
50+
{
51+
name: 'foreground color only',
52+
input: '<html><p><span style="color: #FF0000">Red text</span></p></html>',
53+
expected:
54+
'<html><p><span style="color: rgb(255, 0, 0);">Red text</span></p></html>',
55+
},
56+
{
57+
name: 'background color only',
58+
input:
59+
'<html><p><span style="background-color: #FFFF00">Yellow bg</span></p></html>',
60+
expected:
61+
'<html><p><span style="background-color: rgb(255, 255, 0);">Yellow bg</span></p></html>',
62+
},
63+
{
64+
name: 'foreground and background color',
65+
input:
66+
'<html><p><span style="color: #FF0000; background-color: #FFFF00">Both</span></p></html>',
67+
expected:
68+
'<html><p><span style="color: rgb(255, 0, 0); background-color: rgb(255, 255, 0);">Both</span></p></html>',
69+
},
70+
{
71+
name: '8-digit hex background (transparent)',
72+
input:
73+
'<html><p><span style="background-color: #00FF0040">25% green bg</span></p></html>',
74+
expected:
75+
'<html><p><span style="background-color: rgba(0, 255, 0, 0.25);">25% green bg</span></p></html>',
76+
},
77+
{
78+
name: '8-digit hex foreground (transparent)',
79+
input:
80+
'<html><p><span style="color: #0000FF80">50% blue text</span></p></html>',
81+
expected:
82+
'<html><p><span style="color: rgba(0, 0, 255, 0.5);">50% blue text</span></p></html>',
83+
},
84+
{
85+
name: 'color inside heading',
86+
input:
87+
'<html><h6><span style="color: #000000; background-color: #00FF00">Black on green</span></h6></html>',
88+
expected:
89+
'<html><h6><span style="color: rgb(0, 0, 0); background-color: rgb(0, 255, 0);">Black on green</span></h6></html>',
90+
},
91+
{
92+
name: 'color wraps bold mark',
93+
input:
94+
'<html><p><span style="color: #FF0000"><b>Bold red</b></span></p></html>',
95+
expected:
96+
'<html><p><span style="color: rgb(255, 0, 0);"><b>Bold red</b></span></p></html>',
97+
},
98+
{
99+
name: 'multiple colored spans in one paragraph',
100+
input:
101+
'<html><p><span style="color: #FF0000">Red</span> plain <span style="color: #0000FF">Blue</span></p></html>',
102+
expected:
103+
'<html><p><span style="color: rgb(255, 0, 0);">Red</span> plain <span style="color: rgb(0, 0, 255);">Blue</span></p></html>',
104+
},
105+
];
106+
107+
test.describe('custom style colors - HTML serialization', () => {
108+
test.beforeEach(async ({ page }) => {
109+
await gotoVisualRegression(page);
110+
});
111+
112+
for (const { name, input, expected } of ROUND_TRIP_CASES) {
113+
test(name, async ({ page }) => {
114+
await setEditorHtml(page, input);
115+
await expect.poll(async () => getSerializedHtml(page)).toBe(expected);
116+
});
117+
}
118+
});
119+
120+
test('custom style colors visual regression', async ({ page }) => {
121+
await gotoVisualRegression(page);
122+
123+
const html = [
124+
'<html>',
125+
'<p><span style="color: #FF5733">Standard 6-digit hex text</span></p>',
126+
'<p><span style="color: #FFFFFF; background-color: #000000">White text on black background</span></p>',
127+
'<p><span style="background-color: #00FF0040">25% transparent green background</span></p>',
128+
'<p><span style="color: #0000FF80">50% transparent blue text</span></p>',
129+
'<p><span style="color: #F00">Red 3-digit shorthand text</span></p>',
130+
'<h6><span style="color: #000000; background-color: #00FF00">Black text on green</span></h6>',
131+
'</html>',
132+
].join('');
133+
134+
await setEditorHtml(page, html);
135+
136+
const editor = editorLocator(page);
137+
await expect(editor).toHaveScreenshot('custom-style-colors-visual.png');
138+
});
139+
140+
test.describe('custom style colors - toolbar interaction', () => {
141+
test.beforeEach(async ({ page }) => {
142+
await gotoVisualRegression(page);
143+
});
144+
145+
test('apply foreground color then type text', async ({ page }) => {
146+
const editor = editorLocator(page);
147+
await editor.click();
148+
149+
await applyTextColor(page, '#FF0000');
150+
await editor.pressSequentially('Red text', { delay: 80 });
151+
152+
await expect
153+
.poll(async () => getSerializedHtml(page))
154+
.toBe(
155+
'<html><p><span style="color: rgb(255, 0, 0);">Red text</span></p></html>'
156+
);
157+
});
158+
159+
test('clear foreground color stops coloring new text', async ({ page }) => {
160+
const editor = editorLocator(page);
161+
await editor.click();
162+
163+
await applyTextColor(page, '#FF0000');
164+
await editor.pressSequentially('Red', { delay: 80 });
165+
await clearTextColor(page);
166+
await editor.pressSequentially(' plain', { delay: 80 });
167+
168+
await expect
169+
.poll(async () => getSerializedHtml(page))
170+
.toBe(
171+
'<html><p><span style="color: rgb(255, 0, 0);">Red</span> plain</p></html>'
172+
);
173+
});
174+
175+
test('apply background color then type text', async ({ page }) => {
176+
const editor = editorLocator(page);
177+
await editor.click();
178+
179+
await applyBgColor(page, '#FFFF00');
180+
await editor.pressSequentially('Yellow back', { delay: 80 });
181+
182+
await expect
183+
.poll(async () => getSerializedHtml(page))
184+
.toBe(
185+
'<html><p><span style="background-color: rgb(255, 255, 0);">Yellow back</span></p></html>'
186+
);
187+
});
188+
189+
test('clear background color stops coloring new text', async ({ page }) => {
190+
const editor = editorLocator(page);
191+
await editor.click();
192+
193+
await applyBgColor(page, '#FFFF00');
194+
await editor.pressSequentially('Yellow', { delay: 80 });
195+
await clearBgColor(page);
196+
await editor.pressSequentially(' plain', { delay: 80 });
197+
198+
await expect
199+
.poll(async () => getSerializedHtml(page))
200+
.toBe(
201+
'<html><p><span style="background-color: rgb(255, 255, 0);">Yellow</span> plain</p></html>'
202+
);
203+
});
204+
205+
test('apply foreground and background color together', async ({ page }) => {
206+
const editor = editorLocator(page);
207+
await editor.click();
208+
209+
await applyTextColor(page, '#FF0000');
210+
await applyBgColor(page, '#FFFF00');
211+
await editor.pressSequentially('Red+Yellow', { delay: 80 });
212+
213+
await expect
214+
.poll(async () => getSerializedHtml(page))
215+
.toBe(
216+
'<html><p><span style="color: rgb(255, 0, 0); background-color: rgb(255, 255, 0);">Red+Yellow</span></p></html>'
217+
);
218+
});
219+
220+
test('foreground color with bold', async ({ page }) => {
221+
const editor = editorLocator(page);
222+
const boldBtn = toolbarButton(page, 'bold');
223+
await editor.click();
224+
225+
await boldBtn.click();
226+
await applyTextColor(page, '#FF0000');
227+
await editor.pressSequentially('Bold red', { delay: 80 });
228+
229+
await expect
230+
.poll(async () => getSerializedHtml(page))
231+
.toBe(
232+
'<html><p><span style="color: rgb(255, 0, 0);"><b>Bold red</b></span></p></html>'
233+
);
234+
});
235+
236+
test('background color with italic', async ({ page }) => {
237+
const editor = editorLocator(page);
238+
const italicBtn = toolbarButton(page, 'italic');
239+
await editor.click();
240+
241+
await italicBtn.click();
242+
await applyBgColor(page, '#FFFF00');
243+
await editor.pressSequentially('Italic yellow back', { delay: 80 });
244+
245+
await expect
246+
.poll(async () => getSerializedHtml(page))
247+
.toBe(
248+
'<html><p><span style="background-color: rgb(255, 255, 0);"><i>Italic yellow back</i></span></p></html>'
249+
);
250+
});
251+
252+
test('toolbar text-color button shows active swatch when color is set', async ({
253+
page,
254+
}) => {
255+
const editor = editorLocator(page);
256+
await editor.click();
257+
258+
await applyTextColor(page, '#FF0000');
259+
260+
// Re-open the picker – the chosen swatch should be marked as active
261+
await textColorButton(page).click();
262+
await expect(colorSwatch(page, '#FF0000')).toHaveClass(
263+
/toolbar-color-swatch--active/
264+
);
265+
266+
// Close the picker
267+
await textColorButton(page).click();
268+
});
269+
270+
test('toolbar bg-color button shows active swatch when color is set', async ({
271+
page,
272+
}) => {
273+
const editor = editorLocator(page);
274+
await editor.click();
275+
276+
await applyBgColor(page, '#FFFF00');
277+
278+
// Re-open the picker – the chosen swatch should be marked as active
279+
await bgColorButton(page).click();
280+
await expect(colorSwatch(page, '#FFFF00')).toHaveClass(
281+
/toolbar-color-swatch--active/
282+
);
283+
284+
// Close the picker
285+
await bgColorButton(page).click();
286+
});
287+
});

apps/example-web/src/components/Toolbar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ export function Toolbar({
319319
<div className="toolbar-color-picker">
320320
<button
321321
type="button"
322+
data-testid="toolbar-color-swatch-clear"
322323
className="toolbar-color-swatch toolbar-color-swatch--clear"
323324
onPointerDown={(e) => {
324325
if (e.pointerType === 'mouse') e.preventDefault();
@@ -339,6 +340,7 @@ export function Toolbar({
339340
<button
340341
key={color}
341342
type="button"
343+
data-testid={`toolbar-color-swatch-${color.replace('#', '')}`}
342344
className={`toolbar-color-swatch${isActive ? ' toolbar-color-swatch--active' : ''}`}
343345
style={{ backgroundColor: color }}
344346
onPointerDown={(e) => {

0 commit comments

Comments
 (0)