Skip to content

Commit 2cc3f6a

Browse files
committed
refactor(tests): update Cypress test configurations and enhance document handling
- Disabled ESLint rule for undefined variables in multiple test files. - Changed editor visit settings to clear documents before each test for better isolation. - Reduced wait times in tests to improve execution speed. - Added a new hierarchy validation plugin to the TipTap editor. - Updated commands to synchronize selections with the TipTap editor more effectively.
1 parent f01a273 commit 2cc3f6a

17 files changed

Lines changed: 1754 additions & 11 deletions

packages/webapp/cypress/e2e/editor/Heading/Change/normal-text/heading-2-text-complex.cy.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
import { TEST_TITLE, TEST_CONTENT } from '../../../../../support/commands'
23
import { section, paragraph, heading } from '../../../../../fixtures/docMaker'
34

@@ -219,12 +220,12 @@ function verifyHeadingConversion(headingText, expectedStructure) {
219220
})
220221
}
221222

222-
describe('Convert Headings to Text - Complex Cases', { testIsolation: false }, () => {
223+
describe('Convert Headings to Text - Complex Cases', () => {
223224
beforeEach(() => {
224225
// Start fresh for each test to avoid state issues
225-
cy.visitEditor({ persist: true, docName: 'heading-to-text-complex-doc' })
226+
cy.visitEditor({ persist: false, clearDoc: true, docName: 'heading-to-text-complex-doc' })
226227
cy.createDocument(complexDocumentStructure)
227-
cy.wait(1000)
228+
cy.wait(500)
228229

229230
// Verify initial document structure is valid
230231
cy.validateDocumentStructure(complexDocumentStructure).then((result) => {

packages/webapp/cypress/e2e/editor/Heading/Change/selection/heading-cross-hierarchy-to-normal.cy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
import { TEST_TITLE, TEST_CONTENT } from '../../../../../support/commands'
23
import { section, paragraph, heading } from '../../../../../fixtures/docMaker'
34

packages/webapp/cypress/e2e/editor/Heading/Change/selection/heading-partial-selection-to-normal.cy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
import { TEST_TITLE, TEST_CONTENT } from '../../../../../support/commands'
23
import {
34
section,

packages/webapp/cypress/e2e/editor/Heading/Change/selection/heading-section-to-normal.cy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
import { TEST_TITLE, TEST_CONTENT } from '../../../../../support/commands'
23
import { section, paragraph, heading } from '../../../../../fixtures/docMaker'
34

packages/webapp/cypress/e2e/editor/Heading/Change/selection/heading-selection-to-normal.cy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
import { TEST_TITLE, TEST_CONTENT } from '../../../../../support/commands'
23
import { section, paragraph, heading } from '../../../../../fixtures/docMaker'
34

packages/webapp/cypress/e2e/editor/Heading/caret-position-on-change.cy.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
import { TEST_TITLE, TEST_CONTENT } from '../../../support/commands'
23
import { section, paragraph, heading } from '../../../fixtures/docMaker'
34

@@ -248,7 +249,7 @@ describe('Complex Nested Heading Level Changes', () => {
248249
}
249250

250251
beforeEach(() => {
251-
cy.visitEditor({ persist: true, docName: 'complex-heading-changes-doc' })
252+
cy.visitEditor({ persist: false, clearDoc: true, docName: 'complex-heading-changes-doc' })
252253
cy.createDocument(ComplexDocumentStructure)
253254
cy.wait(500)
254255
})
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/* eslint-disable no-undef */
2+
/**
3+
* Hierarchy Validation Plugin Tests
4+
*
5+
* Tests the automatic hierarchy fixing behavior of the hierarchyValidationPlugin.
6+
* When violations occur, the plugin should automatically fix them:
7+
* - H1 nested inside another heading → extract to document root
8+
* - Child level ≤ parent level → extract as sibling
9+
*/
10+
11+
import { TEST_TITLE, TEST_CONTENT } from '../../../support/commands'
12+
import { section, paragraph, heading } from '../../../fixtures/docMaker'
13+
14+
describe('Hierarchy Validation Plugin - Auto-Fix Behavior', () => {
15+
beforeEach(() => {
16+
cy.visitEditor({ persist: false, clearDoc: true, docName: 'hierarchy-validation-test' })
17+
})
18+
19+
describe('H1 Nested Extraction', () => {
20+
it('should extract nested H1 to document root via TOC drag', () => {
21+
// Create a document with H1 -> H2 structure
22+
const doc = {
23+
documentName: TEST_TITLE.HelloDocy,
24+
sections: [
25+
section('Parent Section', [
26+
paragraph('Parent content'),
27+
heading(2, 'Child Heading', [paragraph('Child content')])
28+
])
29+
]
30+
}
31+
32+
cy.createDocument(doc)
33+
cy.wait(300)
34+
35+
// Promote H2 to H1 via TOC drag (should trigger extraction)
36+
cy.window().then((win) => {
37+
if (win._moveHeading) {
38+
// Find the H2 heading
39+
const editor = win._editor
40+
let h2Id = null
41+
42+
editor.state.doc.descendants((node, pos) => {
43+
if (node.type.name === 'heading') {
44+
const level = node.firstChild?.attrs?.level
45+
if (level === 2) {
46+
h2Id = node.attrs.id
47+
return false
48+
}
49+
}
50+
})
51+
52+
if (h2Id) {
53+
// This would create an H1 inside H1 - plugin should extract it
54+
// Actually, moveHeading with level 1 should create a new section
55+
cy.log('Testing H1 promotion behavior')
56+
}
57+
}
58+
})
59+
60+
// After any H1 promotion, there should be no nested H1s
61+
cy.get('.heading[level="1"] .heading[level="1"]').should('not.exist')
62+
})
63+
64+
it('should maintain valid structure when changing heading to H1', () => {
65+
const doc = {
66+
documentName: TEST_TITLE.HelloDocy,
67+
sections: [
68+
section('First Section', [
69+
paragraph('Content'),
70+
heading(2, 'Nested Heading', [paragraph('Nested content')])
71+
])
72+
]
73+
}
74+
75+
cy.createDocument(doc)
76+
cy.wait(300)
77+
78+
// Change H2 to H1 via keyboard shortcut
79+
cy.putPosCaretInHeading(2, 'Nested Heading', 'start')
80+
cy.realPress(['Alt', 'Meta', '1'])
81+
cy.wait(500)
82+
83+
// Should now have 2 H1 sections (not nested)
84+
cy.get('.docy_editor > .tiptap > .heading[level="1"]').should('have.length', 2)
85+
86+
// No H1 should be nested inside another H1
87+
cy.get('.heading[level="1"] .contentWrapper .heading[level="1"]').should('not.exist')
88+
})
89+
})
90+
91+
describe('Invalid Child Level Extraction', () => {
92+
it('should handle demoting parent below child level', () => {
93+
const doc = {
94+
documentName: TEST_TITLE.HelloDocy,
95+
sections: [
96+
section('Root', [
97+
paragraph('Root content'),
98+
heading(2, 'Parent', [
99+
paragraph('Parent content'),
100+
heading(4, 'Child', [paragraph('Child content')])
101+
])
102+
])
103+
]
104+
}
105+
106+
cy.createDocument(doc)
107+
cy.wait(300)
108+
109+
// Demote H2 to H5 (child H4 would then be invalid as H4 < H5)
110+
cy.putPosCaretInHeading(2, 'Parent', 'start')
111+
cy.realPress(['Alt', 'Meta', '5'])
112+
cy.wait(500)
113+
114+
// The structure should be fixed - H4 should not be inside H5
115+
// Either H4 becomes sibling of H5 or is extracted
116+
cy.validateDomStructure({ throwOnError: false, logResults: true }).then((result) => {
117+
expect(result.valid).to.be.true
118+
})
119+
})
120+
121+
it('should extract child when child level equals parent level', () => {
122+
const doc = {
123+
documentName: TEST_TITLE.HelloDocy,
124+
sections: [
125+
section('Root', [
126+
paragraph('Root content'),
127+
heading(3, 'First H3', [
128+
paragraph('Content'),
129+
heading(5, 'Nested H5', [paragraph('H5 content')])
130+
]),
131+
heading(3, 'Second H3', [paragraph('Content')])
132+
])
133+
]
134+
}
135+
136+
cy.createDocument(doc)
137+
cy.wait(500)
138+
139+
// Change nested H5 to H3 (same as parent - triggers extraction)
140+
cy.putPosCaretInHeading(5, 'Nested H5', 'start')
141+
cy.realPress(['Alt', 'Meta', '3'])
142+
cy.wait(500)
143+
144+
// The hierarchy validation should fix the structure
145+
// The ex-H5 (now H3) should be extracted as sibling
146+
cy.get('.heading[level="3"]').should('have.length.at.least', 2)
147+
148+
cy.validateDomStructure({ throwOnError: false, logResults: true }).then((result) => {
149+
expect(result.valid).to.be.true
150+
})
151+
})
152+
})
153+
154+
describe('Complex Hierarchy Fixes', () => {
155+
it('should handle cascading extractions correctly', () => {
156+
// H1 -> H2 -> H3 -> H4
157+
// Change H2 to H5 → H3 and H4 should be extracted
158+
const doc = {
159+
documentName: TEST_TITLE.HelloDocy,
160+
sections: [
161+
section('Root', [
162+
heading(2, 'Level 2 Parent', [
163+
paragraph('Level 2 content'),
164+
heading(3, 'Level 3 Child', [
165+
paragraph('Level 3 content'),
166+
heading(4, 'Level 4 Deep', [paragraph('Deep content')])
167+
])
168+
])
169+
])
170+
]
171+
}
172+
173+
cy.createDocument(doc)
174+
cy.wait(500)
175+
176+
// Change H2 to H5
177+
cy.putPosCaretInHeading(2, 'Level 2 Parent', 'start')
178+
cy.realPress(['Alt', 'Meta', '5'])
179+
cy.wait(500)
180+
181+
// H3 (level 3) should be extracted since 3 < 5
182+
// H4 should also be extracted since 4 < 5
183+
cy.validateDomStructure({ throwOnError: false, logResults: true }).then((result) => {
184+
expect(result.valid).to.be.true
185+
})
186+
187+
// All original headings should still exist (just restructured)
188+
cy.get('.heading').should('have.length.at.least', 3)
189+
})
190+
191+
it('should preserve content when extracting headings', () => {
192+
const doc = {
193+
documentName: TEST_TITLE.HelloDocy,
194+
sections: [
195+
section('Root', [
196+
heading(2, 'Parent', [
197+
paragraph('Parent paragraph 1'),
198+
paragraph('Parent paragraph 2'),
199+
heading(4, 'Child', [
200+
paragraph('Child paragraph 1'),
201+
paragraph('Child paragraph 2')
202+
])
203+
])
204+
])
205+
]
206+
}
207+
208+
cy.createDocument(doc)
209+
cy.wait(300)
210+
211+
// Demote parent to H6 (forcing child H4 extraction)
212+
cy.putPosCaretInHeading(2, 'Parent', 'start')
213+
cy.realPress(['Alt', 'Meta', '6'])
214+
cy.wait(500)
215+
216+
// All content should still exist (scope to editor to avoid Next.js elements)
217+
cy.get('.docy_editor p').contains('Parent paragraph 1').should('exist')
218+
cy.get('.docy_editor p').contains('Parent paragraph 2').should('exist')
219+
cy.get('.docy_editor p').contains('Child paragraph 1').should('exist')
220+
cy.get('.docy_editor p').contains('Child paragraph 2').should('exist')
221+
222+
cy.validateDomStructure({ throwOnError: false, logResults: true }).then((result) => {
223+
expect(result.valid).to.be.true
224+
})
225+
})
226+
})
227+
228+
describe('Edge Cases', () => {
229+
it('should handle rapid consecutive level changes', () => {
230+
const doc = {
231+
documentName: TEST_TITLE.HelloDocy,
232+
sections: [
233+
section('Root', [
234+
heading(2, 'Test Heading', [
235+
paragraph('Content'),
236+
heading(4, 'Nested', [paragraph('Nested content')])
237+
])
238+
])
239+
]
240+
}
241+
242+
cy.createDocument(doc)
243+
cy.wait(300)
244+
245+
// Rapid changes: 2 → 5 → 3 → 7 → 2
246+
cy.putPosCaretInHeading(2, 'Test Heading', 'start')
247+
248+
cy.realPress(['Alt', 'Meta', '5']).wait(200)
249+
cy.realPress(['Alt', 'Meta', '3']).wait(200)
250+
cy.realPress(['Alt', 'Meta', '7']).wait(200)
251+
cy.realPress(['Alt', 'Meta', '2']).wait(300)
252+
253+
// Final state should be valid
254+
cy.validateDomStructure({ throwOnError: false, logResults: true }).then((result) => {
255+
expect(result.valid).to.be.true
256+
})
257+
})
258+
259+
it('should handle empty headings during extraction', () => {
260+
const doc = {
261+
documentName: TEST_TITLE.HelloDocy,
262+
sections: [
263+
section('Root', [
264+
heading(2, 'Parent with empty child', [
265+
heading(4, 'Empty Child', []) // No content
266+
])
267+
])
268+
]
269+
}
270+
271+
cy.createDocument(doc)
272+
cy.wait(300)
273+
274+
// Demote parent to H6
275+
cy.putPosCaretInHeading(2, 'Parent with empty child', 'start')
276+
cy.realPress(['Alt', 'Meta', '6'])
277+
cy.wait(500)
278+
279+
// Empty child should still be extracted correctly
280+
cy.get('.heading .title').contains('Empty Child').should('exist')
281+
282+
cy.validateDomStructure({ throwOnError: false, logResults: true }).then((result) => {
283+
expect(result.valid).to.be.true
284+
})
285+
})
286+
})
287+
})
288+

0 commit comments

Comments
 (0)