Phase 5: data-driven, new-player-friendly character builder
Rebuilt CreationWizard to consume the structured ruleset data (Phase 7): - Class step: rich class cards (description, hit die, key ability, caster, playstyle tag) + subclass picker, plus "ready-made hero" quick-start templates that prefill class + abilities for instant play. - Origin step: searchable ancestry/race + background pickers with descriptions (auto speed/ancestry-HP where known). - Abilities: key-ability hints from the chosen class. Skills: choices from class data. - Spells step (casters only): searchable starting-spell picker → spellcasting.spells. - Review applies derived build + data-driven saves (esp. PF2e classes). - Updated the e2e helper + wizard spec to the new flow (class-card data-testid). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,34 +9,33 @@ test.beforeEach(async ({ page }) => {
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('creation wizard builds a complete character (HP, skills, spell slots)', async ({ page }) => {
|
||||
test('creation wizard builds a complete character (HP, skills, spells)', async ({ page }) => {
|
||||
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
||||
await page.locator('input[data-autofocus]').fill('Wizard Camp');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('link', { name: 'Characters' }).click();
|
||||
await page.getByRole('button', { name: '+ New character' }).first().click();
|
||||
|
||||
// Basics: a level-3 Wizard
|
||||
// Class step: a level-3 Wizard (a caster, so a Spells step appears).
|
||||
await page.getByLabel('Name').fill('Mira');
|
||||
await page.getByLabel('Class').selectOption('Wizard');
|
||||
await page.getByLabel('Level', { exact: true }).fill('3');
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByTestId('class-card').filter({ hasText: 'Wizard' }).click();
|
||||
await page.getByRole('button', { name: 'Next' }).click(); // → Origin
|
||||
await page.getByRole('button', { name: 'Next' }).click(); // → Abilities (standard array valid)
|
||||
await page.getByRole('button', { name: 'Next' }).click(); // → Skills
|
||||
|
||||
// Abilities: standard array assignment is valid by default (CON 13 → +1)
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
|
||||
// Skills: Wizard chooses 2
|
||||
const boxes = page.locator('input[type="checkbox"]');
|
||||
await boxes.nth(0).check();
|
||||
await boxes.nth(1).check();
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByRole('button', { name: 'Next' }).click(); // → Spells (Wizard is a caster)
|
||||
|
||||
await expect(page.getByPlaceholder('Search spells…')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Next' }).click(); // → Review
|
||||
|
||||
// Review shows derived HP + spell slots before finishing
|
||||
await expect(page.getByText('Max HP')).toBeVisible();
|
||||
await expect(page.getByText('Spell slots')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Create character' }).click();
|
||||
|
||||
// Lands on the sheet with real HP (d6 + CON over 3 levels = 17), not the old 1/1
|
||||
// Lands on the sheet with real derived HP, not the old 1/1.
|
||||
await expect(page.getByText('Hit Points')).toBeVisible();
|
||||
await expect(page.getByText(/\/\s*17/)).toBeVisible();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user