import { test, expect } from './mind-elixir-test' const data = { nodeData: { topic: 'Root Topic', id: 'root', children: [ { id: 'left-main', topic: 'Left Main', children: [ { id: 'left-child-1', topic: 'Left Child 1', }, { id: 'left-child-2', topic: 'Left Child 2', }, { id: 'left-child-3', topic: 'Left Child 3', }, ], }, { id: 'right-main', topic: 'Right Main', children: [ { id: 'right-child-1', topic: 'Right Child 1', }, { id: 'right-child-2', topic: 'Right Child 2', }, ], }, ], }, } test.beforeEach(async ({ me }) => { await me.init(data) }) test('Create summary for single node', async ({ page, me }) => { // Select a single child node await me.click('Left Child 1') // Get position of the selected node const leftChild1 = page.getByText('Left Child 1', { exact: true }) const nodeBox = await leftChild1.boundingBox() // Right click to open context menu await leftChild1.click({ button: 'right', force: true }) // Click summary option in context menu await page.locator('#cm-summary').click() // Verify summary SVG group appears await expect(page.locator('svg g[id^="s-"]')).toBeVisible() // Verify summary text label is visible await expect(page.locator('svg g[id^="s-"] text')).toBeVisible() await expect(page.locator('svg g[id^="s-"] text')).toHaveText('summary') // Verify summary path (bracket shape) is visible await expect(page.locator('svg g[id^="s-"] path')).toBeVisible() // Verify summary is positioned near the selected node const summaryPath = page.locator('svg g[id^="s-"] path') const summaryBox = await summaryPath.boundingBox() // Summary should be vertically aligned with the selected node (with some tolerance) expect(Math.abs(summaryBox!.y - nodeBox!.y)).toBeLessThan(50) }) test('Create summary for multiple nodes', async ({ page, me }) => { // Use drag selection to select multiple child nodes await me.dragSelect('Left Child 1', 'Left Child 3') // Get positions of the selected nodes before creating summary const leftChild1 = page.getByText('Left Child 1', { exact: true }) const leftChild2 = page.getByText('Left Child 2', { exact: true }) const leftChild3 = page.getByText('Left Child 3', { exact: true }) const node1Box = await leftChild1.boundingBox() const node2Box = await leftChild2.boundingBox() const node3Box = await leftChild3.boundingBox() // Right click to open context menu on one of the selected nodes await leftChild1.click({ button: 'right', force: true }) // Click summary option in context menu await page.locator('#cm-summary').click() // Verify summary appears await expect(page.locator('svg g[id^="s-"]')).toBeVisible() await expect(page.locator('svg g[id^="s-"] text')).toHaveText('summary') await expect(page.locator('svg g[id^="s-"] path')).toBeVisible() // Verify summary bracket spans across all three selected nodes const summaryPath = page.locator('svg g[id^="s-"] path') const summaryBox = await summaryPath.boundingBox() // Summary should span from the top of the first node to the bottom of the last node const topMostNode = Math.min(node1Box!.y, node2Box!.y, node3Box!.y) const bottomMostNode = Math.max(node1Box!.y + node1Box!.height, node2Box!.y + node2Box!.height, node3Box!.y + node3Box!.height) // Summary bracket should cover the vertical range of all selected nodes // Allow some tolerance for padding and bracket styling expect(summaryBox!.y).toBeLessThanOrEqual(topMostNode + 10) expect(summaryBox!.y + summaryBox!.height).toBeGreaterThanOrEqual(bottomMostNode - 10) }) test('Select and highlight summary', async ({ page, me }) => { // Create a summary first await me.click('Left Child 1') await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Wait for edit mode to finish (press Enter to complete editing) await page.keyboard.press('Enter') await expect(page.locator('#input-box')).toBeHidden() // Click on the summary to select it await page.locator('svg g[id^="s-"]').click() // Verify selection rectangle appears await expect(page.locator('svg g[id^="s-"] rect')).toBeVisible() }) test('Edit summary text', async ({ page, me }) => { // Create a summary first await me.click('Left Child 1') await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Summary creation automatically enters edit mode, so input box should already be visible await expect(page.locator('#input-box')).toBeVisible() // Clear existing text and type new text await page.keyboard.press('Control+a') await page.keyboard.insertText('Custom Summary') await page.keyboard.press('Enter') // Verify input box disappears await expect(page.locator('#input-box')).toBeHidden() // Verify new text is displayed await expect(page.locator('svg g[id^="s-"] text')).toHaveText('Custom Summary') }) test('Remove summary', async ({ page, me }) => { // Create a summary first await me.click('Left Child 1') await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Wait for edit mode to finish (press Enter to complete editing) await page.keyboard.press('Enter') await expect(page.locator('#input-box')).toBeHidden() // Verify summary exists await expect(page.locator('svg g[id^="s-"]')).toBeVisible() // Select and delete summary await page.locator('svg g[id^="s-"]').click() await page.keyboard.press('Delete') // Verify summary is removed await expect(page.locator('svg g[id^="s-"]')).not.toBeVisible() }) test('Cannot create summary on root node', async ({ page, me }) => { // Try to select root node await me.click('Root Topic') // Try to create summary via right click menu await page.getByText('Root Topic', { exact: true }).click({ button: 'right', force: true }) // Verify summary option is not available or doesn't work for root // (The context menu might not show summary option for root, or it might be disabled) const summaryOption = page.locator('#cm-summary') if (await summaryOption.isVisible()) { await summaryOption.click() } // Verify no summary is created await expect(page.locator('svg g[id^="s-"]')).not.toBeVisible() }) test('Summary appears on correct side for left branch', async ({ page, me }) => { // Select node on left side await me.click('Left Child 1') await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Get the summary group const summaryGroup = page.locator('svg g[id^="s-"]') await expect(summaryGroup).toBeVisible() // Verify text anchor is 'end' for left side (text should be right-aligned) const summaryText = summaryGroup.locator('text') await expect(summaryText).toHaveAttribute('text-anchor', 'end') }) test('Summary appears on correct side for right branch', async ({ page, me }) => { // Select node on right side await me.click('Right Child 1') await page.getByText('Right Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Get the summary group const summaryGroup = page.locator('svg g[id^="s-"]') await expect(summaryGroup).toBeVisible() // Verify text anchor is 'start' for right side (text should be left-aligned) const summaryText = summaryGroup.locator('text') await expect(summaryText).toHaveAttribute('text-anchor', 'start') }) test('Multiple summaries can coexist', async ({ page, me }) => { // Create first summary await me.click('Left Child 1') await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Create second summary await me.click('Right Child 1') await page.getByText('Right Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Verify both summaries exist const summaryGroups = page.locator('svg g[id^="s-"]') await expect(summaryGroups).toHaveCount(2) // Verify both have text elements await expect(page.locator('svg g[id^="s-"] text')).toHaveCount(2) }) test('Summary covers exact range of selected nodes', async ({ page, me }) => { // Use drag selection to select all three child nodes await me.dragSelect('Left Child 1', 'Left Child 3') // Create summary await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() // Finish editing await page.keyboard.press('Enter') await expect(page.locator('#input-box')).toBeHidden() // Get positions of all child nodes const child1Box = await page.getByText('Left Child 1', { exact: true }).boundingBox() const child2Box = await page.getByText('Left Child 2', { exact: true }).boundingBox() const child3Box = await page.getByText('Left Child 3', { exact: true }).boundingBox() // Get summary bracket position const summaryBox = await page.locator('svg g[id^="s-"] path').boundingBox() // Summary should span from Child 1 to Child 3 (including Child 2 in between) // This verifies that the summary covers the range correctly const topNode = Math.min(child1Box!.y, child2Box!.y, child3Box!.y) const bottomNode = Math.max(child1Box!.y + child1Box!.height, child2Box!.y + child2Box!.height, child3Box!.y + child3Box!.height) // Summary bracket should cover the range including all selected nodes expect(summaryBox!.y).toBeLessThanOrEqual(topNode + 10) expect(summaryBox!.y + summaryBox!.height).toBeGreaterThanOrEqual(bottomNode - 10) // Verify that all three children are within the summary range expect(summaryBox!.y).toBeLessThanOrEqual(child1Box!.y + 10) expect(summaryBox!.y).toBeLessThanOrEqual(child2Box!.y + 10) expect(summaryBox!.y).toBeLessThanOrEqual(child3Box!.y + 10) expect(summaryBox!.y + summaryBox!.height).toBeGreaterThanOrEqual(child1Box!.y + child1Box!.height - 10) expect(summaryBox!.y + summaryBox!.height).toBeGreaterThanOrEqual(child2Box!.y + child2Box!.height - 10) expect(summaryBox!.y + summaryBox!.height).toBeGreaterThanOrEqual(child3Box!.y + child3Box!.height - 10) }) test('Summary selection state management', async ({ page, me }) => { // Create two summaries await me.click('Left Child 1') await page.getByText('Left Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() await me.click('Right Child 1') await page.getByText('Right Child 1', { exact: true }).click({ button: 'right', force: true }) await page.locator('#cm-summary').click() const summaryGroups = page.locator('svg g[id^="s-"]') const firstSummary = summaryGroups.first() const secondSummary = summaryGroups.last() // Select first summary await firstSummary.click() await expect(firstSummary.locator('rect')).toBeVisible() await expect(secondSummary.locator('rect')).not.toBeVisible() // Select second summary await secondSummary.click() await expect(firstSummary.locator('rect')).not.toBeVisible() await expect(secondSummary.locator('rect')).toBeVisible() // Click elsewhere to deselect await page.locator('#map').click() await expect(firstSummary.locator('rect')).not.toBeVisible() await expect(secondSummary.locator('rect')).not.toBeVisible() })