Skip to content

Commit 477cc57

Browse files
committed
test(mdviewer): add checkbox task list sync integration tests
Add md-editor-edit-integ-test.js with tests for checkbox toggle syncing to CM source ([x] ↔ [ ]) and checkboxes being enabled in edit mode / disabled in reader mode. Add __clickCheckboxForTest helper in bridge.js for reliable checkbox interaction from tests. Add checkbox-test.md fixture file.
1 parent 3887446 commit 477cc57

5 files changed

Lines changed: 272 additions & 10 deletions

File tree

src-mdviewer/src/bridge.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ export function initBridge() {
128128
content.dispatchEvent(new Event("input", { bubbles: true }));
129129
}
130130
};
131+
window.__clickCheckboxForTest = function (index) {
132+
const content = document.getElementById("viewer-content");
133+
if (!content) { return false; }
134+
const checkboxes = content.querySelectorAll('input[type="checkbox"]');
135+
if (index >= checkboxes.length) { return false; }
136+
checkboxes[index].click();
137+
return checkboxes[index].checked;
138+
};
131139

132140
// Listen for messages from Phoenix parent
133141
window.addEventListener("message", (event) => {

src-mdviewer/to-create-tests.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
11
# Markdown Viewer/Editor — Integration Tests To Create
22

3-
## Checkbox (Task List) Sync
4-
- [ ] Clicking checkbox in edit mode toggles it and syncs to CM source ([x][ ])
5-
- [ ] Checkboxes enabled in edit mode, disabled in reader mode
6-
- [ ] Checkbox toggle creates an undo entry
7-
- [ ] Undo reverses checkbox toggle
8-
9-
## Empty Line Placeholder
10-
- [x] Empty paragraph in edit mode shows hint class
11-
- [x] Hint only shows in edit mode, not reader mode
12-
133
## Slash Menu (/ command)
144
- [x] Slash menu appears when typing / at start of line
155
- [x] Escape dismisses slash menu

test/UnitTestSuite.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ define(function (require, exports, module) {
111111
require("spec/LiveDevelopmentMultiBrowser-test");
112112
require("spec/LiveDevelopmentCustomServer-test");
113113
require("spec/md-editor-integ-test");
114+
require("spec/md-editor-edit-integ-test");
114115
require("spec/NewFileContentManager-test");
115116
require("spec/InstallExtensionDialog-integ-test");
116117
require("spec/ExtensionInstallation-test");
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Checkbox Test
2+
3+
## Task List
4+
5+
- [x] Completed task
6+
- [ ] Incomplete task
7+
- [ ] Another pending task
8+
9+
Some text after the task list.
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it
7+
* under the terms of the GNU Affero General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
14+
* for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
18+
*
19+
*/
20+
21+
/*global describe, beforeAll, afterAll, awaitsFor, it, awaitsForDone, expect*/
22+
23+
define(function (require, exports, module) {
24+
25+
const SpecRunnerUtils = require("spec/SpecRunnerUtils");
26+
27+
const testFolder = SpecRunnerUtils.getTestPath("/spec/LiveDevelopment-MultiBrowser-test-files");
28+
const mdTestFolder = SpecRunnerUtils.getTestPath("/spec/LiveDevelopment-Markdown-test-files");
29+
30+
let testWindow, brackets, CommandManager, Commands, EditorManager, WorkspaceManager,
31+
LiveDevMultiBrowser;
32+
33+
function _getMdPreviewIFrame() {
34+
return testWindow.document.getElementById("panel-md-preview-frame");
35+
}
36+
37+
function _getMdIFrameDoc() {
38+
const mdIFrame = _getMdPreviewIFrame();
39+
return mdIFrame && mdIFrame.contentDocument;
40+
}
41+
42+
function _getMdIFrameWin() {
43+
const mdIFrame = _getMdPreviewIFrame();
44+
return mdIFrame && mdIFrame.contentWindow;
45+
}
46+
47+
function _setMdEditMode(editMode) {
48+
const mdIFrame = _getMdPreviewIFrame();
49+
if (mdIFrame && mdIFrame.contentWindow) {
50+
mdIFrame.contentWindow.postMessage({
51+
type: "MDVIEWR_SET_EDIT_MODE",
52+
editMode: editMode
53+
}, "*");
54+
}
55+
}
56+
57+
async function _enterEditMode() {
58+
const win = _getMdIFrameWin();
59+
// Force reader→edit transition to ensure enterEditMode runs in editor.js
60+
// (attaches checkboxHandler, inputHandler, etc.)
61+
if (win && win.__setEditModeForTest) {
62+
win.__setEditModeForTest(false);
63+
}
64+
_setMdEditMode(true);
65+
if (win && win.__setEditModeForTest) {
66+
win.__setEditModeForTest(true);
67+
}
68+
await awaitsFor(() => {
69+
const mdDoc = _getMdIFrameDoc();
70+
if (!mdDoc) { return false; }
71+
const content = mdDoc.getElementById("viewer-content");
72+
return content && content.classList.contains("editing");
73+
}, "edit mode to activate");
74+
}
75+
76+
async function _enterReaderMode() {
77+
_setMdEditMode(false);
78+
const win = _getMdIFrameWin();
79+
if (win && win.__setEditModeForTest) {
80+
win.__setEditModeForTest(false);
81+
}
82+
await awaitsFor(() => {
83+
const mdDoc = _getMdIFrameDoc();
84+
if (!mdDoc) { return false; }
85+
const content = mdDoc.getElementById("viewer-content");
86+
return content && !content.classList.contains("editing");
87+
}, "reader mode to activate");
88+
}
89+
90+
async function _waitForMdPreviewReady(editor) {
91+
const expectedSrc = editor ? editor.document.getText() : null;
92+
await awaitsFor(() => {
93+
const mdIFrame = _getMdPreviewIFrame();
94+
if (!mdIFrame || mdIFrame.style.display === "none") { return false; }
95+
if (!mdIFrame.src || !mdIFrame.src.includes("mdViewer")) { return false; }
96+
const win = mdIFrame.contentWindow;
97+
if (!win || typeof win.__setEditModeForTest !== "function") { return false; }
98+
if (win.__isSuppressingContentChange && win.__isSuppressingContentChange()) { return false; }
99+
const content = mdIFrame.contentDocument && mdIFrame.contentDocument.getElementById("viewer-content");
100+
if (!content || content.children.length === 0) { return false; }
101+
if (!EditorManager.getActiveEditor()) { return false; }
102+
if (expectedSrc) {
103+
const viewerSrc = win.__getCurrentContent && win.__getCurrentContent();
104+
if (viewerSrc !== expectedSrc) { return false; }
105+
}
106+
return true;
107+
}, "md preview synced with editor content");
108+
}
109+
110+
describe("livepreview:Markdown Editor Edit Mode", function () {
111+
112+
if (Phoenix.browser.desktop.isFirefox ||
113+
(Phoenix.isTestWindowPlaywright && !Phoenix.browser.desktop.isChromeBased)) {
114+
it("Markdown edit mode tests are disabled in Firefox/non-Chrome playwright", function () {});
115+
return;
116+
}
117+
118+
beforeAll(async function () {
119+
if (!testWindow) {
120+
const useWindowInsteadOfIframe = Phoenix.browser.desktop.isFirefox;
121+
testWindow = await SpecRunnerUtils.createTestWindowAndRun({
122+
forceReload: false, useWindowInsteadOfIframe
123+
});
124+
brackets = testWindow.brackets;
125+
CommandManager = brackets.test.CommandManager;
126+
Commands = brackets.test.Commands;
127+
EditorManager = brackets.test.EditorManager;
128+
WorkspaceManager = brackets.test.WorkspaceManager;
129+
LiveDevMultiBrowser = brackets.test.LiveDevMultiBrowser;
130+
131+
await SpecRunnerUtils.loadProjectInTestWindow(mdTestFolder);
132+
await SpecRunnerUtils.deletePathAsync(mdTestFolder + "/.phcode.json", true);
133+
134+
if (!WorkspaceManager.isPanelVisible("live-preview-panel")) {
135+
await awaitsForDone(CommandManager.execute(Commands.FILE_LIVE_FILE_PREVIEW));
136+
}
137+
138+
// Open HTML first to start live dev
139+
await awaitsForDone(SpecRunnerUtils.openProjectFiles(["simple.html"]),
140+
"open simple.html");
141+
LiveDevMultiBrowser.open();
142+
await awaitsFor(() =>
143+
LiveDevMultiBrowser.status === LiveDevMultiBrowser.STATUS_ACTIVE,
144+
"live dev to open", 20000);
145+
146+
// Open the checkbox test md file
147+
await awaitsForDone(SpecRunnerUtils.openProjectFiles(["checkbox-test.md"]),
148+
"open checkbox-test.md");
149+
await _waitForMdPreviewReady(EditorManager.getActiveEditor());
150+
}
151+
}, 30000);
152+
153+
afterAll(async function () {
154+
if (LiveDevMultiBrowser) {
155+
LiveDevMultiBrowser.close();
156+
}
157+
if (CommandManager) {
158+
await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE_ALL, { _forceClose: true }),
159+
"final close all files");
160+
}
161+
testWindow = null;
162+
brackets = null;
163+
CommandManager = null;
164+
Commands = null;
165+
EditorManager = null;
166+
WorkspaceManager = null;
167+
LiveDevMultiBrowser = null;
168+
}, 30000);
169+
170+
describe("Checkbox (Task List) Sync", function () {
171+
172+
async function _openMdFile(fileName) {
173+
await awaitsForDone(SpecRunnerUtils.openProjectFiles([fileName]),
174+
"open " + fileName);
175+
await _waitForMdPreviewReady(EditorManager.getActiveEditor());
176+
}
177+
178+
function _getCheckboxes() {
179+
const mdDoc = _getMdIFrameDoc();
180+
const content = mdDoc && mdDoc.getElementById("viewer-content");
181+
if (!content) { return []; }
182+
return Array.from(content.querySelectorAll('input[type="checkbox"]'));
183+
}
184+
185+
it("should clicking checkbox in edit mode toggle it and sync to CM source", async function () {
186+
await _openMdFile("checkbox-test.md");
187+
await _enterEditMode();
188+
189+
const checkboxes = _getCheckboxes();
190+
expect(checkboxes.length).toBeGreaterThan(1);
191+
192+
// Find first unchecked checkbox ("Incomplete task")
193+
let uncheckedIdx = -1;
194+
for (let i = 0; i < checkboxes.length; i++) {
195+
if (!checkboxes[i].checked) {
196+
uncheckedIdx = i;
197+
break;
198+
}
199+
}
200+
expect(uncheckedIdx).toBeGreaterThanOrEqual(0);
201+
202+
// Click the checkbox using the iframe-context helper
203+
const win = _getMdIFrameWin();
204+
const checkedResult = win.__clickCheckboxForTest(uncheckedIdx);
205+
expect(checkedResult).toBeTrue();
206+
207+
// Verify CM source updated: [ ] → [x]
208+
const editor = EditorManager.getActiveEditor();
209+
await awaitsFor(() => {
210+
return /\[x\]\s+Incomplete task/.test(editor.document.getText());
211+
}, "CM source to sync checkbox to [x]");
212+
213+
// Document should be dirty
214+
expect(editor.document.isDirty).toBeTrue();
215+
216+
// Click again to uncheck
217+
const uncheckedResult = win.__clickCheckboxForTest(uncheckedIdx);
218+
expect(uncheckedResult).toBeFalse();
219+
220+
// Verify CM source updated: [x] → [ ]
221+
await awaitsFor(() => {
222+
return /\[ \]\s+Incomplete task/.test(editor.document.getText());
223+
}, "CM source to sync checkbox back to [ ]");
224+
225+
await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE, { _forceClose: true }),
226+
"force close checkbox-test.md");
227+
}, 15000);
228+
229+
it("should checkboxes be enabled in edit mode and disabled in reader mode", async function () {
230+
await _openMdFile("checkbox-test.md");
231+
232+
// In reader mode: checkboxes should be disabled
233+
await _enterReaderMode();
234+
let checkboxes = _getCheckboxes();
235+
expect(checkboxes.length).toBeGreaterThan(0);
236+
for (const cb of checkboxes) {
237+
expect(cb.disabled).toBeTrue();
238+
}
239+
240+
// In edit mode: checkboxes should be enabled
241+
await _enterEditMode();
242+
checkboxes = _getCheckboxes();
243+
expect(checkboxes.length).toBeGreaterThan(0);
244+
for (const cb of checkboxes) {
245+
expect(cb.disabled).toBeFalse();
246+
}
247+
248+
await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE, { _forceClose: true }),
249+
"force close checkbox-test.md");
250+
}, 15000);
251+
252+
});
253+
});
254+
});

0 commit comments

Comments
 (0)