Skip to content

Commit 6df182e

Browse files
authored
test: added Bold visual tests (#767)
1 parent 4bad59f commit 6df182e

File tree

6 files changed

+244
-22
lines changed

6 files changed

+244
-22
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"playwright": "playwright test --config=tests/playwright/playwright.config.ts",
3636
"playwright:generate": "node scripts/generate-playwright-test.js",
3737
"playwright:watch": "npm run playwright -- --ui",
38+
"playwright:headed": "playwright test --config=tests/playwright/playwright.config.ts --headed",
3839
"playwright:update": "npm run playwright -- -u",
3940
"playwright:clear": "rm -rf ./tests/playwright/.cache",
4041
"playwright:report": "npx playwright show-report playwright-report",

scripts/generate-playwright-test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ if (!fs.existsSync(outputDirPath)) {
3636
fs.mkdirSync(outputDirPath, {recursive: true});
3737
}
3838

39-
const fileName = `${name}.test.tsx`;
39+
// Remove the underscores to enable the test (e.g., `name.visual.test.tsx`).
40+
const fileName = `${name}._visual_.test.tsx`;
4041
const filePath = path.join(outputDirPath, fileName);
4142
const content = template.replace(/%%name%%/g, name);
4243

tests/playwright/core/editor.ts

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,41 @@ export class MarkdownEditorPage {
6262
}
6363

6464
/**
65-
* Asserts that the toolbar button is disabled
65+
* Asserts that the toolbar button is disabled.
6666
*/
67-
async assertToolbarButtonDisabled(label: string) {
68-
const button = this.getToolbarButton(label);
67+
async assertToolbarButtonDisabled(label: string, inPopup = false) {
68+
const root = inPopup ? this.page.locator('.g-popup.g-popup_open') : this.locators.editor;
69+
const button = root.getByLabel(label);
6970
await this.expect(button).toHaveClass(/disabled/);
7071
}
7172

7273
/**
73-
* Asserts that the toolbar button is enabled
74+
* Asserts that the toolbar button is enabled.
7475
*/
75-
async assertToolbarButtonEnabled(label: string) {
76-
const button = this.getToolbarButton(label);
76+
async assertToolbarButtonEnabled(label: string, inPopup = false) {
77+
const root = inPopup ? this.page.locator('.g-popup.g-popup_open') : this.locators.editor;
78+
const button = root.getByLabel(label);
7779
await this.expect(button).not.toHaveClass(/disabled/);
7880
}
7981

82+
/**
83+
* Asserts that the toolbar button is selected.
84+
*/
85+
async assertToolbarButtonSelected(label: string, inPopup = false) {
86+
const root = inPopup ? this.page.locator('.g-popup.g-popup_open') : this.locators.editor;
87+
const button = root.getByLabel(label);
88+
await this.expect(button).toHaveClass(/selected/);
89+
}
90+
91+
/**
92+
* Asserts that the toolbar button is not selected.
93+
*/
94+
async assertToolbarButtonNotSelected(label: string, inPopup = false) {
95+
const root = inPopup ? this.page.locator('.g-popup.g-popup_open') : this.locators.editor;
96+
const button = root.getByLabel(label);
97+
await this.expect(button).not.toHaveClass(/selected/);
98+
}
99+
80100
/**
81101
* Returns the current editor mode
82102
*/
@@ -197,10 +217,13 @@ export class MarkdownEditorPage {
197217
}
198218

199219
/**
200-
* Clicks a toolbar button using its aria-label
220+
* Clicks a toolbar button using its aria-label.
221+
* @param label - The aria-label of the button.
222+
* @param inPopup - If true, only search within open popups; otherwise, search the main editor toolbar.
201223
*/
202-
async clickToolbarButton(label: string) {
203-
const button = this.getToolbarButton(label);
224+
async clickToolbarButton(label: string, inPopup = false) {
225+
const root = inPopup ? this.page.locator('.g-popup.g-popup_open') : this.locators.editor;
226+
const button = root.getByLabel(label);
204227

205228
await this.expect(button).toBeEnabled();
206229
await button.click();
@@ -221,6 +244,13 @@ export class MarkdownEditorPage {
221244
await this.locators.contenteditable.blur();
222245
}
223246

247+
/**
248+
* Add focus from the contenteditable area
249+
*/
250+
async focus() {
251+
await this.locators.contenteditable.focus();
252+
}
253+
224254
/**
225255
* Presses a key within the contenteditable area
226256
*/
@@ -236,16 +266,23 @@ export class MarkdownEditorPage {
236266
}
237267

238268
/**
239-
* Simulates input rule behavior by typing a sequence in WYSIWYG mode.
240-
* Clears the editor and types each character
269+
* Types the given sequence of characters and then presses Space to apply an input rule.
241270
*/
242271
async inputRule(sequence: string) {
243-
await this.switchMode('wysiwyg');
244-
await this.clearContent();
245272
await this.pressSequentially(sequence);
246273
await this.press('Space');
247274
}
248275

276+
/**
277+
* Switches to WYSIWYG mode, clears all content, then types the given sequence
278+
* and presses Space to apply an input rule.
279+
*/
280+
async inputRuleWithClear(sequence: string) {
281+
await this.switchMode('wysiwyg');
282+
await this.clearContent();
283+
await this.inputRule(sequence);
284+
}
285+
249286
/**
250287
* Pastes data into the contenteditable area
251288
*/

tests/playwright/templates/Extension.template.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ test.describe('%%name%%', () => {
5252
test.skip('should insert via input rule @wysiwyg', async ({editor, wait}) => {
5353
/* TODO: unskip */
5454
await editor.switchMode('wysiwyg');
55-
await editor.inputRule('/* TODO: input rule */');
55+
await editor.inputRuleWithClear('/* TODO: input rule */');
5656
await wait.timeout();
5757

5858
/* TODO: write test */
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import dd from 'ts-dedent';
2+
3+
import {expect, test} from 'playwright/core';
4+
5+
import {Playground} from './Playground.helpers';
6+
7+
test.describe('Bold', () => {
8+
test.beforeEach(async ({mount}) => {
9+
const initialMarkup = dd`
10+
some text
11+
`;
12+
13+
await mount(<Playground initial={initialMarkup} />);
14+
});
15+
16+
test.describe('mark', () => {
17+
test('should mark via toolbar @wysiwyg', async ({editor, wait}) => {
18+
await editor.switchMode('wysiwyg');
19+
await editor.assertToolbarButtonNotSelected('Bold');
20+
21+
await editor.focus();
22+
await editor.press('ArrowDown');
23+
await editor.press('Enter');
24+
25+
await editor.clickToolbarButton('Bold');
26+
await wait.timeout();
27+
28+
await editor.pressSequentially('next');
29+
await editor.assertToolbarButtonSelected('Bold');
30+
31+
await editor.press('ArrowUp');
32+
await editor.assertToolbarButtonNotSelected('Bold');
33+
});
34+
35+
test('should mark via input rule @wysiwyg', async ({editor, wait}) => {
36+
await editor.switchMode('wysiwyg');
37+
await editor.assertToolbarButtonNotSelected('Bold');
38+
39+
await editor.focus();
40+
await editor.press('ArrowDown');
41+
await editor.press('Enter');
42+
43+
await editor.inputRule('**next**');
44+
await wait.timeout();
45+
await editor.press('ArrowLeft');
46+
47+
await editor.assertToolbarButtonSelected('Bold');
48+
49+
await editor.press('ArrowUp');
50+
await editor.assertToolbarButtonNotSelected('Bold');
51+
});
52+
53+
//
54+
test.skip('should mark via keyboard shortcut @wysiwyg', async ({editor, wait}) => {
55+
// Skip: key combo fails in Docker for unknown reason
56+
await editor.switchMode('wysiwyg');
57+
await editor.assertToolbarButtonNotSelected('Bold');
58+
59+
await editor.focus();
60+
await editor.press('ArrowDown');
61+
await editor.press('Enter');
62+
63+
await editor.press('Control+B');
64+
await wait.timeout();
65+
66+
await editor.pressSequentially('next');
67+
await wait.timeout();
68+
69+
await editor.assertToolbarButtonSelected('Bold');
70+
71+
await editor.press('ArrowUp');
72+
await editor.assertToolbarButtonNotSelected('Bold');
73+
});
74+
75+
test('should mark via toolbar @markup', async ({editor, wait}) => {
76+
await editor.switchMode('markup');
77+
78+
await editor.focus();
79+
await editor.press('ArrowDown');
80+
await editor.press('Enter');
81+
82+
await editor.clickToolbarButton('Bold');
83+
await wait.timeout();
84+
await editor.pressSequentially('next');
85+
86+
await expect(editor.getByTextInContenteditable('**next**')).toBeVisible();
87+
});
88+
});
89+
90+
test.describe('mode switch', () => {
91+
test('should remain after mode switch @wysiwyg @markup', async ({editor, wait}) => {
92+
await editor.clearContent();
93+
94+
const markup = 'some text\n**next**';
95+
await editor.switchMode('markup');
96+
await editor.fill(markup);
97+
await wait.timeout();
98+
99+
await editor.switchMode('wysiwyg');
100+
101+
await editor.focus();
102+
await editor.press('ArrowDown');
103+
await wait.timeout();
104+
105+
await editor.assertToolbarButtonSelected('Bold');
106+
107+
await editor.press('ArrowUp');
108+
await editor.assertToolbarButtonNotSelected('Bold');
109+
110+
await editor.switchMode('markup');
111+
});
112+
});
113+
114+
test.describe('interaction', () => {
115+
test('should add mark to selected text via toolbar @wysiwyg', async ({editor, wait}) => {
116+
await editor.switchMode('wysiwyg');
117+
await editor.assertToolbarButtonNotSelected('Bold');
118+
119+
await editor.focus();
120+
await editor.press('ArrowDown');
121+
await editor.press('Enter');
122+
123+
await editor.pressSequentially('next');
124+
await wait.timeout();
125+
126+
await editor.selectTextIn('p:nth-child(2)');
127+
128+
await editor.assertToolbarButtonNotSelected('Bold');
129+
await editor.clickToolbarButton('Bold');
130+
await wait.timeout(300);
131+
132+
await editor.assertToolbarButtonSelected('Bold');
133+
await editor.press('ArrowUp');
134+
await wait.timeout();
135+
136+
await editor.assertToolbarButtonNotSelected('Bold');
137+
});
138+
139+
test('should add mark to selected text via context toolbar @wysiwyg', async ({
140+
editor,
141+
wait,
142+
}) => {
143+
await editor.switchMode('wysiwyg');
144+
await editor.assertToolbarButtonNotSelected('Bold');
145+
146+
await editor.focus();
147+
await editor.press('ArrowDown');
148+
await editor.press('Enter');
149+
150+
await editor.pressSequentially('next');
151+
await wait.timeout();
152+
153+
await editor.selectTextIn('p:nth-child(2)');
154+
155+
await editor.assertToolbarButtonNotSelected('Bold');
156+
await editor.assertToolbarButtonNotSelected('Bold', true);
157+
await editor.clickToolbarButton('Bold', true);
158+
await wait.timeout(300);
159+
160+
await editor.assertToolbarButtonSelected('Bold');
161+
await editor.press('ArrowUp');
162+
163+
await editor.assertToolbarButtonNotSelected('Bold');
164+
});
165+
166+
test('should delete mark to selected text via toolbar @wysiwyg', async ({editor, wait}) => {
167+
await editor.switchMode('wysiwyg');
168+
169+
await editor.focus();
170+
await editor.press('ArrowDown');
171+
await editor.press('Enter');
172+
173+
await editor.inputRule('**next**');
174+
await wait.timeout();
175+
176+
await editor.selectTextIn('p:nth-child(2)');
177+
await editor.assertToolbarButtonSelected('Bold');
178+
179+
await editor.clickToolbarButton('Bold');
180+
await wait.timeout();
181+
await editor.assertToolbarButtonNotSelected('Bold');
182+
});
183+
});
184+
});

tests/visual-tests/playground/Cut.visual.test.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,21 @@ test.describe('Cut', () => {
3838
await editor.switchMode('wysiwyg');
3939

4040
await editor.clickToolbarMoreActionButton();
41-
await editor.clickToolbarButton('Cut');
41+
await editor.clickToolbarButton('Cut', true);
4242

4343
await editor.clickToolbarMoreActionButton();
44-
45-
await editor.assertToolbarButtonDisabled('Cut');
44+
await editor.assertToolbarButtonDisabled('Cut', true);
4645

4746
await editor.press('ArrowDown');
4847
await wait.timeout();
4948
await editor.press('ArrowDown');
5049

51-
await editor.assertToolbarButtonDisabled('Cut');
50+
await editor.assertToolbarButtonDisabled('Cut', true);
5251

5352
await editor.press('Enter');
5453
await wait.timeout();
5554

56-
await editor.assertToolbarButtonEnabled('Cut');
55+
await editor.assertToolbarButtonEnabled('Cut', true);
5756
});
5857

5958
test('should insert via command menu @wysiwyg', async ({page, editor, actions, wait}) => {
@@ -83,7 +82,7 @@ test.describe('Cut', () => {
8382
});
8483

8584
test('should insert via input rule @wysiwyg', async ({editor, wait}) => {
86-
await editor.inputRule('{% cut');
85+
await editor.inputRuleWithClear('{% cut');
8786
await wait.timeout();
8887

8988
const cutBlock = editor.getByTextInContenteditable('Cut title').first();
@@ -105,7 +104,7 @@ test.describe('Cut', () => {
105104
await editor.clearContent();
106105

107106
await editor.clickToolbarMoreActionButton();
108-
await editor.clickToolbarButton('Cut');
107+
await editor.clickToolbarButton('Cut', true);
109108

110109
await expect(editor.getByTextInContenteditable('{% cut "title" %}')).toBeVisible();
111110
});

0 commit comments

Comments
 (0)