Skip to content

Commit 67d9a11

Browse files
authored
test(core): added actions, updated editor and wait methods (#720)
1 parent 253c0b5 commit 67d9a11

File tree

21 files changed

+284
-91
lines changed

21 files changed

+284
-91
lines changed

docs/how-to-add-visual-test.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ See:
106106
tests/playwright/core/helpers.ts
107107
```
108108

109+
### Locator Best Practices
110+
When writing tests targeting the editor’s view layer and plugins in the WYSIWYG mode, follow these guidelines for reliable locator usage:
111+
112+
1. Use `editor.fill`, `editor.press`, or `editor.pressSequentially` to emulate user input within the `contenteditable` region.
113+
2. Prefer locating elements via `data-qa` attributes when working with React-based components (e.g., plugins that define node views via React).
114+
3. When targeting elements rendered without React (e.g., via `toDOM` in a ProseMirror node spec), use class names or specific attributes instead.
115+
109116
## Clipboard specifics
110117

111118
Playwright clipboard support varies across browsers. Keep this in mind when writing copy-paste tests.

src/bundle/MarkupEditorComponent.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import {useEffect, useLayoutEffect, useRef} from 'react';
22

3+
import type {QAProps} from '@gravity-ui/uikit';
4+
35
import type {EditorInt} from './Editor';
46

5-
export type MarkupEditorComponentProps = {
7+
export type MarkupEditorComponentProps = QAProps & {
68
editor: EditorInt;
79
autofocus?: boolean;
810
className?: string;
911
children?: React.ReactNode;
1012
};
1113

1214
export const MarkupEditorComponent: React.FC<MarkupEditorComponentProps> =
13-
function MarkupEditorComponent({editor, autofocus, className, children}) {
15+
function MarkupEditorComponent({editor, autofocus, className, children, qa}) {
1416
const ref = useRef<HTMLDivElement>(null);
1517

1618
// insert editor to dom
@@ -35,6 +37,7 @@ export const MarkupEditorComponent: React.FC<MarkupEditorComponentProps> =
3537
return (
3638
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
3739
<div
40+
data-qa={qa}
3841
ref={ref}
3942
className={className}
4043
onClick={(event) => {

src/bundle/MarkupEditorView.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const MarkupEditorView = memo<MarkupEditorViewProps>((props) => {
7171
}}
7272
>
7373
<ToolbarView
74+
qa="g-md-toolbar"
7475
editor={editor}
7576
editorMode="markup"
7677
toolbarEditor={editor}

src/bundle/ToolbarView.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {useLayoutEffect, useRef} from 'react';
22

3+
import type {QAProps} from '@gravity-ui/uikit';
34
import {useUpdate} from 'react-use';
45

56
import type {ClassNameProps} from '../classname';
@@ -11,17 +12,18 @@ import type {EditorInt} from './Editor';
1112
import {stickyCn} from './sticky';
1213
import type {MarkdownEditorMode} from './types';
1314

14-
export type ToolbarViewProps<T> = ClassNameProps & {
15-
editor: EditorInt;
16-
editorMode: MarkdownEditorMode;
17-
toolbarEditor: T;
18-
toolbarFocus: () => void;
19-
toolbarConfig: ToolbarData<T>;
20-
settingsVisible?: boolean;
21-
hiddenActionsConfig?: ToolbarItemData<T>[];
22-
children?: React.ReactNode;
23-
stickyToolbar: boolean;
24-
};
15+
export type ToolbarViewProps<T> = ClassNameProps &
16+
QAProps & {
17+
editor: EditorInt;
18+
editorMode: MarkdownEditorMode;
19+
toolbarEditor: T;
20+
toolbarFocus: () => void;
21+
toolbarConfig: ToolbarData<T>;
22+
settingsVisible?: boolean;
23+
hiddenActionsConfig?: ToolbarItemData<T>[];
24+
children?: React.ReactNode;
25+
stickyToolbar: boolean;
26+
};
2527

2628
export function ToolbarView<T>({
2729
editor,
@@ -34,6 +36,7 @@ export function ToolbarView<T>({
3436
className,
3537
children,
3638
stickyToolbar,
39+
qa,
3740
}: ToolbarViewProps<T>) {
3841
const rerender = useUpdate();
3942
useLayoutEffect(() => {
@@ -48,6 +51,7 @@ export function ToolbarView<T>({
4851

4952
return (
5053
<div
54+
data-qa={qa}
5155
ref={wrapperRef}
5256
className={stickyCn.toolbar(
5357
{

src/bundle/WysiwygEditorComponent.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import {useEffect, useRef} from 'react';
22

3+
import type {QAProps} from '@gravity-ui/uikit';
4+
35
import type {EditorInt} from './Editor';
46

5-
export type WysiwygEditorComponentProps = {
7+
export type WysiwygEditorComponentProps = QAProps & {
68
className?: string;
79
autofocus?: boolean;
810
editor: EditorInt;
911
children?: React.ReactNode;
1012
};
1113

1214
export const WysiwygEditorComponent: React.FC<WysiwygEditorComponentProps> =
13-
function WysiwygEditorComponent({className, autofocus, editor, children}) {
15+
function WysiwygEditorComponent({className, autofocus, editor, children, qa}) {
1416
const ref = useRef<HTMLDivElement>(null);
1517
useEffect(() => {
1618
const {current} = ref;
@@ -23,7 +25,7 @@ export const WysiwygEditorComponent: React.FC<WysiwygEditorComponentProps> =
2325
}, [editor]);
2426

2527
return (
26-
<div ref={ref} className={className}>
28+
<div data-qa={qa} ref={ref} className={className}>
2729
{children}
2830
</div>
2931
);

src/bundle/WysiwygEditorView.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const WysiwygEditorView = memo<WysiwygEditorViewProps>((props) => {
5959
<div className={b({toolbar: toolbarVisible}, [className])} data-qa={qa}>
6060
{toolbarVisible ? (
6161
<ToolbarView
62+
qa="g-md-toolbar"
6263
editor={editor}
6364
editorMode="wysiwyg"
6465
toolbarEditor={editor}

src/bundle/settings/index.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
Menu,
1313
Popup,
1414
type PopupPlacement,
15+
type QAProps,
1516
} from '@gravity-ui/uikit';
1617

1718
import {type ClassNameProps, cn} from '../../classname';
@@ -89,6 +90,7 @@ export const EditorSettings = memo<EditorSettingsProps>(function EditorSettings(
8990
>
9091
<SettingsContent
9192
{...props}
93+
qa="g-md-settings-content"
9294
onClose={hidePopup}
9395
className={bSettings('content')}
9496
/>
@@ -99,18 +101,19 @@ export const EditorSettings = memo<EditorSettingsProps>(function EditorSettings(
99101
);
100102
});
101103

102-
type SettingsContentProps = ClassNameProps & {
103-
mode: MarkdownEditorMode;
104-
onClose: () => void;
105-
onModeChange: (mode: MarkdownEditorMode) => void;
106-
onShowPreviewChange: (showPreview: boolean) => void;
107-
showPreview: boolean;
108-
toolbarVisibility: boolean;
109-
onToolbarVisibilityChange: (val: boolean) => void;
110-
splitMode?: MarkdownEditorSplitMode;
111-
splitModeEnabled?: boolean;
112-
onSplitModeChange?: (splitModeEnabled: boolean) => void;
113-
};
104+
type SettingsContentProps = ClassNameProps &
105+
QAProps & {
106+
mode: MarkdownEditorMode;
107+
onClose: () => void;
108+
onModeChange: (mode: MarkdownEditorMode) => void;
109+
onShowPreviewChange: (showPreview: boolean) => void;
110+
showPreview: boolean;
111+
toolbarVisibility: boolean;
112+
onToolbarVisibilityChange: (val: boolean) => void;
113+
splitMode?: MarkdownEditorSplitMode;
114+
splitModeEnabled?: boolean;
115+
onSplitModeChange?: (splitModeEnabled: boolean) => void;
116+
};
114117

115118
const mdHelpPlacement: PopupPlacement = ['bottom', 'bottom-end', 'right-start', 'right', 'left'];
116119

@@ -125,9 +128,10 @@ const SettingsContent: React.FC<SettingsContentProps> = function SettingsContent
125128
splitModeEnabled,
126129
className,
127130
showPreview,
131+
qa,
128132
}) {
129133
return (
130-
<div className={bContent(null, [className])} data-qa="g-md-settings-content">
134+
<div className={bContent(null, [className])} data-qa={qa}>
131135
<Menu size="l" className={bContent('mode')}>
132136
<Menu.Item
133137
qa="g-md-settings-mode-wysiwyg"

src/extensions/behavior/CommandMenu/component.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ export const CommandMenuComponent: React.FC<CommandMenuComponentProps> = ({
4343
if (!anchorElement) return null;
4444

4545
return (
46-
<Popup open anchorElement={anchorElement} placement={placement} onOpenChange={onOpenChange}>
46+
<Popup
47+
open
48+
anchorElement={anchorElement}
49+
placement={placement}
50+
onOpenChange={onOpenChange}
51+
qa="g-md-command-menu"
52+
>
4753
<div className={b()}>
4854
<List<CommandMenuItem>
4955
virtualized

src/extensions/markdown/Link/PlaceholderWidget/widget.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ export const LinkPlaceholderWidget: React.FC<LinkPlaceholderWidgetProps> = ({
2626
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
2727
<a href="#">{i18n('link')}</a>
2828
</span>
29-
<Popup open modal onOpenChange={onCancel} anchorElement={anchor} placement={placement}>
29+
<Popup
30+
qa="g-md-link-form"
31+
open
32+
modal
33+
onOpenChange={onCancel}
34+
anchorElement={anchor}
35+
placement={placement}
36+
>
3037
<LinkForm autoFocus onSubmit={onSubmit} onCancel={onCancel} />
3138
</Popup>
3239
</>

src/toolbar/FlexToolbar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function FlexToolbar<E>(props: FlexToolbarProps<E>) {
6363
<Toolbar {...props} data={items} className={b('bar')} />
6464
{dots?.length && (
6565
<ToolbarListButton
66+
qa="g-md-toolbar-more-action"
6667
data={dots}
6768
icon={{data: Ellipsis}}
6869
title={props.dotsTitle}

0 commit comments

Comments
 (0)