2879059ba5
Render the map on a single viewport-sized canvas with a camera transform at devicePixelRatio (image, fog, drawings, overlay) and draw the grid as crisp 1px device-space lines — instead of CSS-scaling a natural-resolution bitmap, which blurred the grid/fog/text at any zoom. Tokens now position in screen space so their labels stay sharp too. Add live tool feedback via a 'hover' phase: polygon shows its outline + vertices + rubber-band + fill preview, the brush shows its footprint, and circle/square AoE preview under the cursor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
3.7 KiB
TypeScript
84 lines
3.7 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { createCharacter } from './helpers';
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.evaluate(async () => {
|
|
indexedDB.deleteDatabase('ttrpg-manager');
|
|
localStorage.clear();
|
|
});
|
|
await page.reload();
|
|
});
|
|
|
|
test('maps: upload, place token, reveal all, show to players', async ({ page }) => {
|
|
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
|
await page.locator('input[data-autofocus]').fill('VTT');
|
|
await page.getByRole('button', { name: 'Create' }).click();
|
|
await page.getByRole('link', { name: 'Dashboard' }).click();
|
|
await page.getByRole('link', { name: 'Maps' }).click();
|
|
|
|
await page.locator('input[type="file"]').setInputFiles('public/pwa-512x512.png');
|
|
|
|
// Editor loads (Move tool present)
|
|
await expect(page.getByRole('button', { name: 'move', exact: true })).toBeVisible();
|
|
|
|
// Add a token
|
|
await page.getByRole('button', { name: '+ Token' }).click();
|
|
await expect(page.getByTestId('map-token')).toHaveCount(1);
|
|
|
|
// Reveal tool exposes the contextual fog controls; reveal all
|
|
await page.getByRole('button', { name: 'reveal', exact: true }).click();
|
|
await page.getByText('Fog', { exact: true }).locator('input[type=checkbox]').check();
|
|
await page.getByRole('button', { name: 'Reveal all' }).click();
|
|
|
|
// Show to players, then the player view renders the battle map
|
|
await page.getByRole('button', { name: 'Show to players' }).click();
|
|
await page.getByRole('link', { name: 'Open player view ↗' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Battle map' })).toBeVisible();
|
|
await expect(page.getByTestId('map-token')).toHaveCount(1);
|
|
});
|
|
|
|
test('maps: zoom controls scale the canvas and fit resets', async ({ page }) => {
|
|
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
|
await page.locator('input[data-autofocus]').fill('Zoom');
|
|
await page.getByRole('button', { name: 'Create' }).click();
|
|
await page.getByRole('link', { name: 'Dashboard' }).click();
|
|
await page.getByRole('link', { name: 'Maps' }).click();
|
|
await page.locator('input[type="file"]').setInputFiles('public/pwa-512x512.png');
|
|
|
|
const viewport = page.getByTestId('map-viewport');
|
|
await expect(viewport).toBeVisible();
|
|
const scaleOf = async () => Number(await viewport.getAttribute('data-zoom'));
|
|
|
|
// Let the initial fit settle, then zooming in must increase the zoom…
|
|
await page.getByRole('button', { name: 'Fit map to view' }).click();
|
|
const fitZoom = await scaleOf();
|
|
expect(fitZoom).toBeGreaterThan(0);
|
|
await page.getByRole('button', { name: 'Zoom in' }).click();
|
|
await page.getByRole('button', { name: 'Zoom in' }).click();
|
|
const zoomedIn = await scaleOf();
|
|
expect(zoomedIn).toBeGreaterThan(fitZoom);
|
|
|
|
// …and Fit brings it back to the fit value.
|
|
await page.getByRole('button', { name: 'Fit map to view' }).click();
|
|
expect(await scaleOf()).toBeCloseTo(fitZoom, 5);
|
|
});
|
|
|
|
test('maps: place a party token from the palette', async ({ page }) => {
|
|
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
|
await page.locator('input[data-autofocus]').fill('Party');
|
|
await page.getByRole('button', { name: 'Create' }).click();
|
|
|
|
await page.getByRole('link', { name: 'Characters' }).click();
|
|
await createCharacter(page, 'Aria');
|
|
|
|
await page.getByRole('link', { name: 'Dashboard' }).click();
|
|
await page.getByRole('link', { name: 'Maps' }).click();
|
|
await page.locator('input[type="file"]').setInputFiles('public/pwa-512x512.png');
|
|
|
|
// Palette lists the PC; clicking it drops a linked token.
|
|
await page.getByRole('button', { name: /Aria/ }).click();
|
|
await expect(page.getByTestId('map-token')).toHaveCount(1);
|
|
await expect(page.getByTestId('map-token')).toContainText('Aria');
|
|
});
|