|
| 1 | +import { test, expect } from '../../fixtures.mjs'; |
| 2 | + |
| 3 | +/** |
| 4 | + * Whitebox-e2e: the FIRST end-to-end proof that the dock ENGINE reorders a pane on a real drag gesture — |
| 5 | + * not the JSON model in isolation (that is unit-tested), but a rendered tab header dragged in the live |
| 6 | + * example, asserted against the committed dockZone.v1 document in the App Worker. |
| 7 | + * |
| 8 | + * Scope (this slice): WITHIN-container reorder. Dragging the "Strategy" tab header past its sibling |
| 9 | + * "Swarm" inside `main-tabs` reorders the committed model from ['strategy','swarm'] to |
| 10 | + * ['swarm','strategy']. The gesture rides the EXISTING tab-header SortZone (no parallel drag system); |
| 11 | + * the DockLayoutAdapter projects `dragResortable: true` and its `moveTo` listener commits the result |
| 12 | + * through applyDockZoneOperation + onDockZoneDocumentChange. Cross-zone drag (an item leaving its tabs |
| 13 | + * node) rides the dashboard SortZone in a follow-up slice. |
| 14 | + * |
| 15 | + * Paradigm (whitebox-e2e protocol): Playwright drives the native mouse gesture; the Neural Link fixture |
| 16 | + * reads the holder's committed document (dockModel) before and after. Two product-level truths are |
| 17 | + * asserted: (1) the tab headers are actually draggable in the DOM, (2) the drag mutates worker truth. |
| 18 | + * |
| 19 | + * Run: npx playwright test DockDragDropNL -c test/playwright/playwright.config.e2e.mjs --workers=1 |
| 20 | + */ |
| 21 | +test.describe('Dock drag-and-drop journey (Neural Link)', () => { |
| 22 | + test.setTimeout(90000); |
| 23 | + test.use({ viewport: { width: 1600, height: 900 } }); |
| 24 | + |
| 25 | + test('dragging a tab header past its sibling reorders the item in the committed dock model', async ({ page, neuralLink }) => { |
| 26 | + await page.goto('/examples/dashboard/dock/'); |
| 27 | + page.on('pageerror', err => console.error('BROWSER JS ERROR:', err)); |
| 28 | + |
| 29 | + await page.waitForTimeout(2500); // settle worker boot + first render |
| 30 | + |
| 31 | + const app = await neuralLink.connectToApp('Neo.examples.dashboard.dock'); |
| 32 | + |
| 33 | + // resolve the dock holder + a reader over its committed document (worker truth) |
| 34 | + const holders = await app.findInstances({ className: 'Neo.examples.dashboard.dock.MainContainer' }, ['id']); |
| 35 | + const holderId = Array.isArray(holders) ? holders[0]?.id : holders?.id; |
| 36 | + expect(holderId, 'the dock MainContainer must exist in the App Worker').toBeTruthy(); |
| 37 | + |
| 38 | + const readModel = async () => (await app.getComponent(holderId, ['dockModel'])).dockModel; |
| 39 | + |
| 40 | + const before = await readModel(); |
| 41 | + expect(before?.nodes?.['main-tabs']?.items, 'the example must seed main-tabs as [strategy, swarm]') |
| 42 | + .toEqual(['strategy', 'swarm']); |
| 43 | + |
| 44 | + // product truth #1: the dock projects draggable tab headers (not just a static model). Both panes of |
| 45 | + // main-tabs plus the two single-tab side zones = four draggable tab headers. |
| 46 | + const draggableTabHeaders = await page.evaluate(() => |
| 47 | + document.querySelectorAll('.neo-tab-header-button.neo-draggable').length); |
| 48 | + expect(draggableTabHeaders, 'the dock tab headers must be draggable (dragResortable projected)') |
| 49 | + .toBeGreaterThanOrEqual(2); |
| 50 | + |
| 51 | + // both headers live in main-tabs' toolbar |
| 52 | + const strategyTab = page.locator('.neo-tab-header-button', { hasText: 'Strategy' }).first(); |
| 53 | + const swarmTab = page.locator('.neo-tab-header-button', { hasText: 'Swarm' }).first(); |
| 54 | + await expect(strategyTab, 'the Strategy tab header must render').toBeVisible({ timeout: 10000 }); |
| 55 | + await expect(swarmTab, 'the Swarm tab header must render').toBeVisible({ timeout: 10000 }); |
| 56 | + |
| 57 | + const strategyBox = await strategyTab.boundingBox(); |
| 58 | + const swarmBox = await swarmTab.boundingBox(); |
| 59 | + |
| 60 | + // native drag: Strategy header -> just past Swarm's right edge (the {steps} cadence arms Neo's drag sensor) |
| 61 | + await page.mouse.move(strategyBox.x + strategyBox.width / 2, strategyBox.y + strategyBox.height / 2); |
| 62 | + await page.mouse.down(); |
| 63 | + await page.mouse.move(swarmBox.x + swarmBox.width / 2, swarmBox.y + swarmBox.height / 2, { steps: 30 }); |
| 64 | + await page.mouse.move(swarmBox.x + swarmBox.width + 12, swarmBox.y + swarmBox.height / 2, { steps: 30 }); |
| 65 | + await page.mouse.up(); |
| 66 | + await page.waitForTimeout(1000); |
| 67 | + |
| 68 | + const after = await readModel(); |
| 69 | + console.log('[dock-dnd] main-tabs:', JSON.stringify(before?.nodes?.['main-tabs']?.items), '->', JSON.stringify(after?.nodes?.['main-tabs']?.items)); |
| 70 | + |
| 71 | + // product truth #2: the drag mutated the COMMITTED dock model in the App Worker |
| 72 | + expect(after?.nodes?.['main-tabs']?.items, 'the tab-drag must reorder the committed dock model — the whole point of the engine') |
| 73 | + .toEqual(['swarm', 'strategy']); |
| 74 | + }); |
| 75 | +}); |
0 commit comments