123 lines
4.2 KiB
TypeScript
123 lines
4.2 KiB
TypeScript
import { test, expect, login, sceneObjectCount } from './fixtures';
|
||
|
||
/**
|
||
* Browser-side checks for the restricted-area box. The geometry itself is
|
||
* covered in tests/unit/boundingBox.spec.ts, which runs in Node against the pure
|
||
* module; what only a browser can answer is whether the thing rasterises and
|
||
* what the operator ends up reading on screen.
|
||
*/
|
||
|
||
test.beforeEach(async ({ context }) => {
|
||
await login(context);
|
||
});
|
||
|
||
/** Seed one workspace at the given launch point, run it, and draw its box. */
|
||
async function runAt(page: import('@playwright/test').Page, lat: number, lng: number) {
|
||
await page.goto('/');
|
||
await page.evaluate(
|
||
({ lat, lng }) => {
|
||
const ws = {
|
||
id: crypto.randomUUID(),
|
||
name: `bbox-${lat}`,
|
||
color: '#dc3545',
|
||
opacity: 1,
|
||
visible: true,
|
||
flightParameters: {
|
||
ascent_rate: 5,
|
||
burst_altitude: 30000,
|
||
dataset: '',
|
||
descent_rate: 5,
|
||
format: 'json',
|
||
launch_altitude: 0,
|
||
launch_latitude: lat,
|
||
launch_longitude: lng,
|
||
profile: 'standard_profile',
|
||
version: 2,
|
||
},
|
||
launchDate: new Date().toISOString().split('T')[0],
|
||
launchTime: '12:00:00',
|
||
result: null,
|
||
bboxMargin: 5,
|
||
bboxVisible: false,
|
||
};
|
||
localStorage.setItem('workspaces', JSON.stringify({ items: [ws], activeId: ws.id }));
|
||
},
|
||
{ lat, lng },
|
||
);
|
||
await page.goto('/predict');
|
||
await page
|
||
.locator('.map-container canvas')
|
||
.first()
|
||
.waitFor({ state: 'attached', timeout: 60_000 });
|
||
|
||
const panel = page.locator('.panel-container-right');
|
||
await panel
|
||
.locator('.workspace-row')
|
||
.first()
|
||
.getByRole('button', { name: /Рассчитать|Run/ })
|
||
.click();
|
||
await expect
|
||
.poll(() => sceneObjectCount(page, 'ws/'), { timeout: 90_000, intervals: [1000, 2000, 3000] })
|
||
.toBeGreaterThan(0);
|
||
|
||
await panel.getByRole('button', { name: /Построить рамку|Generate bounding box/ }).click();
|
||
await expect.poll(() => sceneObjectCount(page, 'bbox'), { timeout: 15_000 }).toBeGreaterThan(0);
|
||
return panel;
|
||
}
|
||
|
||
/**
|
||
* Regression: the box must actually rasterise.
|
||
*
|
||
* An earlier attempt made a pole-touching box circumpolar with west=-180 and
|
||
* east=180. Both meridional edges then lay on the antimeridian, and Cesium's
|
||
* splitLongitude pass — which cuts geometry at the IDL — produced mismatched
|
||
* attribute lists and killed the render loop:
|
||
*
|
||
* DeveloperError: All attribute lists must have the same number of attributes.
|
||
* at k.splitLongitude / S.combineGeometry
|
||
*
|
||
* Assertions on polyline.positions could not see this, because that is the entity
|
||
* definition and geometry is combined later in a worker. This listens for the
|
||
* render failure itself.
|
||
*/
|
||
test('drawing a polar box does not break the renderer', async ({ page }) => {
|
||
test.setTimeout(150_000);
|
||
await runAt(page, 89.99, 30);
|
||
|
||
await page.evaluate(() => {
|
||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||
const v: any = (window as any)._lsvMap;
|
||
(window as any).__renderErrors = [];
|
||
v.scene.renderError.addEventListener((_s: unknown, e: any) =>
|
||
(window as any).__renderErrors.push(String(e?.message ?? e)),
|
||
);
|
||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||
});
|
||
await page.waitForTimeout(6000);
|
||
|
||
const errors = await page.evaluate(
|
||
() => (window as never as { __renderErrors: string[] }).__renderErrors,
|
||
);
|
||
expect(errors).toEqual([]);
|
||
|
||
// Cesium swaps in an error panel when the render loop dies.
|
||
await expect(page.locator('.cesium-widget-errorPanel')).toHaveCount(0);
|
||
});
|
||
|
||
test('the panel reports a corridor-sized area at the pole, not a polar cap', async ({ page }) => {
|
||
test.setTimeout(150_000);
|
||
const panel = await runAt(page, 89.99, 30);
|
||
|
||
// Near a pole the four corners are unreadable on their own — a box whose north
|
||
// edge passes over the pole comes back down the far side — so the size line is
|
||
// what the operator actually checks. The lat/lon-rectangle form reported the
|
||
// whole cap north of 88.93, 44 200 km^2, for a corridor of about 1 700.
|
||
const size = panel.locator('.font-monospace', { hasText: 'km @' }).first();
|
||
await expect(size).toBeVisible();
|
||
const text = (await size.textContent()) ?? '';
|
||
const [w, h] = text.split('km @')[0].split('×').map((s) => parseFloat(s));
|
||
|
||
expect(w).toBeGreaterThan(0);
|
||
expect(h).toBeGreaterThan(0);
|
||
expect(w * h).toBeLessThan(10_000);
|
||
});
|