Files
gold_land_user/e2e/castle-exam.spec.ts
T
safavimohadeseh78andCursor c53a67831d
Frontend CI / unit (test, type-check, build) (push) Successful in 7m21s
Frontend CI / browser E2E (frontend + pinned backend) (push) Successful in 11m38s
feat: complete the thirteen-stage learning experience and E2E journey
Play levels 7-13 in the app and pin E2E to the local thirteen-stage backend commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 19:42:04 +03:30

113 lines
8.6 KiB
TypeScript

import { test, expect, type Page } from '@playwright/test'
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
const correct = [
'مبادله را آسان می‌کند و مقایسه قیمت را ممکن می‌سازد',
'مبلغی متناسب با شرایط را منظم کنار بگذاریم',
'اول آب آشامیدنی را تأمین کنیم',
'طلا یک دارایی است و قیمتش می‌تواند بالا یا پایین برود',
'خیر',
'با همان پول، مقدار کمتری از آن کالاها می‌توان خرید',
'رفت‌وآمد ۳۰، غذا ۲۵، پس‌انداز ۲۰، تفریح ۱۵، ذخیره ۱۰',
'پول لازم را وارد پیشنهاد نامطمئن با احتمال زیان نکنیم',
'خیر؛ چند گزینه ممکن است هم‌زمان افت کنند',
'۱۲۱',
'ب، چون ۴۵ در برابر ۵۰ است',
'۶ هفته',
]
const wrongTail = [
'همان پول ضروری را برای سود بیشتر وارد پیشنهاد نامطمئن کنیم',
'بله، با چند گزینه دیگر زیان ممکن نیست',
'۱۲۰',
'الف، چون عدد روی کارت کوچک‌تر است',
'۸ هفته',
]
async function finishThroughGoal(page: Page, headers: { authorization: string }) {
const lessons: Record<number, unknown> = {
1: [{ questionId: 'money-q1', answerId: 'option-a' }, { questionId: 'money-q2', answerId: 'true' }, { questionId: 'money-q3', answerId: 'option-a' }, { questionId: 'money-q4', answerId: 'true' }, { questionId: 'money-q5', answerId: 'option-b' }],
2: [{ questionId: 'saving-q1', answerId: 'option-b' }, { questionId: 'saving-q2', answerId: 'true' }, { questionId: 'saving-q3', answerId: 'option-a' }, { questionId: 'saving-q4', answerId: 'true' }, { questionId: 'saving-q5', answerId: 'option-a' }],
3: [{ questionId: 'needs-q1', answerId: 'option-a' }, { questionId: 'needs-q2', answerId: 'true' }, { questionId: 'needs-q3', orderedItemIds: ['water', 'food', 'medicine', 'shoes', 'toy', 'crown'] }, { questionId: 'needs-q4', answerId: 'option-a' }, { questionId: 'needs-q5', answerId: 'true' }],
4: [{ questionId: 'gold-q1', answerId: 'option-a' }, { questionId: 'gold-q2', answerId: 'true' }, { questionId: 'gold-q3', answerId: 'option-a' }, { questionId: 'gold-q4', answerId: 'false' }, { questionId: 'gold-q5', answerId: 'option-b' }],
5: [{ questionId: 'market-q1', answerId: 'option-a' }, { questionId: 'market-q2', answerId: 'false' }, { questionId: 'market-q3', answerId: 'option-a' }, { questionId: 'market-q4', answerId: 'true' }, { questionId: 'market-q5', answerId: 'option-b' }],
6: [{ questionId: 'inflation-q1', answerId: 'option-b' }, { questionId: 'inflation-q2', answerId: 'true' }, { questionId: 'inflation-q3', answerId: 'option-a' }, { questionId: 'inflation-q4', answerId: 'false' }, { questionId: 'inflation-q5', answerId: 'option-b' }],
}
for (let levelId = 1; levelId <= 6; levelId += 1) {
expect((await page.request.post(`/api/gold-land/levels/${levelId}/start`, { headers })).ok()).toBeTruthy()
expect((await page.request.post(`/api/gold-land/levels/${levelId}/complete`, { headers, data: { answers: lessons[levelId] } })).ok()).toBeTruthy()
}
const later = [
{ answers: [{ questionId: 'initial', allocations: { transport: 20, food: 25, savings: 20, fun: 0 } }, { questionId: 'final', allocations: { transport: 30, food: 25, savings: 20, fun: 0 } }] },
{ answers: [{ questionId: 'q-loss', answerId: 'b' }, { questionId: 'q-certain', answerId: 'no' }, { questionId: 'q-need', answerId: 'keep' }, { questionId: 'q-compare', answerId: 'range' }, { questionId: 'q-promise', answerId: 'check' }] },
{ answers: [{ questionId: 'portfolio', holdings: { sun: 40, rain: 30, wind: 30 } }, { questionId: 'q-one', answerId: 'depend' }, { questionId: 'q-loss', answerId: 'no' }, { questionId: 'q-name', answerId: 'no' }, { questionId: 'q-best', answerId: 'no' }, { questionId: 'q-approach', answerId: 'balanced' }] },
{ answers: [{ questionId: 'compound-q1', answerId: 'on-balance' }, { questionId: 'compound-q2', answerId: '121' }, { questionId: 'compound-q3', answerId: 'from-110' }, { questionId: 'compound-q4', answerId: 'compound' }, { questionId: 'compound-q5', answerId: 'false' }] },
{ answers: [{ questionId: 'notebook-choice', answerId: 'b' }, { questionId: 'notebook-why', answerId: 'fits-and-cheaper' }, { questionId: 'discount-choice', answerId: 'b' }, { questionId: 'discount-why', answerId: 'pay-amount' }, { questionId: 'info-choice', answerId: 'ask' }, { questionId: 'info-why', answerId: 'incomplete' }] },
{ answers: [{ questionId: 'plan', plan: { initialWeekly: 15, exceptionWeekly: 5, continueWeekly: 15 } }, { questionId: 'q-goal', answerId: 'specific' }, { questionId: 'q-left', answerId: '90' }, { questionId: 'q-weeks', answerId: '6' }, { questionId: 'q-need', answerId: 'adjust' }, { questionId: 'q-change', answerId: 'update' }] },
]
for (let index = 0; index < later.length; index += 1) {
const levelId = index + 7
expect((await page.request.post(`/api/gold-land/levels/${levelId}/start`, { headers })).ok()).toBeTruthy()
expect((await page.request.post(`/api/gold-land/levels/${levelId}/complete`, { headers, data: later[index] })).ok()).toBeTruthy()
}
}
async function answerExam(page: Page, labels: string[]) {
for (let index = 0; index < labels.length; index += 1) {
await page.getByText(labels[index]!, { exact: true }).click()
const name = index === labels.length - 1 ? 'مرور پاسخ‌ها' : 'بعدی'
await page.getByRole('button', { name }).click()
}
}
test('castle exam from the map', async ({ page }) => {
const user = uniqueUsername('castle')
await registerViaUi(page, user, 'pass1234', 'قلعه')
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
await finishThroughGoal(page, { authorization: `Bearer ${token}` })
await page.goto('/map')
await page.getByRole('button', { name: 'مرحله 13: آزمون قلعه' }).click()
await page.getByRole('button', { name: 'شروع آزمون' }).click()
await page.getByRole('button', { name: 'شروع سؤال‌ها' }).click()
await expect(page.getByText('پایه‌های پول')).toBeVisible()
const beforeFail = await fetchMe(page)
await answerExam(page, [...correct.slice(0, 7), ...wrongTail])
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
await expect(page.getByText('این تلاش کامل نشد')).toBeVisible()
await expect(page.getByRole('button', { name: 'مرحله بعد' })).toHaveCount(0)
const afterFail = await fetchMe(page)
expect(afterFail.user.coins).toBe(beforeFail.user.coins)
await page.getByRole('button', { name: 'تلاش مجدد' }).click()
await answerExam(page, correct)
const before = await fetchMe(page)
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
await expect(page.getByText('آفرین! مسیر آموزشی قلعه را کامل کردی')).toBeVisible()
await expect(page.getByText('پاداش:')).toBeVisible()
await expect(page.getByRole('button', { name: 'مرحله بعد' })).toHaveCount(0)
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
await page.reload()
const after = await fetchMe(page)
expect(after.progress.mapLevels.find((level) => level.id === 13)?.stars).toBe(3)
expect(after.user.coins).toBe(before.user.coins + 50 + 9)
await page.getByRole('button', { name: 'مرحله 13: آزمون قلعه' }).click()
await page.getByRole('button', { name: 'مرور مجدد' }).click()
await page.getByRole('button', { name: 'شروع سؤال‌ها' }).click()
await answerExam(page, correct)
const replayBefore = await fetchMe(page)
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
await expect(page.getByText('پاداش تازه‌ای اضافه نشد')).toBeVisible()
const replayAfter = await fetchMe(page)
expect(replayAfter.user.coins).toBe(replayBefore.user.coins)
await page.getByRole('button', { name: 'تلاش مجدد' }).click()
await answerExam(page, [...correct.slice(0, 7), ...wrongTail])
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
await expect(page.getByText('این تلاش کامل نشد')).toBeVisible()
await expect(page.getByText('ستاره ذخیره‌شده همچنان 3 از ۳ است')).toBeVisible()
await expect(page.getByText('آفرین! مسیر آموزشی قلعه را کامل کردی')).toHaveCount(0)
const kept = await fetchMe(page)
expect(kept.progress.mapLevels.find((level) => level.id === 13)?.stars).toBe(3)
expect(kept.user.coins).toBe(replayAfter.user.coins)
})