| const { test, expect } = require('playwright/test'); |
|
|
| const transparentPng = Buffer.from( |
| 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=', |
| 'base64', |
| ); |
|
|
| test.beforeEach(async ({ page }) => { |
| await page.route('https://*.tile.openstreetmap.org/**', (route) => route.fulfill({ |
| status: 200, |
| contentType: 'image/png', |
| body: transparentPng, |
| })); |
| }); |
|
|
| function collectBrowserErrors(page) { |
| const errors = []; |
| page.on('pageerror', (error) => errors.push(error.message)); |
| page.on('console', (message) => { |
| if (message.type() === 'error') errors.push(message.text()); |
| }); |
| return errors; |
| } |
|
|
| function demoSelect(page) { |
| return page.locator('.deliveries-field').filter({ hasText: 'Demo Data' }).locator('select'); |
| } |
|
|
| function routingSelect(page) { |
| return page.locator('.deliveries-field').filter({ hasText: 'Routing Mode' }).locator('select'); |
| } |
|
|
| test('boots the real deliveries app and serves required browser assets', async ({ page, request }) => { |
| const errors = collectBrowserErrors(page); |
|
|
| await expect(request.get('/health')).resolves.toBeOK(); |
| await expect(request.get('/sf/sf.js')).resolves.toBeOK(); |
| await expect(request.get('/sf/modules/sf-map.js')).resolves.toBeOK(); |
| await expect(request.get('/app.js')).resolves.toBeOK(); |
| await expect(request.get('/sf-config.json')).resolves.toBeOK(); |
|
|
| await page.goto('/'); |
| await expect(page).toHaveTitle('SolverForge Deliveries'); |
| await expect(page.getByText('Retained delivery-route optimization with route previews')).toBeVisible(); |
| await expect(page.locator('#sfStatusText')).toHaveText('Ready'); |
| await expect(page.locator('.sf-constraint-dot')).toHaveCount(4); |
|
|
| for (const tab of ['Overview', 'By Vehicle', 'By Delivery', 'Data', 'REST API']) { |
| await expect(page.getByRole('tab', { name: tab })).toBeVisible(); |
| } |
|
|
| await expect(demoSelect(page)).toHaveValue('PHILADELPHIA'); |
| await expect(routingSelect(page)).toHaveValue('road_network'); |
| await expect(page.getByText('PHILADELPHIA 路 road network 路 82 deliveries 路 10 vehicles')).toBeVisible(); |
| await expect(page.locator('.deliveries-list__row')).toHaveCount(10); |
| await expect(page.locator('#deliveries-map')).toBeVisible(); |
| await expect(page.locator('.sf-marker-vehicle')).toHaveCount(10); |
| await expect(page.locator('.sf-marker-visit')).toHaveCount(82); |
|
|
| expect(errors).toEqual([]); |
| }); |
|
|
| test('switches datasets and exposes delivery-specific app panels', async ({ page }) => { |
| const errors = collectBrowserErrors(page); |
|
|
| await page.goto('/'); |
| await demoSelect(page).selectOption('HARTFORD'); |
| await expect(page.getByText('HARTFORD 路 road network 路 50 deliveries 路 10 vehicles')).toBeVisible(); |
| await expect(page.locator('.sf-marker-visit')).toHaveCount(50); |
|
|
| await demoSelect(page).selectOption('FIRENZE'); |
| await expect(page.getByText('FIRENZE 路 road network 路 80 deliveries 路 10 vehicles')).toBeVisible(); |
| await expect(page.locator('.sf-marker-visit')).toHaveCount(80); |
|
|
| await page.getByRole('tab', { name: 'Data' }).click(); |
| await expect(page.getByRole('heading', { name: 'Draft Data' })).toBeVisible(); |
| await expect(page.getByRole('heading', { name: 'Vehicles' })).toBeVisible(); |
| await expect(page.getByRole('heading', { name: 'Deliveries' })).toBeVisible(); |
| await expect(page.getByRole('button', { name: 'Recommend' }).first()).toBeVisible(); |
| await expect(page.locator('button').filter({ hasText: /Add|Remove/ })).toHaveCount(0); |
|
|
| await page.getByRole('tab', { name: 'REST API' }).click(); |
| await expect(page.getByRole('heading', { name: 'GET /jobs/{id}/routes?snapshot_revision={n}' })).toBeVisible(); |
| await expect(page.getByRole('heading', { name: 'POST /recommendations/delivery-insertions' })).toBeVisible(); |
|
|
| expect(errors).toEqual([]); |
| }); |
|
|
| test('highlights a route without moving or zooming the map', async ({ page }) => { |
| const errors = collectBrowserErrors(page); |
|
|
| await page.goto('/'); |
| await expect(page.locator('.sf-marker-visit')).toHaveCount(82); |
| await page.evaluate(() => { |
| window.__fitBoundsCalls = 0; |
| const originalFitBounds = window.L.Map.prototype.fitBounds; |
| window.L.Map.prototype.fitBounds = function (...args) { |
| window.__fitBoundsCalls += 1; |
| return originalFitBounds.apply(this, args); |
| }; |
| }); |
|
|
| const firstRoute = page.locator('.deliveries-list__row').first(); |
| await firstRoute.click(); |
|
|
| await expect(firstRoute).toHaveClass(/is-focused/); |
| await expect(firstRoute.getByRole('button')).toHaveText('Show All'); |
| await expect.poll(() => page.evaluate(() => window.__fitBoundsCalls)).toBe(0); |
|
|
| expect(errors).toEqual([]); |
| }); |
|
|
| test('starts a retained straight-line solve and returns control to the user', async ({ page }) => { |
| const errors = collectBrowserErrors(page); |
|
|
| await page.goto('/'); |
| await routingSelect(page).selectOption('straight_line'); |
| await page.locator('button').filter({ hasText: 'Solve' }).first().click(); |
|
|
| await expect(page.locator('#sf-app')).toHaveAttribute('data-job-id', /.+/, { timeout: 10_000 }); |
| const stopButton = page.locator('button').filter({ hasText: 'Stop' }).first(); |
| if (await stopButton.isVisible()) { |
| await stopButton.click(); |
| } |
| await expect(page.locator('button').filter({ hasText: 'Solve' }).first()).toBeVisible({ timeout: 15_000 }); |
|
|
| expect(errors).toEqual([]); |
| }); |
|
|