lotr-sut/tests/e2e/steps/quest-management.steps.ts
Fellowship Scholar f6a5823439 init commit
2026-03-29 20:07:56 +00:00

367 lines
14 KiB
TypeScript

import { Given, When, Then, expect } from '../fixtures/bdd-fixtures';
import { Quest } from '../types';
/**
* Create a quest via the API (faster than UI interaction).
* User must already be authenticated via the Background step.
*/
async function createQuestViaApi(page: any, baseUrl: string, questData: {
title: string;
description: string;
status?: string;
quest_type?: string;
priority?: string;
is_dark_magic?: boolean;
location_id?: number;
}): Promise<number | undefined> {
const statusMap: Record<string, string> = {
'Not Yet Begun': 'not_yet_begun',
'The Road Goes Ever On...': 'the_road_goes_ever_on',
'It Is Done': 'it_is_done',
'The Shadow Falls': 'the_shadow_falls',
};
const response = await page.request.post(`${baseUrl}/api/quests`, {
data: {
title: questData.title,
description: questData.description,
status: statusMap[questData.status || 'Not Yet Begun'] || questData.status || 'not_yet_begun',
quest_type: questData.quest_type || 'The Journey',
priority: questData.priority || 'Standard',
is_dark_magic: questData.is_dark_magic || false,
location_id: questData.location_id,
},
});
if (response.ok()) {
const data = await response.json();
return data.id;
}
return undefined;
}
/**
* Get location ID by name via the API.
*/
async function getLocationIdByName(page: any, baseUrl: string, name: string): Promise<number | undefined> {
const response = await page.request.get(`${baseUrl}/api/locations`);
if (response.ok()) {
const locations: Array<{ id: number; name: string }> = await response.json();
const found = locations.find(l => l.name.toLowerCase().includes(name.toLowerCase()));
return found?.id;
}
return undefined;
}
Given('I navigate to the quests page', async ({ page }) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
// Clean up accumulated test quests before each scenario to prevent DB bloat / slow loads
try {
await page.request.post(`${baseUrl}/api/test/cleanup`);
} catch {
// Cleanup endpoint may not be available — continue
}
await questsPage.goto();
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Given('I have a quest titled {string}', async ({ page }, questTitle: string) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const id = await createQuestViaApi(page, baseUrl, {
title: questTitle,
description: `Test quest: ${questTitle}`,
status: 'Not Yet Begun',
quest_type: 'The Journey',
});
(testContext as any).lastCreatedQuestTitle = questTitle;
(testContext as any).lastCreatedQuestId = id;
// Reload the quests page to see the new quest
await questsPage.goto();
await page.waitForTimeout(500);
});
Given('I have multiple quests with different statuses', async ({ page }) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
await createQuestViaApi(page, baseUrl, { title: 'Completed Quest 1', description: 'Done', status: 'It Is Done', quest_type: 'The Ring' });
await createQuestViaApi(page, baseUrl, { title: 'Completed Quest 2', description: 'Done', status: 'It Is Done', quest_type: 'The Journey' });
await createQuestViaApi(page, baseUrl, { title: 'In Progress Quest', description: 'WIP', status: 'The Road Goes Ever On...', quest_type: 'The Battle' });
await questsPage.goto();
await page.waitForTimeout(500);
});
Given('I have quests of different types', async ({ page }) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
await createQuestViaApi(page, baseUrl, { title: 'Journey Quest BDD', description: 'A long journey', quest_type: 'The Journey' });
await createQuestViaApi(page, baseUrl, { title: 'Battle Quest BDD', description: 'A great battle', quest_type: 'The Battle' });
await createQuestViaApi(page, baseUrl, { title: 'Ring Quest BDD', description: 'The One Ring', quest_type: 'The Ring' });
await questsPage.goto();
await page.waitForTimeout(500);
});
Given('I have quests in different locations', async ({ page }) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
const mordorId = await getLocationIdByName(page, baseUrl, 'Mordor');
const rivendellId = await getLocationIdByName(page, baseUrl, 'Rivendell');
await createQuestViaApi(page, baseUrl, { title: 'Mordor Quest BDD', description: 'Darkness falls', quest_type: 'The Ring', location_id: mordorId });
await createQuestViaApi(page, baseUrl, { title: 'Rivendell Quest BDD', description: 'Elven halls', quest_type: 'The Journey', location_id: rivendellId });
await questsPage.goto();
await page.waitForTimeout(500);
});
Given('I have an incomplete quest titled {string}', async ({ page }, questTitle: string) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const id = await createQuestViaApi(page, baseUrl, {
title: questTitle,
description: `Incomplete quest: ${questTitle}`,
status: 'Not Yet Begun',
quest_type: 'The Journey',
});
(testContext as any).lastCreatedQuestTitle = questTitle;
(testContext as any).lastCreatedQuestId = id;
await questsPage.goto();
await page.waitForTimeout(500);
});
Given('a quest completion mini-game is displayed', async ({ page }) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
// Create a quest via API and trigger completion via UI
const questTitle = `Mini-game Quest ${Date.now()}`;
await createQuestViaApi(page, baseUrl, { title: questTitle, description: 'For mini-game test', status: 'Not Yet Begun' });
await questsPage.goto();
await page.waitForTimeout(1000); // Wait for quest list to reload
// Click the specifically created quest (not necessarily the first one)
const questTitles = await questsPage.getQuestTitles();
// Find our created quest title (partial match in case list is truncated)
const target = questTitles.find(t => t.includes('Mini-game Quest')) || questTitles[0];
if (target) {
await questsPage.clickQuest(target);
await questsPage.completeQuest();
await page.waitForTimeout(1000);
const isMiniGameVisible = await questsPage.isMiniGameModalVisible();
expect(isMiniGameVisible).toBeTruthy();
}
});
Given('I have a dark magic quest', async ({ page }) => {
const baseUrl = global.fixtures.baseUrl;
const questsPage = global.fixtures.questsPage!;
await createQuestViaApi(page, baseUrl, {
title: 'Dark Magic Quest BDD',
description: 'A quest touched by dark magic',
status: 'Not Yet Begun',
quest_type: 'Dark Magic',
is_dark_magic: true,
});
await questsPage.goto();
await page.waitForTimeout(500);
});
When('I create a quest with title {string} and description {string}', async ({ page }, title: string, description: string) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const questData: Omit<Quest, 'id'> = {
title,
description,
status: 'Not Yet Begun',
type: 'The Ring',
};
await questsPage.createQuest(questData);
testContext.addQuest(questData);
(testContext as any).lastCreatedQuestTitle = title;
});
When('I edit the quest and change the status to {string}', async ({ page }, newStatus: string) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const questTitle = (testContext as any).lastCreatedQuestTitle || (await questsPage.getQuestTitles())[0];
await questsPage.clickQuest(questTitle);
await page.waitForTimeout(500);
await questsPage.editQuest({ status: newStatus });
await page.waitForTimeout(500);
(testContext as any).lastEditedStatus = newStatus;
});
When('I delete the quest', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const questTitle = (testContext as any).lastCreatedQuestTitle || (await questsPage.getQuestTitles())[0];
await questsPage.clickQuest(questTitle);
await page.waitForTimeout(500);
await questsPage.deleteQuest();
await page.waitForTimeout(500);
(testContext as any).deletedQuestTitle = questTitle;
});
When('I filter quests by status {string}', async ({ page }, status: string) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
await questsPage.filterByStatus(status);
(testContext as any).lastFilterType = 'status';
(testContext as any).lastFilterValue = status;
});
When('I filter quests by type {string}', async ({ page }, questType: string) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
await questsPage.filterByType(questType);
(testContext as any).lastFilterType = 'type';
(testContext as any).lastFilterValue = questType;
});
When('I filter quests by location {string}', async ({ page }, location: string) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
if (typeof questsPage.filterByLocation === 'function') {
await questsPage.filterByLocation(location);
}
(testContext as any).lastFilterType = 'location';
(testContext as any).lastFilterValue = location;
});
When('I click complete on the quest', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const questTitle = (testContext as any).lastCreatedQuestTitle || (await questsPage.getQuestTitles())[0];
await questsPage.clickQuest(questTitle);
await page.waitForTimeout(500);
await questsPage.completeQuest();
await page.waitForTimeout(500);
});
When('I fail the mini-game', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
if (typeof questsPage.failMiniGame === 'function') {
await questsPage.failMiniGame();
}
});
When('I win the mini-game', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
if (typeof questsPage.winMiniGame === 'function') {
await questsPage.winMiniGame();
}
});
When('I interact with the dark magic quest', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const questTitles = await questsPage.getQuestTitles();
const darkMagicQuest = questTitles.find(t => t.includes('Dark'));
if (darkMagicQuest) {
await questsPage.clickQuest(darkMagicQuest);
await page.waitForTimeout(500);
}
});
Then('the quest should appear in the quest list', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const questTitle = (testContext as any).lastCreatedQuestTitle;
const questTitles = await questsPage.getQuestTitles();
expect(questTitles).toContain(questTitle);
});
Then('the quest should have status {string}', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Then('the quest should be updated', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const questTitle = (testContext as any).lastCreatedQuestTitle;
const questTitles = await questsPage.getQuestTitles();
expect(questTitles).toContain(questTitle);
});
Then('the new status should be displayed', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Then('the quest should no longer appear in the quest list', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const testContext = global.fixtures.testContext;
const deletedQuestTitle = (testContext as any).deletedQuestTitle;
const questTitles = await questsPage.getQuestTitles();
expect(questTitles).not.toContain(deletedQuestTitle);
});
Then('only completed quests should be displayed', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Then('only {string} type quests should be displayed', async ({ page }, _questType: string) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Then('only quests in {string} should be displayed', async ({ page }, _location: string) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Then('a mini-game modal should appear', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const isMiniGameVisible = await questsPage.isMiniGameModalVisible();
expect(isMiniGameVisible).toBeTruthy();
});
Then('I should see one of: Trivia, Memory, Reaction, Find the Ring', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const miniGameType = await questsPage.getMiniGameType();
expect(miniGameType?.length || 0).toBeGreaterThan(0);
});
Then('I should see a {string} button', async ({ page }, buttonText: string) => {
const button = page.locator(`button:has-text("${buttonText}")`);
const isVisible = await button.isVisible({ timeout: 5000 });
expect(isVisible).toBeTruthy();
});
Then('I should be able to retry the mini-game', async ({ page }) => {
// Wait for modal to be visible
await page.locator('h2:has-text("Quest Challenge")').waitFor({ state: 'visible', timeout: 10000 });
// Wait for the retry button to appear
const retryButton = page.locator('button:has-text("Try Again")');
await retryButton.waitFor({ state: 'visible', timeout: 10000 });
expect(await retryButton.isVisible()).toBeTruthy();
});
Then('the mini-game modal should close', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
await questsPage.waitForMiniGameModalToClose();
const isMiniGameVisible = await questsPage.isMiniGameModalVisible();
expect(isMiniGameVisible).toBeFalsy();
});
Then('the quest status should change to {string}', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});
Then('I should receive gold reward', async ({ page }) => {
const testContext = global.fixtures.testContext;
expect(testContext).toBeDefined();
});
Then('the quest may intermittently appear or fail completion', async ({ page }) => {
const questsPage = global.fixtures.questsPage!;
const isLoaded = await questsPage.isLoaded();
expect(isLoaded).toBeTruthy();
});