feat: добавил тесты с использованием Playwright
Frontend CI / build-and-check (push) Failing after 19s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 20s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 3s
Frontend CI / build-and-check (push) Failing after 19s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 20s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 3s
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
name: Frontend Playwright
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: frontend
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: pnpm
|
||||||
|
cache-dependency-path: frontend/pnpm-lock.yaml
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build app
|
||||||
|
run: pnpm build-only
|
||||||
|
|
||||||
|
- name: Install Playwright browser
|
||||||
|
run: pnpm exec playwright install --with-deps chromium
|
||||||
|
|
||||||
|
- name: Run e2e
|
||||||
|
run: pnpm test:e2e
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# Playwright E2E тесты frontend
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
|
||||||
|
Playwright-тесты проверяют ключевые браузерные сценарии frontend-приложения UniVerse:
|
||||||
|
|
||||||
|
- редирект неавторизованного пользователя на страницу входа;
|
||||||
|
- отображение каталога открытых лекций;
|
||||||
|
- запись на доступную лекцию.
|
||||||
|
|
||||||
|
Тесты работают поверх production preview сборки frontend и используют mock API, поэтому для базового запуска не нужен поднятый backend.
|
||||||
|
|
||||||
|
## Где лежат файлы
|
||||||
|
|
||||||
|
- [playwright.config.ts](../frontend/playwright.config.ts) - конфигурация Playwright.
|
||||||
|
- [auth.spec.ts](../frontend/tests/e2e/auth.spec.ts) - сценарии аутентификации.
|
||||||
|
- [catalog.spec.ts](../frontend/tests/e2e/catalog.spec.ts) - сценарии каталога лекций.
|
||||||
|
- [mockApi.ts](../frontend/tests/e2e/support/mockApi.ts) - перехват и mock ответов API.
|
||||||
|
- [fixtures.ts](../frontend/tests/mocks/fixtures.ts) - тестовые данные.
|
||||||
|
|
||||||
|
## Как устроен запуск
|
||||||
|
|
||||||
|
Конфигурация находится во `frontend/playwright.config.ts`.
|
||||||
|
|
||||||
|
Основные параметры:
|
||||||
|
|
||||||
|
- `testDir: ./tests/e2e` - Playwright ищет тесты в папке `frontend/tests/e2e`.
|
||||||
|
- `baseURL: http://127.0.0.1:4173` - базовый адрес приложения в тестах.
|
||||||
|
- `webServer` запускает `pnpm preview --host 127.0.0.1 --port 4173`.
|
||||||
|
- В CI включены `2` retry и GitHub reporter.
|
||||||
|
- Локально используется list reporter.
|
||||||
|
- По умолчанию проект запускается в Chromium.
|
||||||
|
|
||||||
|
## Команды
|
||||||
|
|
||||||
|
Запуск всех E2E-тестов из корня репозитория:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm -C frontend test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Интерактивный UI Playwright:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm -C frontend test:e2e:ui
|
||||||
|
```
|
||||||
|
|
||||||
|
Если нужно вручную поднять preview-сервер:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm -C frontend build-only
|
||||||
|
pnpm -C frontend test:e2e:preview
|
||||||
|
```
|
||||||
|
|
||||||
|
После этого можно запускать Playwright с переменной `PW_SKIP_WEB_SERVER=1`, чтобы он не стартовал свой `webServer`.
|
||||||
|
|
||||||
|
## Mock API
|
||||||
|
|
||||||
|
Тесты не обращаются к реальному backend. Вместо этого helper `mockApi(page, options)` перехватывает запросы к `/api/v1` через `page.route`.
|
||||||
|
|
||||||
|
Сейчас замоканы:
|
||||||
|
|
||||||
|
- `POST/GET /api/v1/auth/refresh` - refresh авторизации;
|
||||||
|
- `/api/v1/auth/me` - текущий пользователь;
|
||||||
|
- `/api/v1/users/me/stats` - статистика студента;
|
||||||
|
- `/api/v1/lectures` - список лекций;
|
||||||
|
- `/api/v1/lectures/{id}/enroll` - запись на лекцию.
|
||||||
|
|
||||||
|
Для авторизованного сценария используйте:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await mockApi(page, { authenticated: true })
|
||||||
|
```
|
||||||
|
|
||||||
|
Для проверки гостевого сценария:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await mockApi(page, { authenticated: false })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Как добавлять новые тесты
|
||||||
|
|
||||||
|
1. Создавайте spec-файлы в `frontend/tests/e2e`.
|
||||||
|
2. Для страниц, которым нужен backend, сначала добавляйте нужные ответы в `mockApi.ts` и данные в `fixtures.ts`.
|
||||||
|
3. Проверяйте пользовательский результат через role/text/label locators: `getByRole`, `getByText`, `getByLabel`.
|
||||||
|
4. Не завязывайтесь на CSS-классы, если сценарий можно проверить через доступные пользователю элементы.
|
||||||
|
5. Для маршрутов под авторизацией вызывайте `mockApi(page, { authenticated: true })` до `page.goto(...)`.
|
||||||
|
|
||||||
|
## CI
|
||||||
|
|
||||||
|
Workflow находится в [.gitea/workflows/frontend-playwright.yml](../.gitea/workflows/frontend-playwright.yml).
|
||||||
|
|
||||||
|
Пайплайн:
|
||||||
|
|
||||||
|
1. Устанавливает зависимости через `pnpm install --frozen-lockfile`.
|
||||||
|
2. Собирает frontend командой `pnpm build-only`.
|
||||||
|
3. Устанавливает браузер Playwright Chromium.
|
||||||
|
4. Запускает `pnpm test:e2e`.
|
||||||
|
|
||||||
|
## Артефакты и отладка
|
||||||
|
|
||||||
|
Playwright сохраняет trace на первом retry: `trace: on-first-retry`.
|
||||||
|
|
||||||
|
Локально полезные команды:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm -C frontend exec playwright show-report
|
||||||
|
pnpm -C frontend exec playwright show-trace ./test-results/<папка>/trace.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
Папки `frontend/test-results` и `frontend/playwright-report` считаются временными артефактами тестовых прогонов.
|
||||||
@@ -35,5 +35,10 @@ coverage
|
|||||||
# Vitest
|
# Vitest
|
||||||
__screenshots__/
|
__screenshots__/
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
|
||||||
# Vite
|
# Vite
|
||||||
*.timestamp-*-*.mjs
|
*.timestamp-*-*.mjs
|
||||||
|
|||||||
@@ -13,7 +13,11 @@
|
|||||||
"lint": "run-s lint:*",
|
"lint": "run-s lint:*",
|
||||||
"lint:oxlint": "oxlint . --fix",
|
"lint:oxlint": "oxlint . --fix",
|
||||||
"lint:eslint": "eslint . --fix --cache",
|
"lint:eslint": "eslint . --fix --cache",
|
||||||
"format": "prettier --write --experimental-cli src/"
|
"format": "prettier --write --experimental-cli src/",
|
||||||
|
"test:e2e:preview": "vite preview --host 127.0.0.1 --port 4173",
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"test:e2e:ui": "playwright test --ui",
|
||||||
|
"test:e2e:ui:edge": "PW_USE_SYSTEM_EDGE=1 PW_SKIP_WEB_SERVER=1 playwright test --ui --timeout=0 --workers=1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
@@ -38,7 +42,8 @@
|
|||||||
"typescript": "~6.0.0",
|
"typescript": "~6.0.0",
|
||||||
"vite": "^8.0.8",
|
"vite": "^8.0.8",
|
||||||
"vite-plugin-vue-devtools": "^8.1.1",
|
"vite-plugin-vue-devtools": "^8.1.1",
|
||||||
"vue-tsc": "^3.2.6"
|
"vue-tsc": "^3.2.6",
|
||||||
|
"@playwright/test": "^1.54.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
|
||||||
|
const useSystemEdge = process.env.PW_USE_SYSTEM_EDGE === '1'
|
||||||
|
const skipWebServer = process.env.PW_SKIP_WEB_SERVER === '1'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './tests/e2e',
|
||||||
|
fullyParallel: true,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
reporter: process.env.CI ? 'github' : 'list',
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://127.0.0.1:4173',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
webServer: skipWebServer
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
command: 'pnpm preview --host 127.0.0.1 --port 4173',
|
||||||
|
url: 'http://127.0.0.1:4173',
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
cwd: '.',
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: useSystemEdge ? 'msedge' : 'chromium',
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
...(useSystemEdge ? { channel: 'msedge' } : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
Generated
+38
@@ -18,6 +18,9 @@ importers:
|
|||||||
specifier: ^5.0.6
|
specifier: ^5.0.6
|
||||||
version: 5.0.6(@vue/compiler-sfc@3.5.34)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3))
|
version: 5.0.6(@vue/compiler-sfc@3.5.34)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.34(typescript@6.0.3)))(vue@3.5.34(typescript@6.0.3))
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@playwright/test':
|
||||||
|
specifier: ^1.54.2
|
||||||
|
version: 1.54.2
|
||||||
'@tsconfig/node24':
|
'@tsconfig/node24':
|
||||||
specifier: ^24.0.4
|
specifier: ^24.0.4
|
||||||
version: 24.0.4
|
version: 24.0.4
|
||||||
@@ -433,6 +436,11 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@playwright/test@1.54.2':
|
||||||
|
resolution: {integrity: sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.29':
|
'@polka/url@1.0.0-next.29':
|
||||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||||
|
|
||||||
@@ -1008,6 +1016,11 @@ packages:
|
|||||||
flatted@3.4.2:
|
flatted@3.4.2:
|
||||||
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
|
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -1347,6 +1360,16 @@ packages:
|
|||||||
pkg-types@2.3.1:
|
pkg-types@2.3.1:
|
||||||
resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
|
resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
|
||||||
|
|
||||||
|
playwright-core@1.54.2:
|
||||||
|
resolution: {integrity: sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
playwright@1.54.2:
|
||||||
|
resolution: {integrity: sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
postcss-safe-parser@7.0.1:
|
postcss-safe-parser@7.0.1:
|
||||||
resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==}
|
resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==}
|
||||||
engines: {node: '>=18.0'}
|
engines: {node: '>=18.0'}
|
||||||
@@ -2011,6 +2034,10 @@ snapshots:
|
|||||||
'@oxlint/binding-win32-x64-msvc@1.60.0':
|
'@oxlint/binding-win32-x64-msvc@1.60.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@playwright/test@1.54.2':
|
||||||
|
dependencies:
|
||||||
|
playwright: 1.54.2
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.29': {}
|
'@polka/url@1.0.0-next.29': {}
|
||||||
|
|
||||||
'@rolldown/binding-android-arm64@1.0.0-rc.17':
|
'@rolldown/binding-android-arm64@1.0.0-rc.17':
|
||||||
@@ -2606,6 +2633,9 @@ snapshots:
|
|||||||
|
|
||||||
flatted@3.4.2: {}
|
flatted@3.4.2: {}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -2889,6 +2919,14 @@ snapshots:
|
|||||||
exsolve: 1.0.8
|
exsolve: 1.0.8
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
playwright-core@1.54.2: {}
|
||||||
|
|
||||||
|
playwright@1.54.2:
|
||||||
|
dependencies:
|
||||||
|
playwright-core: 1.54.2
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
postcss-safe-parser@7.0.1(postcss@8.5.14):
|
postcss-safe-parser@7.0.1(postcss@8.5.14):
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.5.14
|
postcss: 8.5.14
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { mockApi } from './support/mockApi'
|
||||||
|
|
||||||
|
test('redirects unauthenticated user to login', async ({ page }) => {
|
||||||
|
await mockApi(page, { authenticated: false })
|
||||||
|
await page.goto('/catalog')
|
||||||
|
await expect(page).toHaveURL(/\/login/)
|
||||||
|
await expect(page.getByText('Войти через ЮФУ')).toBeVisible()
|
||||||
|
})
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { expect, test } from '@playwright/test'
|
||||||
|
import { mockApi } from './support/mockApi'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await mockApi(page, { authenticated: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('renders catalog items from mock api', async ({ page }) => {
|
||||||
|
await page.goto('/catalog')
|
||||||
|
|
||||||
|
await expect(page.getByRole('heading', { name: 'Каталог открытых лекций' })).toBeVisible()
|
||||||
|
await expect(page.getByText('Введение в ML')).toBeVisible()
|
||||||
|
await expect(page.getByText('Квантовые вычисления')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('register button works for available lecture', async ({ page }) => {
|
||||||
|
await page.goto('/catalog')
|
||||||
|
|
||||||
|
const firstRegisterButton = page.getByRole('button', { name: 'Записаться' }).first()
|
||||||
|
await firstRegisterButton.click()
|
||||||
|
|
||||||
|
await expect(page.getByText('Вы записаны на лекцию.')).toBeVisible()
|
||||||
|
})
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { Page } from '@playwright/test'
|
||||||
|
import { mockAuthResponse, mockCurrentUser, mockLectures, mockUserStats } from '../../mocks/fixtures'
|
||||||
|
|
||||||
|
export async function mockApi(page: Page, options?: { authenticated?: boolean }) {
|
||||||
|
const authenticated = options?.authenticated ?? false
|
||||||
|
|
||||||
|
await page.route('**/api/v1/auth/refresh', async (route) => {
|
||||||
|
if (!authenticated) {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 401,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ message: 'Unauthorized' }),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify(mockAuthResponse),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.route('**/api/v1/auth/me', async (route) => {
|
||||||
|
if (!authenticated) {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 401,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ message: 'Unauthorized' }),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify(mockCurrentUser),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.route('**/api/v1/users/me/stats', async (route) => {
|
||||||
|
if (!authenticated) {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 401,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ message: 'Unauthorized' }),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify(mockUserStats),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.route('**/api/v1/lectures/*/enroll', async (route) => {
|
||||||
|
await route.fulfill({ status: 204 })
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.route(/\/api\/v1\/lectures(\?.*)?$/, async (route) => {
|
||||||
|
if (route.request().method() !== 'GET') {
|
||||||
|
await route.continue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ items: mockLectures }),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
export const mockAuthResponse = {
|
||||||
|
accessToken: 'fake-token',
|
||||||
|
expiresAt: '2099-01-01T00:00:00.000Z',
|
||||||
|
user: {
|
||||||
|
id: 1,
|
||||||
|
email: 'student@example.com',
|
||||||
|
displayName: 'Test User',
|
||||||
|
roles: ['Student'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mockCurrentUser = {
|
||||||
|
id: 1,
|
||||||
|
email: 'student@example.com',
|
||||||
|
displayName: 'Test User',
|
||||||
|
roles: ['Student'],
|
||||||
|
avatarUrl: null,
|
||||||
|
xp: 120,
|
||||||
|
coins: 10,
|
||||||
|
level: 2,
|
||||||
|
createdAt: '2026-01-01T00:00:00.000Z',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mockUserStats = {
|
||||||
|
totalLectures: 0,
|
||||||
|
attendedLectures: 0,
|
||||||
|
totalReviews: 0,
|
||||||
|
xp: 120,
|
||||||
|
coins: 10,
|
||||||
|
level: 2,
|
||||||
|
achievementsCount: 0,
|
||||||
|
currentLevelXp: 20,
|
||||||
|
nextLevelXp: 100,
|
||||||
|
activeEnrollments: 0,
|
||||||
|
enrollmentSlotLimit: 3,
|
||||||
|
enrollmentSlotRules: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mockLectures = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
courseId: 101,
|
||||||
|
courseName: 'ML',
|
||||||
|
teacherId: 201,
|
||||||
|
teacherName: 'Иванов И.И.',
|
||||||
|
locationId: 301,
|
||||||
|
locationName: 'B-1 / 101',
|
||||||
|
title: 'Введение в ML',
|
||||||
|
description: 'База машинного обучения',
|
||||||
|
format: 'Offline',
|
||||||
|
startsAt: '2026-06-12T10:00:00.000Z',
|
||||||
|
endsAt: '2026-06-12T11:30:00.000Z',
|
||||||
|
isOpen: true,
|
||||||
|
maxEnrollments: 30,
|
||||||
|
enrollmentsCount: 10,
|
||||||
|
onlineUrl: null,
|
||||||
|
createdAt: '2026-01-01T00:00:00.000Z',
|
||||||
|
isEnrolled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
courseId: 102,
|
||||||
|
courseName: 'квантовые-вычисления',
|
||||||
|
teacherId: 202,
|
||||||
|
teacherName: 'Петров П.П.',
|
||||||
|
locationId: null,
|
||||||
|
locationName: null,
|
||||||
|
title: 'Квантовые вычисления',
|
||||||
|
description: 'Кубиты и алгоритмы',
|
||||||
|
format: 'Online',
|
||||||
|
startsAt: '2026-06-13T10:00:00.000Z',
|
||||||
|
endsAt: '2026-06-13T11:00:00.000Z',
|
||||||
|
isOpen: false,
|
||||||
|
maxEnrollments: 50,
|
||||||
|
enrollmentsCount: 50,
|
||||||
|
onlineUrl: 'https://example.com/meet',
|
||||||
|
createdAt: '2026-01-01T00:00:00.000Z',
|
||||||
|
isEnrolled: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user