Skip to content

Commit df3760f

Browse files
committed
add tests for CodeEditor markdown toolbar using the appearance config
1 parent 4b7c29c commit df3760f

2 files changed

Lines changed: 269 additions & 0 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import React from "react";
2+
import { fireEvent, render, screen } from "@testing-library/react";
3+
4+
import "@testing-library/jest-dom";
5+
6+
import { CLASSPREFIX as eccgui } from "../../../configuration/constants";
7+
import { CodeEditor } from "../CodeMirror";
8+
9+
const contextOverlayClass = `${eccgui}-contextoverlay`;
10+
11+
const setupDocumentRange = () => {
12+
document.createRange = () => {
13+
const range = new Range();
14+
range.getBoundingClientRect = jest.fn();
15+
range.getClientRects = () => ({
16+
item: () => null,
17+
length: 0,
18+
[Symbol.iterator]: jest.fn(),
19+
});
20+
return range;
21+
};
22+
};
23+
24+
describe("CodeEditor - markdown mode with toolbar", () => {
25+
beforeAll(() => {
26+
setupDocumentRange();
27+
});
28+
29+
// The toolbar contains a Paragraphs ContextMenu first, then the EditorAppearanceConfigMenu last.
30+
const getConfigMenuOverlay = (container: HTMLElement) => {
31+
const overlays = container.getElementsByClassName(contextOverlayClass);
32+
return overlays[overlays.length - 1] as HTMLElement;
33+
};
34+
35+
it("renders toolbar when mode is markdown and useToolbar is true", () => {
36+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={true} />);
37+
expect(container.querySelector(`.${eccgui}-codeeditor__toolbar`)).not.toBeNull();
38+
});
39+
40+
it("does not render toolbar when useToolbar is false", () => {
41+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={false} />);
42+
expect(container.querySelector(`.${eccgui}-codeeditor__toolbar`)).toBeNull();
43+
});
44+
45+
it("does not render toolbar for non-markdown modes even when useToolbar is true", () => {
46+
const { container } = render(<CodeEditor name="test-editor" mode="yaml" useToolbar={true} />);
47+
expect(container.querySelector(`.${eccgui}-codeeditor__toolbar`)).toBeNull();
48+
});
49+
50+
it("includes the EditorAppearanceConfigMenu in the markdown toolbar", () => {
51+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={true} />);
52+
const toolbar = container.querySelector(`.${eccgui}-codeeditor__toolbar`);
53+
// Toolbar contains at least the Paragraphs menu and the EditorAppearanceConfigMenu
54+
expect(toolbar?.getElementsByClassName(contextOverlayClass).length).toBeGreaterThanOrEqual(2);
55+
});
56+
57+
it("defaults wrapLines to true in markdown mode with toolbar", async () => {
58+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={true} />);
59+
60+
fireEvent.click(getConfigMenuOverlay(container));
61+
62+
const wrapLinesItem = await screen.findByText("wrapLines");
63+
expect(wrapLinesItem.closest("[aria-selected='true']")).not.toBeNull();
64+
});
65+
66+
it("defaults preventLineNumbers to true in markdown mode with toolbar", async () => {
67+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={true} />);
68+
69+
fireEvent.click(getConfigMenuOverlay(container));
70+
71+
const preventLineNumbersItem = await screen.findByText("preventLineNumbers");
72+
expect(preventLineNumbersItem.closest("[aria-selected='true']")).not.toBeNull();
73+
});
74+
75+
it("locks wrapLines in config menu when wrapLines prop is explicitly provided", async () => {
76+
const { container } = render(
77+
<CodeEditor name="test-editor" mode="markdown" useToolbar={true} wrapLines={false} />
78+
);
79+
80+
fireEvent.click(getConfigMenuOverlay(container));
81+
82+
const wrapLinesItem = await screen.findByText("wrapLines");
83+
expect(wrapLinesItem.closest("[aria-disabled='true']")).not.toBeNull();
84+
});
85+
86+
it("locks preventLineNumbers in config menu when preventLineNumbers prop is explicitly provided", async () => {
87+
const { container } = render(
88+
<CodeEditor name="test-editor" mode="markdown" useToolbar={true} preventLineNumbers={false} />
89+
);
90+
91+
fireEvent.click(getConfigMenuOverlay(container));
92+
93+
const preventLineNumbersItem = await screen.findByText("preventLineNumbers");
94+
expect(preventLineNumbersItem.closest("[aria-disabled='true']")).not.toBeNull();
95+
});
96+
97+
it("does not lock wrapLines in config menu when wrapLines prop is not provided", async () => {
98+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={true} />);
99+
100+
fireEvent.click(getConfigMenuOverlay(container));
101+
102+
const wrapLinesItem = await screen.findByText("wrapLines");
103+
expect(wrapLinesItem.closest("[aria-disabled='true']")).toBeNull();
104+
});
105+
106+
it("does not lock preventLineNumbers in config menu when preventLineNumbers prop is not provided", async () => {
107+
const { container } = render(<CodeEditor name="test-editor" mode="markdown" useToolbar={true} />);
108+
109+
fireEvent.click(getConfigMenuOverlay(container));
110+
111+
const preventLineNumbersItem = await screen.findByText("preventLineNumbers");
112+
expect(preventLineNumbersItem.closest("[aria-disabled='true']")).toBeNull();
113+
});
114+
115+
it("disables config menu trigger when both wrapLines and preventLineNumbers props are provided", () => {
116+
const { container } = render(
117+
<CodeEditor
118+
name="test-editor"
119+
mode="markdown"
120+
useToolbar={true}
121+
wrapLines={true}
122+
preventLineNumbers={true}
123+
/>
124+
);
125+
126+
const configMenuTrigger = getConfigMenuOverlay(container).querySelector("button");
127+
expect(configMenuTrigger).toBeDisabled();
128+
});
129+
130+
it("disables config menu trigger when editor is disabled", () => {
131+
const { container } = render(
132+
<CodeEditor name="test-editor" mode="markdown" useToolbar={true} disabled={true} />
133+
);
134+
135+
const configMenuTrigger = getConfigMenuOverlay(container).querySelector("button");
136+
expect(configMenuTrigger).toBeDisabled();
137+
});
138+
});
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import React from "react";
2+
import { fireEvent, render, screen } from "@testing-library/react";
3+
4+
import "@testing-library/jest-dom";
5+
6+
import { CLASSPREFIX as eccgui } from "../../../configuration/constants";
7+
import { EditorAppearanceConfigMenu } from "../toolbars/EditorAppearanceConfigMenu";
8+
9+
const contextOverlayClass = `${eccgui}-contextoverlay`;
10+
11+
describe("EditorAppearanceConfigMenu", () => {
12+
it("renders menu items for each config property, using key as fallback label", async () => {
13+
const config = { wrapLines: true, preventLineNumbers: false };
14+
const setConfig = jest.fn();
15+
16+
const { container } = render(<EditorAppearanceConfigMenu config={config} setConfig={setConfig} />);
17+
18+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
19+
20+
expect(await screen.findByText("wrapLines")).toBeVisible();
21+
expect(await screen.findByText("preventLineNumbers")).toBeVisible();
22+
});
23+
24+
it("uses configPropertyTranslate for menu item labels", async () => {
25+
const config = { wrapLines: true, preventLineNumbers: false };
26+
const setConfig = jest.fn();
27+
const translate = (key: string) => `Label_${key}` as string | false;
28+
29+
const { container } = render(
30+
<EditorAppearanceConfigMenu config={config} setConfig={setConfig} configPropertyTranslate={translate} />
31+
);
32+
33+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
34+
35+
expect(await screen.findByText("Label_wrapLines")).toBeVisible();
36+
expect(await screen.findByText("Label_preventLineNumbers")).toBeVisible();
37+
});
38+
39+
it("calls setConfig with the toggled value when a menu item is clicked", async () => {
40+
const config = { wrapLines: true, preventLineNumbers: false };
41+
const setConfig = jest.fn();
42+
43+
const { container } = render(<EditorAppearanceConfigMenu config={config} setConfig={setConfig} />);
44+
45+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
46+
const wrapLinesItem = await screen.findByText("wrapLines");
47+
fireEvent.click(wrapLinesItem);
48+
49+
expect(setConfig).toHaveBeenCalledWith({ wrapLines: false, preventLineNumbers: false });
50+
});
51+
52+
it("menu trigger is disabled when all config properties are locked", () => {
53+
const config = { wrapLines: true, preventLineNumbers: false };
54+
const configLocked = { wrapLines: true, preventLineNumbers: true };
55+
const setConfig = jest.fn();
56+
57+
const { container } = render(
58+
<EditorAppearanceConfigMenu config={config} configLocked={configLocked} setConfig={setConfig} />
59+
);
60+
61+
const trigger = container.getElementsByClassName(contextOverlayClass)[0].querySelector("button");
62+
expect(trigger).toBeDisabled();
63+
});
64+
65+
it("menu trigger is enabled when not all config properties are locked", () => {
66+
const config = { wrapLines: true, preventLineNumbers: false };
67+
const configLocked = { wrapLines: true }; // only one locked
68+
const setConfig = jest.fn();
69+
70+
const { container } = render(
71+
<EditorAppearanceConfigMenu config={config} configLocked={configLocked} setConfig={setConfig} />
72+
);
73+
74+
const trigger = container.getElementsByClassName(contextOverlayClass)[0].querySelector("button");
75+
expect(trigger).not.toBeDisabled();
76+
});
77+
78+
it("locked config property has a disabled menu item", async () => {
79+
const config = { wrapLines: true, preventLineNumbers: false };
80+
const configLocked = { wrapLines: true };
81+
const setConfig = jest.fn();
82+
83+
const { container } = render(
84+
<EditorAppearanceConfigMenu config={config} configLocked={configLocked} setConfig={setConfig} />
85+
);
86+
87+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
88+
89+
const wrapLinesItem = await screen.findByText("wrapLines");
90+
expect(wrapLinesItem.closest("[aria-disabled='true']")).not.toBeNull();
91+
});
92+
93+
it("unlocked config property has an enabled menu item", async () => {
94+
const config = { wrapLines: true, preventLineNumbers: false };
95+
const configLocked = { wrapLines: true }; // only wrapLines is locked
96+
const setConfig = jest.fn();
97+
98+
const { container } = render(
99+
<EditorAppearanceConfigMenu config={config} configLocked={configLocked} setConfig={setConfig} />
100+
);
101+
102+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
103+
104+
const preventLineNumbersItem = await screen.findByText("preventLineNumbers");
105+
expect(preventLineNumbersItem.closest("[aria-disabled='true']")).toBeNull();
106+
});
107+
108+
it("selected config property is marked as selected in the menu", async () => {
109+
const config = { wrapLines: true, preventLineNumbers: false };
110+
const setConfig = jest.fn();
111+
112+
const { container } = render(<EditorAppearanceConfigMenu config={config} setConfig={setConfig} />);
113+
114+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
115+
116+
const wrapLinesItem = await screen.findByText("wrapLines");
117+
expect(wrapLinesItem.closest("[aria-selected='true']")).not.toBeNull();
118+
});
119+
120+
it("unselected config property is not marked as selected in the menu", async () => {
121+
const config = { wrapLines: true, preventLineNumbers: false };
122+
const setConfig = jest.fn();
123+
124+
const { container } = render(<EditorAppearanceConfigMenu config={config} setConfig={setConfig} />);
125+
126+
fireEvent.click(container.getElementsByClassName(contextOverlayClass)[0]);
127+
128+
const preventLineNumbersItem = await screen.findByText("preventLineNumbers");
129+
expect(preventLineNumbersItem.closest("[aria-selected='true']")).toBeNull();
130+
});
131+
});

0 commit comments

Comments
 (0)