File size: 5,362 Bytes
f6213fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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([]);
});