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>
This commit is contained in:
@@ -2,6 +2,6 @@
|
||||
"repositoryUrl": "https://get.feep.ir/gold_land/gold_land_server.git",
|
||||
"checkoutPath": "backend-nest",
|
||||
"localRef": "",
|
||||
"pinnedRef": "dfb20ca92cb13f1d25c6aca64b6256c09176e603",
|
||||
"pinnedRef": "866be223956ef5d3961b0d835b36adedfeb35c82",
|
||||
"notes": "CI uses E2E_BACKEND_REF when set, otherwise pinnedRef (full SHA only). Never falls back to main. Local sibling: E2E_BACKEND_ROOT=../backend-nest and skip ci:checkout-backend."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
const perfectLessons: Record<number, Array<Record<string, 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' },
|
||||
],
|
||||
}
|
||||
|
||||
async function authHeader(page: import('@playwright/test').Page) {
|
||||
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
|
||||
if (!token) throw new Error('missing token')
|
||||
return { authorization: `Bearer ${token}` }
|
||||
}
|
||||
|
||||
test('budget mission from the map, then replay without extra reward', async ({ page }) => {
|
||||
const user = uniqueUsername('budget')
|
||||
await registerViaUi(page, user, 'pass1234', 'بودجه')
|
||||
const headers = await authHeader(page)
|
||||
|
||||
for (let levelId = 1; levelId <= 6; levelId += 1) {
|
||||
const start = await page.request.post(`/api/gold-land/levels/${levelId}/start`, { headers })
|
||||
expect(start.ok()).toBeTruthy()
|
||||
const done = await page.request.post(`/api/gold-land/levels/${levelId}/complete`, {
|
||||
headers,
|
||||
data: { answers: perfectLessons[levelId] },
|
||||
})
|
||||
expect(done.ok()).toBeTruthy()
|
||||
}
|
||||
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 7: بودجهبندی' }).click()
|
||||
await page.getByRole('button', { name: 'شروع بازی' }).click()
|
||||
await page.getByRole('button', { name: 'ساخت بودجه' }).click()
|
||||
await page.getByRole('button', { name: 'بررسی برنامه' }).click()
|
||||
await expect(page.getByRole('button', { name: 'تأیید برنامه' })).toBeDisabled()
|
||||
await expect(page.getByText('رفتوآمد حداقل ۲۰ سکه')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'اصلاح برنامه' }).click()
|
||||
|
||||
for (let i = 0; i < 4; i += 1) await page.getByRole('button', { name: 'افزایش رفتوآمد' }).click()
|
||||
for (let i = 0; i < 5; i += 1) await page.getByRole('button', { name: 'افزایش آب و غذا' }).click()
|
||||
for (let i = 0; i < 4; i += 1) await page.getByRole('button', { name: 'افزایش پسانداز برای هدف' }).click()
|
||||
await page.getByRole('button', { name: 'بررسی برنامه' }).click()
|
||||
await page.getByRole('button', { name: 'تأیید برنامه' }).click()
|
||||
await expect(page.getByText('هزینه رفتوآمد ۱۰ سکه بیشتر شد')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازتنظیم بودجه' }).click()
|
||||
await expect(page.getByRole('button', { name: 'ثبت نتیجه' })).toBeDisabled()
|
||||
await expect(page.getByText('حداقل ۳۰ سکه')).toBeVisible()
|
||||
for (let i = 0; i < 2; i += 1) await page.getByRole('button', { name: 'افزایش رفتوآمد' }).click()
|
||||
await expect(page.getByRole('button', { name: 'ثبت نتیجه' })).toBeEnabled()
|
||||
|
||||
const before = await fetchMe(page)
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await expect(page.getByText('پاداش:')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
await page.waitForURL(/\/map/)
|
||||
await page.reload()
|
||||
const after = await fetchMe(page)
|
||||
expect(after.progress.mapLevels.find((level) => level.id === 7)?.stars).toBe(3)
|
||||
expect(after.user.coins).toBe(before.user.coins + 18 + 9)
|
||||
|
||||
await page.getByRole('button', { name: 'مرحله 7: بودجهبندی' }).click()
|
||||
await page.getByRole('button', { name: 'مرور مجدد' }).click()
|
||||
await page.getByRole('button', { name: 'ساخت بودجه' }).click()
|
||||
for (let i = 0; i < 4; i += 1) await page.getByRole('button', { name: 'افزایش رفتوآمد' }).click()
|
||||
for (let i = 0; i < 5; i += 1) await page.getByRole('button', { name: 'افزایش آب و غذا' }).click()
|
||||
for (let i = 0; i < 4; i += 1) await page.getByRole('button', { name: 'افزایش پسانداز برای هدف' }).click()
|
||||
await page.getByRole('button', { name: 'بررسی برنامه' }).click()
|
||||
await page.getByRole('button', { name: 'تأیید برنامه' }).click()
|
||||
await page.getByRole('button', { name: 'بازتنظیم بودجه' }).click()
|
||||
for (let i = 0; i < 2; i += 1) await page.getByRole('button', { name: 'افزایش رفتوآمد' }).click()
|
||||
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)
|
||||
expect(replayAfter.user.xp).toBe(replayBefore.user.xp)
|
||||
|
||||
await page.goto('/map')
|
||||
await expect(page.getByRole('button', { name: 'مرحله 9: سبد سرمایهگذاری' })).toBeDisabled()
|
||||
})
|
||||
@@ -0,0 +1,112 @@
|
||||
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)
|
||||
})
|
||||
@@ -0,0 +1,149 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
const lessons: Record<number, Array<Record<string, 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' },
|
||||
],
|
||||
}
|
||||
|
||||
async function answerCompound(page: import('@playwright/test').Page) {
|
||||
const choices = [
|
||||
'رشد دوره بعد بر موجودی جدید، شامل رشد قبلی، محاسبه میشود',
|
||||
'۱۲۱ سکه',
|
||||
'چون ۱۰٪ از ۱۱۰ محاسبه شده است',
|
||||
'مرکب؛ ۱۳۳٫۱ در برابر ۱۳۰',
|
||||
]
|
||||
for (const label of choices) {
|
||||
await page.getByRole('button', { name: label, exact: true }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'نادرست', exact: true }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
}
|
||||
|
||||
test('compound interest lesson from the map', async ({ page }) => {
|
||||
const user = uniqueUsername('cmp')
|
||||
await registerViaUi(page, user, 'pass1234', 'مرکب')
|
||||
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
|
||||
const headers = { authorization: `Bearer ${token}` }
|
||||
|
||||
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()
|
||||
}
|
||||
expect((await page.request.post('/api/gold-land/levels/7/start', { headers })).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/7/complete', {
|
||||
headers,
|
||||
data: {
|
||||
answers: [
|
||||
{ questionId: 'initial', allocations: { transport: 20, food: 25, savings: 20, fun: 0 } },
|
||||
{ questionId: 'final', allocations: { transport: 30, food: 25, savings: 20, fun: 0 } },
|
||||
],
|
||||
},
|
||||
})).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/8/start', { headers })).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/8/complete', {
|
||||
headers,
|
||||
data: {
|
||||
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' },
|
||||
],
|
||||
},
|
||||
})).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/9/start', { headers })).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/9/complete', {
|
||||
headers,
|
||||
data: {
|
||||
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' },
|
||||
],
|
||||
},
|
||||
})).ok()).toBeTruthy()
|
||||
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 10: سود مرکب' }).click()
|
||||
await page.getByRole('button', { name: 'شروع درس' }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
await page.getByRole('button', { name: 'دوره بعد' }).click()
|
||||
await page.getByRole('button', { name: 'دوره بعد' }).click()
|
||||
await page.getByRole('button', { name: 'دوره بعد' }).click()
|
||||
await expect(page.getByText('۱۳۳٫۱ سکه').first()).toBeVisible()
|
||||
await expect(page.getByText('۱۲۱ + ۱۲٫۱ = ۱۳۳٫۱')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'دوره قبل' }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
await answerCompound(page)
|
||||
const before = await fetchMe(page)
|
||||
await page.getByRole('button', { name: 'اتمام درس' }).click()
|
||||
await expect(page.getByText('۱۰٪ آن ۱۱ سکه')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
await page.reload()
|
||||
const after = await fetchMe(page)
|
||||
expect(after.progress.mapLevels.find((level) => level.id === 10)?.stars).toBe(3)
|
||||
expect(after.user.coins).toBe(before.user.coins + 18 + 9)
|
||||
|
||||
await page.getByRole('button', { name: 'مرحله 10: سود مرکب' }).click()
|
||||
await page.getByRole('button', { name: 'مرور مجدد' }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
await answerCompound(page)
|
||||
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.goto('/map')
|
||||
await expect(page.getByRole('button', { name: 'مرحله 12: هدف مالی' })).toBeDisabled()
|
||||
})
|
||||
@@ -0,0 +1,89 @@
|
||||
import { test, expect, type Page } from '@playwright/test'
|
||||
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
async function finishThroughShop(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' }] },
|
||||
]
|
||||
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 answerGoal(page: Page) {
|
||||
const labels = [
|
||||
'خرید بسته مشخص با قیمت ۱۲۰ و برنامه پسانداز',
|
||||
'۹۰ سکه',
|
||||
'۶ هفته',
|
||||
'نیاز را تأمین کن و مبلغ پسانداز یا زمان هدف را اصلاح کن',
|
||||
'برنامه و زمان را دوباره بررسی و بهروز کن',
|
||||
]
|
||||
for (let index = 0; index < labels.length; index += 1) {
|
||||
await page.getByText(labels[index]!, { exact: true }).click()
|
||||
if (index < labels.length - 1) await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
else await page.getByRole('button', { name: 'مرور پاسخها' }).click()
|
||||
}
|
||||
}
|
||||
|
||||
async function buildPlan(page: Page) {
|
||||
await page.getByRole('button', { name: 'ساخت برنامه' }).click()
|
||||
await page.getByRole('button', { name: '15 سکه در هفته' }).click()
|
||||
await expect(page.getByText('برآورد زمان: 6 هفته')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'تأیید برنامه اولیه' }).click()
|
||||
await page.getByRole('button', { name: 'هفته دوم: 5 سکه' }).click()
|
||||
await page.getByRole('button', { name: 'ادامه: 15 سکه' }).click()
|
||||
await expect(page.getByText('کل زمان: 7 هفته')).toBeVisible()
|
||||
await expect(page.getByText('موجودی نهایی برآوردی: 125 سکه')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'رفتن به سؤالها' }).click()
|
||||
await answerGoal(page)
|
||||
}
|
||||
|
||||
test('financial goal from the map', async ({ page }) => {
|
||||
const user = uniqueUsername('goal')
|
||||
await registerViaUi(page, user, 'pass1234', 'هدف')
|
||||
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
|
||||
await finishThroughShop(page, { authorization: `Bearer ${token}` })
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 12: هدف مالی' }).click()
|
||||
await page.getByRole('button', { name: 'شروع چالش' }).click()
|
||||
await buildPlan(page)
|
||||
const before = await fetchMe(page)
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await expect(page.getByText('پاداش:')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
await page.reload()
|
||||
const after = await fetchMe(page)
|
||||
expect(after.progress.mapLevels.find((level) => level.id === 12)?.stars).toBe(3)
|
||||
expect(after.user.coins).toBe(before.user.coins + 20 + 9)
|
||||
|
||||
await page.getByRole('button', { name: 'مرحله 12: هدف مالی' }).click()
|
||||
await page.getByRole('button', { name: 'مرور مجدد' }).click()
|
||||
await buildPlan(page)
|
||||
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.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 13: آزمون قلعه' }).click()
|
||||
await expect(page.getByRole('button', { name: 'شروع آزمون' })).toBeEnabled()
|
||||
})
|
||||
@@ -0,0 +1,242 @@
|
||||
import { test, expect, type Page } from '@playwright/test'
|
||||
import { fetchMe, loginViaUi, logoutViaProfile, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
async function openLevel(page: Page, name: string, start: string) {
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name }).click()
|
||||
await page.getByRole('button', { name: start }).click()
|
||||
await page.getByText('در حال بارگذاری').waitFor({ state: 'detached', timeout: 30_000 }).catch(() => undefined)
|
||||
}
|
||||
|
||||
async function clickAnswer(page: Page, label: string) {
|
||||
if (label === 'درست' || label === 'نادرست') {
|
||||
await page.getByRole('button', { name: label, exact: true }).click()
|
||||
return
|
||||
}
|
||||
const button = page.getByRole('button', { name: label, exact: true })
|
||||
if (await button.count()) {
|
||||
await button.first().click()
|
||||
return
|
||||
}
|
||||
await page.getByText(label, { exact: true }).click()
|
||||
}
|
||||
|
||||
async function finishLesson(page: Page, labels: string[]) {
|
||||
let cursor = 0
|
||||
for (let step = 0; step < 30; step += 1) {
|
||||
if (await page.getByText('امتیاز').first().isVisible().catch(() => false)) return
|
||||
if (await page.getByText('خوراکی ضروری').isVisible().catch(() => false)) {
|
||||
for (let move = 0; move < 4; move += 1) {
|
||||
await page.locator('.sort-item').filter({ hasText: 'خوراکی ضروری' }).getByRole('button', { name: '↑' }).click()
|
||||
}
|
||||
}
|
||||
if (cursor < labels.length) {
|
||||
const label = labels[cursor]!
|
||||
const visible = await page.getByText(label, { exact: true }).isVisible().catch(() => false)
|
||||
if (visible) {
|
||||
await clickAnswer(page, label)
|
||||
cursor += 1
|
||||
}
|
||||
}
|
||||
const finish = page.getByRole('button', { name: 'اتمام درس' })
|
||||
if (await finish.isVisible().catch(() => false) && await finish.isEnabled().catch(() => false)) {
|
||||
await finish.click()
|
||||
await page.getByText('امتیاز').first().waitFor()
|
||||
return
|
||||
}
|
||||
const next = page.getByRole('button', { name: 'بعدی' })
|
||||
if (await next.isEnabled().catch(() => false)) await next.click()
|
||||
}
|
||||
throw new Error('lesson did not finish')
|
||||
}
|
||||
|
||||
async function starsOf(page: Page, levelId: number) {
|
||||
const me = await fetchMe(page)
|
||||
return me.progress.mapLevels.find((level) => level.id === levelId)?.stars ?? 0
|
||||
}
|
||||
|
||||
test('full thirteen-level journey from the interface', async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.name !== 'desktop', 'full journey runs once on desktop')
|
||||
test.setTimeout(600_000)
|
||||
const errors: string[] = []
|
||||
page.on('pageerror', (error) => errors.push(error.message))
|
||||
page.on('console', (message) => {
|
||||
if (message.type() === 'error') errors.push(message.text())
|
||||
})
|
||||
|
||||
const user = uniqueUsername('path')
|
||||
await registerViaUi(page, user, 'pass1234', 'مسیر')
|
||||
|
||||
const lessons: Array<{ name: string; labels: string[] }> = [
|
||||
{ name: 'مرحله 1: پول چیه؟', labels: ['مبادله آسانتر کالاها', 'درست', 'چون همه همیشه همان چیز را میخواهند', 'درست', 'پول وسیلهٔ مبادله و نگهداری ارزش است'] },
|
||||
{ name: 'مرحله 2: پسانداز', labels: ['بخشی از پول را برای بعد کنار گذاشتن', 'درست', 'برای هدفش برنامه نداشت و همه را خرج کرد', 'درست', 'هدف آینده چیزی است که برایش برنامه میریزیم'] },
|
||||
{ name: 'مرحله 3: نیاز و خواسته', labels: ['چیزی که برای زندگی سالم لازم است', 'درست', 'اول نیازها را برطرف کنیم', 'درست'] },
|
||||
{ name: 'مرحله 4: طلا چیه؟', labels: ['چون واحد استاندارد وزن طلا است', 'درست', 'زیورآلات و صنعت', 'نادرست', 'طلا دارایی با ارزش است که قیمتش میتواند تغییر کند'] },
|
||||
{ name: 'مرحله 5: بازار طلا', labels: ['قیمت بالا و پایین میشود', 'نادرست', 'چون ممکن است وقت کافی برای فکر کردن نداشته باشیم', 'درست', 'فکر کردن و پراکنده نکردن تمام پول در یک دارایی'] },
|
||||
{ name: 'مرحله 6: تورم', labels: ['افزایش عمومی قیمتها در طول زمان', 'درست', 'قدرت خرید پول کم شده', 'نادرست', 'ممکن است ارزش واقعیاش کم شود'] },
|
||||
]
|
||||
for (let index = 0; index < lessons.length; index += 1) {
|
||||
const lesson = lessons[index]!
|
||||
await openLevel(page, lesson.name, 'شروع درس')
|
||||
await finishLesson(page, lesson.labels)
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, index + 1)).toBeGreaterThan(0)
|
||||
if (index < 5) {
|
||||
await expect(page.getByRole('button', { name: lessons[index + 1]!.name })).toBeEnabled()
|
||||
}
|
||||
}
|
||||
await page.reload()
|
||||
expect(await starsOf(page, 6)).toBeGreaterThan(0)
|
||||
|
||||
await openLevel(page, 'مرحله 7: بودجهبندی', 'شروع بازی')
|
||||
await page.getByRole('button', { name: 'ساخت بودجه' }).click()
|
||||
for (let index = 0; index < 4; index += 1) await page.getByRole('button', { name: 'افزایش رفتوآمد' }).click()
|
||||
for (let index = 0; index < 5; index += 1) await page.getByRole('button', { name: 'افزایش آب و غذا' }).click()
|
||||
for (let index = 0; index < 4; index += 1) await page.getByRole('button', { name: 'افزایش پسانداز برای هدف' }).click()
|
||||
await page.getByRole('button', { name: 'بررسی برنامه' }).click()
|
||||
await page.getByRole('button', { name: 'تأیید برنامه' }).click()
|
||||
await page.getByRole('button', { name: 'بازتنظیم بودجه' }).click()
|
||||
for (let index = 0; index < 2; index += 1) await page.getByRole('button', { name: 'افزایش رفتوآمد' }).click()
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 7)).toBeGreaterThan(0)
|
||||
|
||||
await openLevel(page, 'مرحله 8: ریسک و بازده', 'شروع چالش')
|
||||
await page.getByRole('button', { name: 'دیدن دو پیشنهاد' }).click()
|
||||
await page.getByRole('button', { name: 'رفتن به فعالیتها' }).click()
|
||||
const risk = [
|
||||
'پیشنهاد ب',
|
||||
'خیر، ۳۶ فقط یکی از نتیجههای ممکن است',
|
||||
'این پول ضروری را وارد پیشنهادهای نامطمئن نکن',
|
||||
'پیشنهاد ب امکان نتیجه بالاتر و زیان بیشتری دارد؛ سود بیشتر قطعی نیست',
|
||||
'وعده را تضمین معتبر فرض نکن، بررسی کن و از بزرگسال قابل اعتماد کمک بگیر',
|
||||
]
|
||||
for (let index = 0; index < risk.length; index += 1) {
|
||||
await page.getByText(risk[index]!, { exact: true }).click()
|
||||
await page.getByRole('button', { name: index === risk.length - 1 ? 'مرور پاسخها' : 'بعدی' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 8)).toBeGreaterThan(0)
|
||||
|
||||
await openLevel(page, 'مرحله 9: سبد سرمایهگذاری', 'شروع بازی')
|
||||
await page.getByRole('button', { name: 'ساخت سبد' }).click()
|
||||
for (let index = 0; index < 4; index += 1) await page.getByRole('button', { name: 'افزایش آفتاب' }).click()
|
||||
for (let index = 0; index < 3; index += 1) await page.getByRole('button', { name: 'افزایش باران' }).click()
|
||||
for (let index = 0; index < 3; index += 1) await page.getByRole('button', { name: 'افزایش باد' }).click()
|
||||
await page.getByRole('button', { name: 'دیدن سه سناریو' }).click()
|
||||
await page.getByRole('button', { name: 'رفتن به سؤالها' }).click()
|
||||
const portfolio = [
|
||||
'نتیجه بیشتر به همان گزینه وابسته میشود',
|
||||
'خیر؛ افت همگانی میتواند چند گزینه را همزمان کم کند',
|
||||
'خیر؛ رفتار و وابستگی آنها مهم است',
|
||||
'خیر؛ یک سناریو آینده را تضمین نمیکند',
|
||||
'توجه به هدف، زمان نیاز به پول و ریسک؛ در مسائل واقعی کمک از بزرگسال قابل اعتماد',
|
||||
]
|
||||
for (let index = 0; index < portfolio.length; index += 1) {
|
||||
await page.getByText(portfolio[index]!, { exact: true }).click()
|
||||
await page.getByRole('button', { name: index === portfolio.length - 1 ? 'مرور پاسخها' : 'بعدی' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 9)).toBeGreaterThan(0)
|
||||
|
||||
await openLevel(page, 'مرحله 10: سود مرکب', 'شروع درس')
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
await page.getByRole('button', { name: 'دوره بعد' }).click()
|
||||
await page.getByRole('button', { name: 'دوره بعد' }).click()
|
||||
await page.getByRole('button', { name: 'دوره بعد' }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
const compound = [
|
||||
'رشد دوره بعد بر موجودی جدید، شامل رشد قبلی، محاسبه میشود',
|
||||
'۱۲۱ سکه',
|
||||
'چون ۱۰٪ از ۱۱۰ محاسبه شده است',
|
||||
'مرکب؛ ۱۳۳٫۱ در برابر ۱۳۰',
|
||||
]
|
||||
for (const label of compound) {
|
||||
await page.getByRole('button', { name: label, exact: true }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'نادرست', exact: true }).click()
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
await page.getByRole('button', { name: 'اتمام درس' }).click()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 10)).toBeGreaterThan(0)
|
||||
|
||||
await openLevel(page, 'مرحله 11: خرید هوشمند', 'شروع بازی')
|
||||
await page.getByRole('button', { name: 'شروع خریدها' }).click()
|
||||
const shop = [
|
||||
['دفتر ۶۰برگ، ۴۰ سکه، تحویل ۵ سکه', 'نیاز را برآورده میکند و هزینه نهاییاش بین گزینههای مناسب کمتر است'],
|
||||
['مداد با قیمت نهایی ۲۸، بدون تخفیف', 'برای کالای یکسان، مبلغ نهایی پرداختی مهم است؛ درصد تخفیف بهتنهایی کافی نیست'],
|
||||
['فعلاً نخر و اندازه و هزینه نهایی را بپرس، با کمک بزرگسال قابل اعتماد', 'اطلاعات لازم برای تصمیم و مقایسه هنوز کامل نیست'],
|
||||
]
|
||||
for (let index = 0; index < shop.length; index += 1) {
|
||||
await page.getByText(shop[index]![0]!, { exact: true }).click()
|
||||
await page.getByText(shop[index]![1]!, { exact: true }).click()
|
||||
await page.getByRole('button', { name: index === shop.length - 1 ? 'مرور انتخابها' : 'مأموریت بعد' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 11)).toBeGreaterThan(0)
|
||||
|
||||
await openLevel(page, 'مرحله 12: هدف مالی', 'شروع چالش')
|
||||
await page.getByRole('button', { name: 'ساخت برنامه' }).click()
|
||||
await page.getByRole('button', { name: '15 سکه در هفته' }).click()
|
||||
await page.getByRole('button', { name: 'تأیید برنامه اولیه' }).click()
|
||||
await page.getByRole('button', { name: 'هفته دوم: 5 سکه' }).click()
|
||||
await page.getByRole('button', { name: 'ادامه: 15 سکه' }).click()
|
||||
await page.getByRole('button', { name: 'رفتن به سؤالها' }).click()
|
||||
const goal = [
|
||||
'خرید بسته مشخص با قیمت ۱۲۰ و برنامه پسانداز',
|
||||
'۹۰ سکه',
|
||||
'۶ هفته',
|
||||
'نیاز را تأمین کن و مبلغ پسانداز یا زمان هدف را اصلاح کن',
|
||||
'برنامه و زمان را دوباره بررسی و بهروز کن',
|
||||
]
|
||||
for (let index = 0; index < goal.length; index += 1) {
|
||||
await page.getByText(goal[index]!, { exact: true }).click()
|
||||
await page.getByRole('button', { name: index === goal.length - 1 ? 'مرور پاسخها' : 'بعدی' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 12)).toBeGreaterThan(0)
|
||||
await page.reload()
|
||||
expect(await starsOf(page, 12)).toBeGreaterThan(0)
|
||||
await expect(page.getByRole('button', { name: 'مرحله 14', exact: false })).toHaveCount(0)
|
||||
|
||||
await openLevel(page, 'مرحله 13: آزمون قلعه', 'شروع آزمون')
|
||||
await page.getByRole('button', { name: 'شروع سؤالها' }).click()
|
||||
const castle = [
|
||||
'مبادله را آسان میکند و مقایسه قیمت را ممکن میسازد',
|
||||
'مبلغی متناسب با شرایط را منظم کنار بگذاریم',
|
||||
'اول آب آشامیدنی را تأمین کنیم',
|
||||
'طلا یک دارایی است و قیمتش میتواند بالا یا پایین برود',
|
||||
'خیر',
|
||||
'با همان پول، مقدار کمتری از آن کالاها میتوان خرید',
|
||||
'رفتوآمد ۳۰، غذا ۲۵، پسانداز ۲۰، تفریح ۱۵، ذخیره ۱۰',
|
||||
'پول لازم را وارد پیشنهاد نامطمئن با احتمال زیان نکنیم',
|
||||
'خیر؛ چند گزینه ممکن است همزمان افت کنند',
|
||||
'۱۲۱',
|
||||
'ب، چون ۴۵ در برابر ۵۰ است',
|
||||
'۶ هفته',
|
||||
]
|
||||
for (let index = 0; index < castle.length; index += 1) {
|
||||
await page.getByText(castle[index]!, { exact: true }).click()
|
||||
await page.getByRole('button', { name: index === castle.length - 1 ? 'مرور پاسخها' : 'بعدی' }).click()
|
||||
}
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await expect(page.getByText('آفرین! مسیر آموزشی قلعه را کامل کردی')).toBeVisible()
|
||||
await expect(page.getByRole('button', { name: 'مرحله بعد' })).toHaveCount(0)
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
expect(await starsOf(page, 13)).toBeGreaterThan(0)
|
||||
|
||||
await logoutViaProfile(page)
|
||||
await loginViaUi(page, user, 'pass1234')
|
||||
await page.goto('/map')
|
||||
for (let levelId = 1; levelId <= 13; levelId += 1) {
|
||||
expect(await starsOf(page, levelId)).toBeGreaterThan(0)
|
||||
}
|
||||
expect(errors.filter((error) => !/favicon|Download the React DevTools/i.test(error))).toEqual([])
|
||||
})
|
||||
@@ -0,0 +1,152 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
const lessons: Record<number, Array<Record<string, 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' },
|
||||
],
|
||||
}
|
||||
|
||||
async function answerPortfolio(page: import('@playwright/test').Page) {
|
||||
const choices = [
|
||||
'نتیجه بیشتر به همان گزینه وابسته میشود',
|
||||
'خیر؛ افت همگانی میتواند چند گزینه را همزمان کم کند',
|
||||
'خیر؛ رفتار و وابستگی آنها مهم است',
|
||||
'خیر؛ یک سناریو آینده را تضمین نمیکند',
|
||||
'توجه به هدف، زمان نیاز به پول و ریسک؛ در مسائل واقعی کمک از بزرگسال قابل اعتماد',
|
||||
]
|
||||
for (let index = 0; index < choices.length; index += 1) {
|
||||
await page.getByText(choices[index]!, { exact: true }).click()
|
||||
if (index < choices.length - 1) await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
else await page.getByRole('button', { name: 'مرور پاسخها' }).click()
|
||||
}
|
||||
}
|
||||
|
||||
test('portfolio game from the map', async ({ page }) => {
|
||||
const user = uniqueUsername('port')
|
||||
await registerViaUi(page, user, 'pass1234', 'سبد')
|
||||
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
|
||||
const headers = { authorization: `Bearer ${token}` }
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
expect((await page.request.post('/api/gold-land/levels/7/start', { headers })).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/7/complete', {
|
||||
headers,
|
||||
data: {
|
||||
answers: [
|
||||
{ questionId: 'initial', allocations: { transport: 20, food: 25, savings: 20, fun: 0 } },
|
||||
{ questionId: 'final', allocations: { transport: 30, food: 25, savings: 20, fun: 0 } },
|
||||
],
|
||||
},
|
||||
})).ok()).toBeTruthy()
|
||||
|
||||
expect((await page.request.post('/api/gold-land/levels/8/start', { headers })).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/8/complete', {
|
||||
headers,
|
||||
data: {
|
||||
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' },
|
||||
],
|
||||
},
|
||||
})).ok()).toBeTruthy()
|
||||
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 9: سبد سرمایهگذاری' }).click()
|
||||
await page.getByRole('button', { name: 'شروع بازی' }).click()
|
||||
await page.getByRole('button', { name: 'ساخت سبد' }).click()
|
||||
await expect(page.getByRole('button', { name: 'دیدن سه سناریو' })).toBeDisabled()
|
||||
|
||||
const sunUp = page.getByRole('button', { name: 'افزایش آفتاب' })
|
||||
const rainUp = page.getByRole('button', { name: 'افزایش باران' })
|
||||
const windUp = page.getByRole('button', { name: 'افزایش باد' })
|
||||
for (let index = 0; index < 4; index += 1) await sunUp.click()
|
||||
for (let index = 0; index < 3; index += 1) await rainUp.click()
|
||||
for (let index = 0; index < 3; index += 1) await windUp.click()
|
||||
await page.getByRole('button', { name: 'دیدن سه سناریو' }).click()
|
||||
await expect(page.getByText('سود 5 سکه')).toBeVisible()
|
||||
await expect(page.getByText('زیان 5 سکه')).toBeVisible()
|
||||
await expect(page.getByText('زیان 10 سکه').first()).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: 'تغییر ترکیب' }).click()
|
||||
await page.getByRole('button', { name: 'کاهش آفتاب' }).click()
|
||||
await page.getByRole('button', { name: 'افزایش باد' }).click()
|
||||
await page.getByRole('button', { name: 'دیدن سه سناریو' }).click()
|
||||
await page.getByRole('button', { name: 'رفتن به سؤالها' }).click()
|
||||
await answerPortfolio(page)
|
||||
|
||||
const before = await fetchMe(page)
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await expect(page.getByText('پاداش:')).toBeVisible()
|
||||
await expect(page.getByText('زیان را حذف یا سود را تضمین نمیکند')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
await page.reload()
|
||||
const after = await fetchMe(page)
|
||||
expect(after.progress.mapLevels.find((level) => level.id === 9)?.stars).toBe(3)
|
||||
expect(after.user.coins).toBe(before.user.coins + 20 + 9)
|
||||
|
||||
await page.getByRole('button', { name: 'مرحله 9: سبد سرمایهگذاری' }).click()
|
||||
await page.getByRole('button', { name: 'مرور مجدد' }).click()
|
||||
await page.getByRole('button', { name: 'ساخت سبد' }).click()
|
||||
for (let index = 0; index < 10; index += 1) await page.getByRole('button', { name: 'افزایش آفتاب' }).click()
|
||||
await page.getByRole('button', { name: 'دیدن سه سناریو' }).click()
|
||||
await page.getByRole('button', { name: 'رفتن به سؤالها' }).click()
|
||||
await answerPortfolio(page)
|
||||
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.goto('/map')
|
||||
await expect(page.getByRole('button', { name: 'مرحله 11: خرید هوشمند' })).toBeDisabled()
|
||||
})
|
||||
@@ -0,0 +1,123 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
const lessons: Record<number, Array<Record<string, 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' },
|
||||
],
|
||||
}
|
||||
|
||||
async function answerChallenge(page: import('@playwright/test').Page) {
|
||||
const choices = [
|
||||
'پیشنهاد ب',
|
||||
'خیر، ۳۶ فقط یکی از نتیجههای ممکن است',
|
||||
'این پول ضروری را وارد پیشنهادهای نامطمئن نکن',
|
||||
'پیشنهاد ب امکان نتیجه بالاتر و زیان بیشتری دارد؛ سود بیشتر قطعی نیست',
|
||||
'وعده را تضمین معتبر فرض نکن، بررسی کن و از بزرگسال قابل اعتماد کمک بگیر',
|
||||
]
|
||||
for (let index = 0; index < choices.length; index += 1) {
|
||||
await page.getByText(choices[index]!, { exact: true }).click()
|
||||
if (index < choices.length - 1) {
|
||||
await page.getByRole('button', { name: 'بعدی' }).click()
|
||||
} else {
|
||||
await page.getByRole('button', { name: 'مرور پاسخها' }).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test('risk and return challenge from the map', async ({ page }) => {
|
||||
const user = uniqueUsername('risk')
|
||||
await registerViaUi(page, user, 'pass1234', 'ریسک')
|
||||
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
|
||||
const headers = { authorization: `Bearer ${token}` }
|
||||
|
||||
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 budget = {
|
||||
answers: [
|
||||
{ questionId: 'initial', allocations: { transport: 20, food: 25, savings: 20, fun: 0 } },
|
||||
{ questionId: 'final', allocations: { transport: 30, food: 25, savings: 20, fun: 0 } },
|
||||
],
|
||||
}
|
||||
expect((await page.request.post('/api/gold-land/levels/7/start', { headers })).ok()).toBeTruthy()
|
||||
expect((await page.request.post('/api/gold-land/levels/7/complete', { headers, data: budget })).ok()).toBeTruthy()
|
||||
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 8: ریسک و بازده' }).click()
|
||||
await page.getByRole('button', { name: 'شروع چالش' }).click()
|
||||
await page.getByRole('button', { name: 'دیدن دو پیشنهاد' }).click()
|
||||
await expect(page.getByText('کارت 1 از 4: 8 سکه')).toBeVisible()
|
||||
await expect(page.getByText('کارت 4 از 4: 36 سکه')).toBeVisible()
|
||||
await expect(page.getByText('کارت 3 از 4: 22 سکه')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'رفتن به فعالیتها' }).click()
|
||||
await answerChallenge(page)
|
||||
|
||||
const before = await fetchMe(page)
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await expect(page.getByText('پاداش:')).toBeVisible()
|
||||
await expect(page.getByText('امکان سود بیشتر، تضمین سود نیست')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
await page.reload()
|
||||
const after = await fetchMe(page)
|
||||
expect(after.progress.mapLevels.find((level) => level.id === 8)?.stars).toBe(3)
|
||||
expect(after.user.coins).toBe(before.user.coins + 20 + 9)
|
||||
|
||||
await page.getByRole('button', { name: 'مرحله 8: ریسک و بازده' }).click()
|
||||
await page.getByRole('button', { name: 'مرور مجدد' }).click()
|
||||
await page.getByRole('button', { name: 'دیدن دو پیشنهاد' }).click()
|
||||
await page.getByRole('button', { name: 'رفتن به فعالیتها' }).click()
|
||||
await answerChallenge(page)
|
||||
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.goto('/map')
|
||||
await expect(page.getByRole('button', { name: 'مرحله 10: سود مرکب' })).toBeDisabled()
|
||||
})
|
||||
@@ -0,0 +1,135 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { fetchMe, registerViaUi, uniqueUsername } from './helpers/auth'
|
||||
|
||||
async function completePrior(page: import('@playwright/test').Page, headers: { authorization: string }) {
|
||||
const lessons: Record<number, Array<Record<string, 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 posts: Array<[string, unknown]> = [
|
||||
['7', { answers: [
|
||||
{ questionId: 'initial', allocations: { transport: 20, food: 25, savings: 20, fun: 0 } },
|
||||
{ questionId: 'final', allocations: { transport: 30, food: 25, savings: 20, fun: 0 } },
|
||||
] }],
|
||||
['8', { 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' },
|
||||
] }],
|
||||
['9', { 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' },
|
||||
] }],
|
||||
['10', { 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' },
|
||||
] }],
|
||||
]
|
||||
for (const [levelId, data] of posts) {
|
||||
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 })).ok()).toBeTruthy()
|
||||
}
|
||||
}
|
||||
|
||||
async function answerShop(page: import('@playwright/test').Page) {
|
||||
const picks = [
|
||||
['دفتر ۶۰برگ، ۴۰ سکه، تحویل ۵ سکه', 'نیاز را برآورده میکند و هزینه نهاییاش بین گزینههای مناسب کمتر است'],
|
||||
['مداد با قیمت نهایی ۲۸، بدون تخفیف', 'برای کالای یکسان، مبلغ نهایی پرداختی مهم است؛ درصد تخفیف بهتنهایی کافی نیست'],
|
||||
['فعلاً نخر و اندازه و هزینه نهایی را بپرس، با کمک بزرگسال قابل اعتماد', 'اطلاعات لازم برای تصمیم و مقایسه هنوز کامل نیست'],
|
||||
]
|
||||
for (let index = 0; index < picks.length; index += 1) {
|
||||
if (index === 2) await expect(page.getByText('نامشخص').first()).toBeVisible()
|
||||
await page.getByText(picks[index]![0]!, { exact: true }).click()
|
||||
await page.getByText(picks[index]![1]!, { exact: true }).click()
|
||||
await page.getByRole('button', { name: index === picks.length - 1 ? 'مرور انتخابها' : 'مأموریت بعد' }).click()
|
||||
}
|
||||
}
|
||||
|
||||
test('smart shop from the map', async ({ page }) => {
|
||||
const user = uniqueUsername('shop')
|
||||
await registerViaUi(page, user, 'pass1234', 'خرید')
|
||||
const token = await page.evaluate(() => localStorage.getItem('gold-land-auth-token'))
|
||||
await completePrior(page, { authorization: `Bearer ${token}` })
|
||||
|
||||
await page.goto('/map')
|
||||
await page.getByRole('button', { name: 'مرحله 11: خرید هوشمند' }).click()
|
||||
await page.getByRole('button', { name: 'شروع بازی' }).click()
|
||||
await page.getByRole('button', { name: 'شروع خریدها' }).click()
|
||||
await expect(page.getByText('بودجه:')).toBeVisible()
|
||||
await expect(page.getByText('جمع نهایی: ۴۵ سکه')).toBeVisible()
|
||||
await answerShop(page)
|
||||
const before = await fetchMe(page)
|
||||
await page.getByRole('button', { name: 'ثبت نتیجه' }).click()
|
||||
await expect(page.getByText('پاداش:')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'بازگشت به نقشه' }).click()
|
||||
await page.reload()
|
||||
const after = await fetchMe(page)
|
||||
expect(after.progress.mapLevels.find((level) => level.id === 11)?.stars).toBe(3)
|
||||
expect(after.user.coins).toBe(before.user.coins + 18 + 9)
|
||||
|
||||
await page.getByRole('button', { name: 'مرحله 11: خرید هوشمند' }).click()
|
||||
await page.getByRole('button', { name: 'مرور مجدد' }).click()
|
||||
await page.getByRole('button', { name: 'شروع خریدها' }).click()
|
||||
await answerShop(page)
|
||||
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.goto('/map')
|
||||
await expect(page.getByRole('button', { name: 'مرحله 13: آزمون قلعه' })).toBeDisabled()
|
||||
})
|
||||
Reference in New Issue
Block a user