3e5bdc06e2
- Condition durations + auto-expiry: timed conditions tick down on the affected combatant's turn and drop at 0 (engine tickConditions; UI rounds field + (Nr) chip) - Initiative: Roll-all (d20 + per-combatant bonus, re-sort anchored) + group/quantity add - Combat log: per-round event feed (turns, round changes, damage/heal, expiries, removals) + in-memory Undo of the last change - Encounter difficulty budget: 5e XP thresholds + PF2e level budget (pure lib + tests); live difficulty/XP-award readout from monster combatants vs the party - Combatants carry initBonus + cr/level; compendium add-to-combat passes them 18 new unit tests (durations, applyInitiatives, budget), new combat-depth e2e. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2.7 KiB
TypeScript
60 lines
2.7 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
// Each test starts from a clean IndexedDB so runs are deterministic.
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.evaluate(async () => {
|
|
indexedDB.deleteDatabase('ttrpg-manager');
|
|
localStorage.clear();
|
|
});
|
|
await page.reload();
|
|
});
|
|
|
|
test('full core flow: campaign → character → dice → combat → compendium', async ({ page }) => {
|
|
// --- Campaign ---
|
|
await expect(page.getByRole('heading', { name: 'Campaigns' })).toBeVisible();
|
|
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
|
await page.getByLabel('').first().waitFor(); // modal open
|
|
await page.locator('input[data-autofocus]').fill('Curse of Strahd');
|
|
await page.getByRole('button', { name: 'Create' }).click();
|
|
|
|
// Card appears and becomes active
|
|
await expect(page.getByRole('heading', { name: 'Curse of Strahd' })).toBeVisible();
|
|
|
|
// --- Character ---
|
|
await page.getByRole('link', { name: 'Characters' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Characters' })).toBeVisible();
|
|
await page.getByRole('button', { name: '+ New character' }).first().click();
|
|
await page.locator('input[data-autofocus]').fill('Ireena');
|
|
await page.getByRole('button', { name: 'Create' }).click();
|
|
await page.getByRole('link', { name: /Ireena/ }).click();
|
|
|
|
// On the sheet: set STR to 16 and expect +3 modifier
|
|
await page.getByLabel('STR score').fill('16');
|
|
await expect(page.getByText('+3').first()).toBeVisible();
|
|
|
|
// --- Dice ---
|
|
await page.getByRole('link', { name: 'Dice' }).click();
|
|
await page.getByRole('button', { name: 'Roll' }).click();
|
|
// a result total renders (1..20 for default 1d20)
|
|
await expect(page.locator('text=/= \\d+/')).toBeVisible();
|
|
|
|
// --- Combat ---
|
|
await page.getByRole('link', { name: 'Combat' }).click();
|
|
await page.getByRole('button', { name: '+ New encounter' }).first().click();
|
|
await page.locator('input[data-autofocus]').fill('Ambush');
|
|
await page.getByRole('button', { name: 'Create' }).click();
|
|
await page.getByLabel('Name').fill('Goblin');
|
|
await page.getByRole('button', { name: 'Add', exact: true }).click();
|
|
await expect(page.getByText('Goblin')).toBeVisible();
|
|
await page.getByRole('button', { name: 'Start combat' }).click();
|
|
await expect(page.getByRole('button', { name: 'End' })).toBeVisible(); // combat active
|
|
|
|
// --- Compendium ---
|
|
await page.getByRole('link', { name: 'Compendium' }).click();
|
|
await expect(page.getByText(/results/)).toBeVisible();
|
|
await page.getByPlaceholder('Search bestiary…').fill('goblin');
|
|
await page.getByRole('button', { name: /^Goblin/ }).first().click();
|
|
await expect(page.getByRole('heading', { name: 'Goblin', exact: true })).toBeVisible();
|
|
});
|