Skip to content

Commit 279f741

Browse files
committed
chore(138): make use of page selector
1 parent 8f1f65b commit 279f741

File tree

18 files changed

+252
-115
lines changed

18 files changed

+252
-115
lines changed

packages/editor-website/e2e/page.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ test('Page has correct title', async ({ page }) => {
66
await expect(page).toHaveTitle('Rich-text Editor - NL Design System');
77
await page.getByRole('paragraph').filter({ hasText: /^$/ }).fill('Paragraaf.');
88
await page.getByTestId('clippy-validations-gutter').getByRole('listitem').nth(1).click();
9+
await page.waitForTimeout(500);
910
await page.getByRole('button', { name: 'Aanpassen' }).first().click();
1011
await page.waitForTimeout(500);
1112
await page.keyboard.type(' heading');

packages/editor-website/playwright.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default defineConfig({
1919
},
2020
],
2121
reporter: [['html', { outputFolder: 'tmp/playwright-report' }]],
22+
retries: 1,
2223
testDir: './e2e',
2324
use: {
2425
baseURL: 'http://localhost:5174',

packages/editor/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"preview": "vite preview",
3030
"pretest": "pnpm exec playwright install --with-deps",
3131
"test": "pnpm run --recursive '/^test:.+$/'",
32-
"test:vitest": "vitest",
32+
"test:vitest": "vitest --run",
3333
"test-coverage": "vitest run --coverage"
3434
},
3535
"dependencies": {
@@ -55,7 +55,7 @@
5555
"@tiptap/extension-underline": "3.11.1",
5656
"@tiptap/extensions": "3.11.1",
5757
"@tiptap/pm": "3.11.1",
58-
"@utrecht/web-component-library-stencil": "3.7.0",
58+
"@utrecht/web-component-library-stencil": "4.0.0",
5959
"lit": "3.3.1",
6060
"prosemirror-model": "1.25.4"
6161
},
Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,68 @@
1-
// typescript
21
import './index.ts';
3-
import userEvent from '@testing-library/user-event';
4-
import { querySelectorDeep } from 'query-selector-shadow-dom';
52
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
3+
import { page, userEvent } from 'vitest/browser';
64
import type { Toolbar } from './index';
5+
import { getDeeplyMountedCustomElement } from '../../../test/helpers';
6+
import { cleanupTestEditor, EditorTestSetup, setupTestEditor } from '../../../test/setupTestEditor';
7+
8+
const tag = 'clippy-toolbar';
79

810
describe('<clippy-toolbar>', () => {
9-
let container: HTMLElement;
11+
let testSetup: EditorTestSetup;
1012
let user: ReturnType<typeof userEvent.setup>;
11-
let element: Toolbar;
1213

1314
beforeEach(async () => {
14-
container = document.createElement('div');
15-
document.body.appendChild(container);
1615
user = userEvent.setup();
16+
testSetup = setupTestEditor(`<${tag}></{tag}>`);
1717

18-
container.innerHTML = '<clippy-toolbar></clippy-toolbar>';
19-
element = container.querySelector<Toolbar>('clippy-toolbar')!;
20-
await element.updateComplete;
18+
await getDeeplyMountedCustomElement<Toolbar>(tag);
2119
});
2220

2321
afterEach(() => {
24-
document.body.removeChild(container);
22+
cleanupTestEditor(testSetup.container);
2523
});
2624

2725
it('renders correctly with required toolbar elements', async () => {
28-
expect(element).toBeDefined();
29-
expect(querySelectorDeep('div[aria-label="Werkbalk tekstbewerker"]')).toBeDefined();
30-
expect(querySelectorDeep('button[aria-label="Bold"]')).toBeDefined();
31-
expect(querySelectorDeep('button[aria-label="Italic"]')).toBeDefined();
32-
const linkButton = querySelectorDeep('clippy-toolbar-link');
33-
await linkButton?.updateComplete;
34-
expect(querySelectorDeep('button[aria-label="Link"]')).toBeDefined();
26+
expect(page.getByLabelText('Werkbalk tekstbewerker')).toBeInTheDocument();
27+
expect(page.getByLabelText('Bold')).toBeInTheDocument();
28+
expect(page.getByLabelText('Italic')).toBeInTheDocument();
29+
expect(page.getByLabelText('Link', { exact: true })).toBeInTheDocument();
3530
});
3631

3732
it('updates all toolbar buttons when editor content changes', async () => {
38-
const boldButton = querySelectorDeep('button[aria-label="Bold"]');
39-
expect(boldButton?.getAttribute('aria-pressed')).toBe('false');
33+
expect(page.getByLabelText('Bold')).toHaveAttribute('aria-pressed', 'false');
4034
});
4135

4236
it('opens shortcuts dialog when keyboard shortcuts button is clicked', async () => {
43-
const shortcutsButton = querySelectorDeep('clippy-shortcuts');
44-
await shortcutsButton?.updateComplete;
37+
expect(page.getByTestId('clippy-shortcuts-dialog')).not.toHaveAttribute('open');
4538

46-
const shortcutsButton2 = querySelectorDeep('button[aria-label="Keyboard shortcuts"]') as HTMLButtonElement;
47-
await user.click(shortcutsButton2);
48-
await shortcutsButton?.updateComplete;
49-
const dialog = querySelectorDeep('dialog#clippy-shortcuts') as HTMLDialogElement;
50-
const title = dialog.querySelector('#clippy-shortcuts-title');
51-
expect(title?.innerHTML).toBe('Sneltoetsen');
39+
const button = page.getByRole('button', { name: 'Keyboard shortcuts' });
40+
await button.click();
41+
expect(page.getByTestId('clippy-shortcuts-dialog')).toHaveAttribute('open');
5242
});
5343

5444
it('changes link URL when link is edited', async () => {
55-
const linkWC = querySelectorDeep('clippy-toolbar-link');
56-
await linkWC?.updateComplete;
57-
const linkButton = querySelectorDeep('button[aria-label="Link"]') as HTMLButtonElement;
45+
const linkButton = page.getByLabelText('Link', { exact: true });
5846
await user.click(linkButton);
5947

60-
const linkElement = querySelectorDeep('clippy-toolbar-link');
61-
await linkElement?.updateComplete;
48+
expect(page.getByTestId('clippy-link-dialog')).toHaveAttribute('open');
49+
const urlInput = page.getByLabelText('Link to:');
50+
await user.type(urlInput, 'https://example.com');
6251

63-
const dialog = querySelectorDeep('dialog#clippy-link-dialog') as HTMLDialogElement;
64-
expect(dialog).toHaveAttribute('open');
52+
await user.keyboard('{Enter}');
53+
expect(page.getByLabelText('Link to:')).toHaveValue('https://example.com');
54+
await user.click(page.getByRole('button', { name: 'Link toevoegen' }));
55+
expect(page.getByTestId('clippy-link-dialog')).not.toHaveAttribute('open');
6556
});
6657

6758
it('adds an image when image upload is completed', async () => {
68-
const imageUploadElement = querySelectorDeep('clippy-toolbar-image-upload');
69-
await imageUploadElement?.updateComplete;
59+
const button = page.getByRole('button', { name: 'Afbeelding' });
60+
await button.click();
61+
expect(page.getByTestId('clippy-image-upload-dialog')).not.toHaveAttribute('open');
62+
const fileInput = page.getByTestId('clippy-image-upload');
7063

71-
const dialog = querySelectorDeep('dialog#clippy-image-upload-dialog') as HTMLDialogElement;
72-
dialog?.showModal();
73-
await imageUploadElement?.updateComplete;
64+
await userEvent.upload(fileInput, new File(['(⌐□_□)'], 'clippy.png', { type: 'image/png' }));
7465

75-
expect(dialog).toHaveAttribute('open');
66+
expect(page.getByRole('listitem').getByRole('img')).toHaveAttribute('alt', 'clippy.png');
7667
});
7768
});

packages/editor/src/components/toolbar/shortcuts-dialog/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { customElement, property } from 'lit/decorators.js';
44
import { ref, type Ref } from 'lit/directives/ref.js';
55
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
66
import shortcutsDialogStyles from './styles.ts';
7+
import './../toolbar-button';
78

89
@customElement('clippy-shortcuts')
910
export class ShortcutsDialog extends LitElement {
@@ -23,6 +24,7 @@ export class ShortcutsDialog extends LitElement {
2324
id="clippy-shortcuts"
2425
class="clippy-shortcuts__dialog"
2526
aria-labelledby="clippy-shortcuts-title"
27+
data-testid="clippy-shortcuts-dialog"
2628
${ref(this.dialogRef)}
2729
>
2830
<div class="clippy-shortcuts__header">

packages/editor/src/components/toolbar/toolbar-format-select/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ export class FormatSelect extends LitElement {
7171

7272
override render() {
7373
return html`
74-
<select class="clippy-toolbar-button" @change=${this.#handleTextFormatChange}>
74+
<select
75+
class="clippy-toolbar-button"
76+
@change=${this.#handleTextFormatChange}
77+
aria-label="Tekst formaat selecteren"
78+
>
7579
${map(
7680
this.options,
7781
(option) => html`<option ?selected=${option.active} value=${option.value}>${option.label}</option>`,

packages/editor/src/components/toolbar/toolbar-image-upload/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,13 @@ export class ToolbarImageUpload extends LitElement {
9898
class="clippy-toolbar-image-upload--button"
9999
@click=${this.#toggleImageUploadDialog}
100100
aria-controls="clippy-image-upload-dialog"
101+
label="Afbeelding"
101102
>
102103
${unsafeSVG(PhotoIcon)}
103104
<input
104105
${ref(this.#inputRef)}
105106
type="file"
106-
id="clippy-image-upload"
107+
data-testid="clippy-image-upload"
107108
style="display:none"
108109
accept="image/*"
109110
multiple
@@ -115,6 +116,7 @@ export class ToolbarImageUpload extends LitElement {
115116
id="clippy-image-upload-dialog"
116117
class="clippy-toolbar-image-upload--dialog"
117118
${ref(this.#dialogRef)}
119+
data-testid="clippy-image-upload-dialog"
118120
>
119121
${map(
120122
this.files,

packages/editor/src/components/toolbar/toolbar-link/index.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@ import { customElement, state } from 'lit/decorators.js';
55
import { createRef, ref, type Ref } from 'lit/directives/ref.js';
66
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
77
import { editor } from '@/decorators/TipTapDecorator.ts';
8+
import './../toolbar-button';
89

9-
@customElement('clippy-toolbar-link')
10+
const tag = 'clippy-toolbar-link';
11+
12+
declare global {
13+
interface HTMLElementTagNameMap {
14+
[tag]: ToolbarLink;
15+
}
16+
}
17+
18+
@customElement(tag)
1019
export class ToolbarLink extends LitElement {
1120
static override readonly styles = [];
1221

@@ -58,24 +67,16 @@ export class ToolbarLink extends LitElement {
5867
>
5968
${unsafeSVG(LinkIcon)}
6069
</clippy-toolbar-button>
61-
<dialog closedby="any" id="clippy-link-dialog" class="link--dialog" ${ref(this.#dialogRef)}>
70+
<dialog closedby="any" class="link--dialog" ${ref(this.#dialogRef)} data-testid="clippy-link-dialog">
6271
<div>
6372
<label>Link to:<input value=${this.previousUrl} ${ref(this.#inputRef)} type="text" /></label>
6473
</div>
65-
<utrecht-button-group>
66-
<utrecht-button @click=${() => this.#dialogRef.value?.close()}>Sluiten</utrecht-button>
67-
<utrecht-button @click=${this.#unsetLink}>Verwijder link</utrecht-button>
68-
<utrecht-button appearance="secondary-action-button" @click=${this.#updateLink}
69-
>Link toevoegen</utrecht-button
70-
>
71-
</utrecht-button-group>
74+
<div>
75+
<button @click=${() => this.#dialogRef.value?.close()}>Sluiten</button>
76+
<button @click=${this.#unsetLink}>Verwijder link</button>
77+
<button @click=${this.#updateLink}>Link toevoegen</button>
78+
</div>
7279
</dialog>
7380
`;
7481
}
7582
}
76-
77-
declare global {
78-
interface HTMLElementTagNameMap {
79-
'clippy-toolbar-link': ToolbarLink;
80-
}
81-
}

packages/editor/src/components/validations/drawer/index.test.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import './index.ts';
22
import type { Editor } from '@tiptap/core';
3-
import { querySelectorDeep } from 'query-selector-shadow-dom';
43
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
4+
import { page } from 'vitest/browser';
55
import { createTestEditor } from '../../../../test/createTestEditor';
66
import { CustomEvents } from '../../../events';
77
import { ValidationResult } from '../../../types/validation';
@@ -30,9 +30,8 @@ describe('<clippy-validations-dialog>', () => {
3030
element.editor = editor;
3131
await element.updateComplete;
3232
}
33-
34-
const dialog = querySelectorDeep('dialog#dialog-content') as HTMLDialogElement;
35-
expect(dialog?.getAttribute('open')).toBeNull();
33+
const dialog = page.getByTestId('clippy-validations-drawer');
34+
expect(dialog).not.toHaveAttribute('open');
3635

3736
globalThis.dispatchEvent(new CustomEvent(CustomEvents.OPEN_VALIDATIONS_DIALOG));
3837
await element?.updateComplete;
@@ -93,8 +92,7 @@ describe('<clippy-validations-dialog>', () => {
9392
await element.updateComplete;
9493
}
9594

96-
const validationList = querySelectorDeep('ul#validation-list');
97-
expect(validationList).toBeDefined();
95+
const validationList = page.getByTestId('clippy-validations-list').element();
9896

9997
const validationItems = validationList?.querySelectorAll('clippy-validation-list-item');
10098
expect(validationItems?.length).toBe(10);

packages/editor/src/components/validations/drawer/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,11 @@ export class ValidationsDialog extends LitElement {
216216
return html`
217217
<dialog
218218
${ref(this.#dialogRef)}
219-
id="dialog-content"
219+
data-testid="clippy-validations-drawer"
220220
class="clippy-dialog__content"
221221
aria-label="Toegankelijkheidsfouten"
222222
>
223-
<ul class="clippy-dialog__list" id="validation-list">
223+
<ul class="clippy-dialog__list" data-testid="clippy-validations-list">
224224
${size > 0
225225
? map(sortedValidations, ([key, { pos, severity, tipPayload }]) => {
226226
const validationKey = key.split('_')[0] as ValidationKey;

0 commit comments

Comments
 (0)