Merge branch 'test/e2e-setup' into dev
This commit is contained in:
		
						commit
						37d88cfd24
					
				
					 15 changed files with 634 additions and 2982 deletions
				
			
		|  | @ -12,7 +12,7 @@ | ||||||
|         "format": "prettier --write src/", |         "format": "prettier --write src/", | ||||||
|         "format-check": "prettier --check src/", |         "format-check": "prettier --check src/", | ||||||
|         "lint": "eslint . --fix", |         "lint": "eslint . --fix", | ||||||
|         "pretest:unit": "npm run build", |         "pretest:unit": "tsx ../docs/api/generate.ts && npm run build", | ||||||
|         "test:unit": "vitest --run" |         "test:unit": "vitest --run" | ||||||
|     }, |     }, | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|  | @ -26,7 +26,6 @@ | ||||||
|         "cross": "^1.0.0", |         "cross": "^1.0.0", | ||||||
|         "cross-env": "^7.0.3", |         "cross-env": "^7.0.3", | ||||||
|         "dotenv": "^16.4.7", |         "dotenv": "^16.4.7", | ||||||
|         "dwengo-1-common": "^0.1.1", |  | ||||||
|         "express": "^5.0.1", |         "express": "^5.0.1", | ||||||
|         "express-jwt": "^8.5.1", |         "express-jwt": "^8.5.1", | ||||||
|         "gift-pegjs": "^1.0.2", |         "gift-pegjs": "^1.0.2", | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ import { getGroupRepository } from '../data/repositories.js'; | ||||||
| import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; | import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; | ||||||
| import { Class } from '../entities/classes/class.entity.js'; | import { Class } from '../entities/classes/class.entity.js'; | ||||||
| import { StudentDTO } from '@dwengo-1/common/interfaces/student'; | import { StudentDTO } from '@dwengo-1/common/interfaces/student'; | ||||||
| import { mapToClassDTO } from './class'; | import { mapToClassDTO } from './class.js'; | ||||||
| 
 | 
 | ||||||
| export function mapToGroup(groupDto: GroupDTO, clazz: Class): Group { | export function mapToGroup(groupDto: GroupDTO, clazz: Class): Group { | ||||||
|     const assignmentDto = groupDto.assignment as AssignmentDTO; |     const assignmentDto = groupDto.assignment as AssignmentDTO; | ||||||
|  |  | ||||||
|  | @ -2,9 +2,9 @@ import { Submission } from '../entities/assignments/submission.entity.js'; | ||||||
| import { mapToGroupDTO } from './group.js'; | import { mapToGroupDTO } from './group.js'; | ||||||
| import { mapToStudentDTO } from './student.js'; | import { mapToStudentDTO } from './student.js'; | ||||||
| import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission'; | import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission'; | ||||||
| import { getSubmissionRepository } from '../data/repositories'; | import { getSubmissionRepository } from '../data/repositories.js'; | ||||||
| import { Student } from '../entities/users/student.entity'; | import { Student } from '../entities/users/student.entity.js'; | ||||||
| import { Group } from '../entities/assignments/group.entity'; | import { Group } from '../entities/assignments/group.entity.js'; | ||||||
| 
 | 
 | ||||||
| export function mapToSubmissionDTO(submission: Submission): SubmissionDTO { | export function mapToSubmissionDTO(submission: Submission): SubmissionDTO { | ||||||
|     return { |     return { | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ import { AnswerDTO, AnswerId } from '@dwengo-1/common/interfaces/answer'; | ||||||
| import { mapToAssignment } from '../interfaces/assignment.js'; | import { mapToAssignment } from '../interfaces/assignment.js'; | ||||||
| import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; | import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; | ||||||
| import { fetchStudent } from './students.js'; | import { fetchStudent } from './students.js'; | ||||||
| import { NotFoundException } from '../exceptions/not-found-exception'; | import { NotFoundException } from '../exceptions/not-found-exception.js'; | ||||||
| import { FALLBACK_VERSION_NUM } from '../config.js'; | import { FALLBACK_VERSION_NUM } from '../config.js'; | ||||||
| 
 | 
 | ||||||
| export async function getQuestionsAboutLearningObjectInAssignment( | export async function getQuestionsAboutLearningObjectInAssignment( | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ | ||||||
|     "oauth2DevicePollingInterval": 5, |     "oauth2DevicePollingInterval": 5, | ||||||
|     "enabled": true, |     "enabled": true, | ||||||
|     "sslRequired": "external", |     "sslRequired": "external", | ||||||
|     "registrationAllowed": false, |     "registrationAllowed": true, | ||||||
|     "registrationEmailAsUsername": false, |     "registrationEmailAsUsername": false, | ||||||
|     "rememberMe": false, |     "rememberMe": false, | ||||||
|     "verifyEmail": false, |     "verifyEmail": false, | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ | ||||||
|     "oauth2DevicePollingInterval": 5, |     "oauth2DevicePollingInterval": 5, | ||||||
|     "enabled": true, |     "enabled": true, | ||||||
|     "sslRequired": "external", |     "sslRequired": "external", | ||||||
|     "registrationAllowed": false, |     "registrationAllowed": true, | ||||||
|     "registrationEmailAsUsername": false, |     "registrationEmailAsUsername": false, | ||||||
|     "rememberMe": false, |     "rememberMe": false, | ||||||
|     "verifyEmail": false, |     "verifyEmail": false, | ||||||
|  |  | ||||||
|  | @ -52,11 +52,18 @@ npm run test:unit | ||||||
| ### Run End-to-End Tests with [Playwright](https://playwright.dev) | ### Run End-to-End Tests with [Playwright](https://playwright.dev) | ||||||
| 
 | 
 | ||||||
| ```sh | ```sh | ||||||
|  | cd frontend | ||||||
|  | 
 | ||||||
| # Install browsers for the first run | # Install browsers for the first run | ||||||
| npx playwright install | npx playwright install | ||||||
|  | # On Ubuntu, you can also use | ||||||
|  | npx playwright install --with-deps | ||||||
|  | # to additionally install the dependencies. | ||||||
| 
 | 
 | ||||||
| # When testing on CI, must build the project first | # When testing on CI, must build the project first | ||||||
|  | cd .. | ||||||
| npm run build | npm run build | ||||||
|  | cd frontend | ||||||
| 
 | 
 | ||||||
| # Runs the end-to-end tests | # Runs the end-to-end tests | ||||||
| npm run test:e2e | npm run test:e2e | ||||||
|  |  | ||||||
							
								
								
									
										82
									
								
								frontend/e2e/basic-homepage.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								frontend/e2e/basic-homepage.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | ||||||
|  | import { test, expect } from "@playwright/test"; | ||||||
|  | 
 | ||||||
|  | test("User can pick their language", async ({ page }) => { | ||||||
|  |     await page.goto("/"); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("button", { name: "translate" })).toBeVisible(); | ||||||
|  |     await page.getByRole("button", { name: "translate" }).click(); | ||||||
|  |     await page.getByText("Nederlands").click(); | ||||||
|  |     await expect(page.locator("h1")).toContainText("Onze sterke punten"); | ||||||
|  |     await expect(page.getByRole("heading", { name: "Innovatief" })).toBeVisible(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("button", { name: "vertalen" })).toBeVisible(); | ||||||
|  |     await page.getByRole("button", { name: "vertalen" }).click(); | ||||||
|  |     await page.getByText("English").click(); | ||||||
|  |     await expect(page.locator("h1")).toContainText("Our strengths"); | ||||||
|  |     await expect(page.getByRole("heading", { name: "Innovative" })).toBeVisible(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test("Teacher can sign in", async ({ page }) => { | ||||||
|  |     await page.goto("/"); | ||||||
|  |     await expect(page.getByRole("link", { name: "log in" })).toBeVisible(); | ||||||
|  |     await page.getByRole("link", { name: "log in" }).click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("button", { name: "teacher" })).toBeVisible(); | ||||||
|  |     await page.getByRole("button", { name: "teacher" }).click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByText("teacher")).toBeVisible(); | ||||||
|  |     await expect(page.getByRole("button", { name: "Sign In" })).toBeVisible(); | ||||||
|  | 
 | ||||||
|  |     await expect(page).toHaveURL(/\/realms\/teacher\//); | ||||||
|  | 
 | ||||||
|  |     await page.getByRole("textbox", { name: "Username or email" }).fill("testleerkracht1"); | ||||||
|  |     await page.getByRole("textbox", { name: "Password" }).fill("password"); | ||||||
|  |     await page.getByRole("button", { name: "Sign In" }).click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("link", { name: "Dwengo logo teacher" })).toBeVisible(); | ||||||
|  |     await expect(page.getByRole("button").nth(1)).toBeVisible(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test("Student can sign in", async ({ page }) => { | ||||||
|  |     await page.goto("/"); | ||||||
|  |     await expect(page.getByRole("link", { name: "log in" })).toBeVisible(); | ||||||
|  |     await page.getByRole("link", { name: "log in" }).click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("button", { name: "student" })).toBeVisible(); | ||||||
|  |     await page.getByRole("button", { name: "student" }).click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page).toHaveURL(/\/realms\/student\//); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByText("student")).toBeVisible(); | ||||||
|  |     await expect(page.getByRole("button", { name: "Sign In" })).toBeVisible(); | ||||||
|  | 
 | ||||||
|  |     await page.getByRole("textbox", { name: "Username or email" }).fill("testleerling1"); | ||||||
|  |     await page.getByRole("textbox", { name: "Password" }).fill("password"); | ||||||
|  |     await page.getByRole("button", { name: "Sign In" }).click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("link", { name: "Dwengo logo student" })).toBeVisible(); | ||||||
|  |     await expect(page.getByRole("button").nth(1)).toBeVisible(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test("Cannot sign in with invalid credentials", async ({ page }) => { | ||||||
|  |     await page.goto("/"); | ||||||
|  |     await page.getByRole("link", { name: "log in" }).click(); | ||||||
|  |     await page.getByRole("button", { name: "teacher" }).click(); | ||||||
|  |     await page.getByRole("textbox", { name: "Username or email" }).fill("doesnotexist"); | ||||||
|  |     await page.getByRole("textbox", { name: "Password" }).fill("wrong"); | ||||||
|  |     await page.getByRole("button", { name: "Sign In" }).click(); | ||||||
|  |     await expect(page.getByText("Invalid username or password.")).toBeVisible(); | ||||||
|  | 
 | ||||||
|  |     await page.goto("/"); | ||||||
|  |     await page.getByRole("link", { name: "log in" }).click(); | ||||||
|  |     await page.getByRole("button", { name: "student" }).click(); | ||||||
|  |     await page.getByRole("textbox", { name: "Username or email" }).fill("doesnotexist"); | ||||||
|  |     await page.getByRole("textbox", { name: "Password" }).fill("wrong"); | ||||||
|  |     await page.getByRole("button", { name: "Sign In" }).click(); | ||||||
|  |     await expect(page.getByText("Invalid username or password.")).toBeVisible(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test("Cannot skip login", async ({ page }) => { | ||||||
|  |     await page.goto("/user"); | ||||||
|  |     await expect(page.getByRole("link", { name: "log in" })).toBeVisible(); | ||||||
|  | }); | ||||||
							
								
								
									
										12
									
								
								frontend/e2e/basic-learning.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/e2e/basic-learning.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | import { test, expect } from "./fixtures.js"; | ||||||
|  | 
 | ||||||
|  | test("Users can filter", async ({ page }) => { | ||||||
|  |     await page.goto("/user"); | ||||||
|  | 
 | ||||||
|  |     await page.getByRole("combobox").filter({ hasText: "Select a themeAll" }).locator("i").click(); | ||||||
|  |     await page.getByText("Nature and climate").click(); | ||||||
|  |     await page.getByRole("combobox").filter({ hasText: "Select ageAll agesSelect age" }).locator("i").click(); | ||||||
|  |     await page.getByText("and older").click(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByRole("link", { name: "AI and Climate Students in" })).toBeVisible(); | ||||||
|  | }); | ||||||
							
								
								
									
										5
									
								
								frontend/e2e/basic-learning.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend/e2e/basic-learning.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | import { test, expect } from "./fixtures.js"; | ||||||
|  | 
 | ||||||
|  | test("myTest", async ({ page }) => { | ||||||
|  |     await expect(page).toHaveURL("/"); | ||||||
|  | }); | ||||||
							
								
								
									
										116
									
								
								frontend/e2e/fixtures.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								frontend/e2e/fixtures.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | /* eslint-disable no-await-in-loop */ | ||||||
|  | // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|  | import { test as baseTest, expect } from "@playwright/test"; | ||||||
|  | import type { Browser } from "playwright-core"; | ||||||
|  | import fs from "fs"; | ||||||
|  | import path from "path"; | ||||||
|  | 
 | ||||||
|  | /* Based on https://playwright.dev/docs/auth#moderate-one-account-per-parallel-worker */ | ||||||
|  | 
 | ||||||
|  | export * from "@playwright/test"; | ||||||
|  | export const ROOT_URL = "http://localhost:5173"; | ||||||
|  | 
 | ||||||
|  | interface Account { | ||||||
|  |     username: string; | ||||||
|  |     password: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Acquire an account by logging in or creating a new one. | ||||||
|  |  * @param id | ||||||
|  |  * @param browser | ||||||
|  |  */ | ||||||
|  | async function acquireAccount(id: number, browser: Browser): Promise<Account> { | ||||||
|  |     const account = { | ||||||
|  |         username: `worker${id}`, | ||||||
|  |         password: "password", | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const page = await browser.newPage(); | ||||||
|  |     await page.goto(ROOT_URL); | ||||||
|  | 
 | ||||||
|  |     await page.getByRole("link", { name: "log in" }).click(); | ||||||
|  |     await page.getByRole("button", { name: "student" }).click(); | ||||||
|  | 
 | ||||||
|  |     await page.getByRole("textbox", { name: "Username" }).fill(account.username); | ||||||
|  |     await page.getByRole("textbox", { name: "Password", exact: true }).fill(account.password); | ||||||
|  |     await page.getByRole("button", { name: "Sign In" }).click(); | ||||||
|  | 
 | ||||||
|  |     let failed = await page.getByText("Invalid username or password.").isVisible(); | ||||||
|  | 
 | ||||||
|  |     if (failed) { | ||||||
|  |         await page.getByRole("link", { name: "Register" }).click(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const MAX_RETRIES = 5; | ||||||
|  |     let retries = 0; | ||||||
|  |     while (failed && retries < MAX_RETRIES) { | ||||||
|  |         // Retry with a different username, based on Unix timestamp.
 | ||||||
|  |         account.username = `worker${id}-${Date.now()}`; | ||||||
|  | 
 | ||||||
|  |         await page.getByRole("textbox", { name: "Username" }).fill(account.username); | ||||||
|  |         await page.getByRole("textbox", { name: "Password", exact: true }).fill(account.password); | ||||||
|  |         await page.getByRole("textbox", { name: "Confirm password" }).fill(account.password); | ||||||
|  |         await page.getByRole("textbox", { name: "Email" }).fill(`${account.username}@dwengo.org`); | ||||||
|  |         await page.getByRole("textbox", { name: "First name" }).fill("Worker"); | ||||||
|  |         await page.getByRole("textbox", { name: "Last name" }).fill(id.toString()); | ||||||
|  |         await page.getByRole("button", { name: "Register" }).click(); | ||||||
|  | 
 | ||||||
|  |         await page.waitForURL(/localhost/); | ||||||
|  | 
 | ||||||
|  |         failed = await page.getByText("Username already exists.").isVisible(); | ||||||
|  |         retries += failed ? 1 : 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     await page.waitForURL(/localhost/); | ||||||
|  |     await page.close(); | ||||||
|  | 
 | ||||||
|  |     return account; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const test = baseTest.extend<object, { workerStorageState: string }>({ | ||||||
|  |     // Use the same storage state for all tests in this worker.
 | ||||||
|  |     storageState: async ({ workerStorageState }, use) => use(workerStorageState), | ||||||
|  | 
 | ||||||
|  |     // Authenticate once per worker with a worker-scoped fixture.
 | ||||||
|  |     workerStorageState: [ | ||||||
|  |         async ({ browser }, use): Promise<void> => { | ||||||
|  |             // Use parallelIndex as a unique identifier for each worker.
 | ||||||
|  |             const id = test.info().parallelIndex; | ||||||
|  |             const fileName = path.resolve(test.info().project.outputDir, `.auth/${id}.json`); | ||||||
|  | 
 | ||||||
|  |             if (fs.existsSync(fileName)) { | ||||||
|  |                 // Reuse existing authentication state if any.
 | ||||||
|  |                 await use(fileName); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Important: make sure we authenticate in a clean environment by unsetting storage state.
 | ||||||
|  |             const page = await browser.newPage({ storageState: undefined }); | ||||||
|  | 
 | ||||||
|  |             // Acquire a unique account by creating a new one.
 | ||||||
|  |             const account = await acquireAccount(id, browser); | ||||||
|  | 
 | ||||||
|  |             // Perform authentication steps. Replace these actions with your own.
 | ||||||
|  |             await page.goto(ROOT_URL); | ||||||
|  |             await page.getByRole("link", { name: "log in" }).click(); | ||||||
|  |             await page.getByRole("button", { name: "student" }).click(); | ||||||
|  |             await page.getByRole("textbox", { name: "Username or email" }).fill(account.username); | ||||||
|  |             await page.getByRole("textbox", { name: "Password" }).fill(account.password); | ||||||
|  |             await page.getByRole("button", { name: "Sign In" }).click(); | ||||||
|  |             // Wait until the page receives the cookies.
 | ||||||
|  |             //
 | ||||||
|  |             // Sometimes login flow sets cookies in the process of several redirects.
 | ||||||
|  |             // Wait for the final URL to ensure that the cookies are actually set.
 | ||||||
|  |             await page.waitForLoadState("domcontentloaded"); | ||||||
|  |             // Alternatively, you can wait until the page reaches a state where all cookies are set.
 | ||||||
|  | 
 | ||||||
|  |             // End of authentication steps.
 | ||||||
|  | 
 | ||||||
|  |             await page.context().storageState({ path: fileName }); | ||||||
|  |             await page.close(); | ||||||
|  |             await use(fileName); | ||||||
|  |         }, | ||||||
|  |         { scope: "worker" }, | ||||||
|  |     ], | ||||||
|  | }); | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| import { test, expect } from "@playwright/test"; |  | ||||||
| 
 |  | ||||||
| // See here how to get started:
 |  | ||||||
| // https://playwright.dev/docs/intro
 |  | ||||||
| test("visits the app root url", async ({ page }) => { |  | ||||||
|     await page.goto("/"); |  | ||||||
|     await expect(page.locator("h1")).toHaveText("You did it!"); |  | ||||||
| }); |  | ||||||
|  | @ -24,10 +24,11 @@ | ||||||
|         "vue": "^3.5.13", |         "vue": "^3.5.13", | ||||||
|         "vue-i18n": "^11.1.2", |         "vue-i18n": "^11.1.2", | ||||||
|         "vue-router": "^4.5.0", |         "vue-router": "^4.5.0", | ||||||
|         "vuetify": "^3.7.12" |         "vuetify": "^3.7.12", | ||||||
|  |         "wait-on": "^8.0.3" | ||||||
|     }, |     }, | ||||||
|     "devDependencies": { |     "devDependencies": { | ||||||
|         "@playwright/test": "^1.50.1", |         "@playwright/test": "1.50.1", | ||||||
|         "@tsconfig/node22": "^22.0.0", |         "@tsconfig/node22": "^22.0.0", | ||||||
|         "@types/jsdom": "^21.1.7", |         "@types/jsdom": "^21.1.7", | ||||||
|         "@types/node": "^22.13.4", |         "@types/node": "^22.13.4", | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ export default defineConfig({ | ||||||
|     /* Fail the build on CI if you accidentally left test.only in the source code. */ |     /* Fail the build on CI if you accidentally left test.only in the source code. */ | ||||||
|     forbidOnly: Boolean(process.env.CI), |     forbidOnly: Boolean(process.env.CI), | ||||||
|     /* Retry on CI only */ |     /* Retry on CI only */ | ||||||
|     retries: process.env.CI ? 2 : 0, |     retries: process.env.CI ? 2 : 1, | ||||||
|     /* Opt out of parallel tests on CI. */ |     /* Opt out of parallel tests on CI. */ | ||||||
|     workers: process.env.CI ? 1 : undefined, |     workers: process.env.CI ? 1 : undefined, | ||||||
|     /* Reporter to use. See https://playwright.dev/docs/test-reporters */ |     /* Reporter to use. See https://playwright.dev/docs/test-reporters */ | ||||||
|  | @ -65,18 +65,18 @@ export default defineConfig({ | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         /* Test against mobile viewports. */ |         /* Test against mobile viewports. */ | ||||||
|         // {
 |         { | ||||||
|         //   Name: 'Mobile Chrome',
 |             name: "Mobile Chrome", | ||||||
|         //   Use: {
 |             use: { | ||||||
|         //     ...devices['Pixel 5'],
 |                 ...devices["Pixel 5"], | ||||||
|         //   },
 |             }, | ||||||
|         // },
 |         }, | ||||||
|         // {
 |         { | ||||||
|         //   Name: 'Mobile Safari',
 |             name: "Mobile Safari", | ||||||
|         //   Use: {
 |             use: { | ||||||
|         //     ...devices['iPhone 12'],
 |                 ...devices["iPhone 12"], | ||||||
|         //   },
 |             }, | ||||||
|         // },
 |         }, | ||||||
| 
 | 
 | ||||||
|         /* Test against branded browsers. */ |         /* Test against branded browsers. */ | ||||||
|         // {
 |         // {
 | ||||||
|  | @ -97,14 +97,28 @@ export default defineConfig({ | ||||||
|     // OutputDir: 'test-results/',
 |     // OutputDir: 'test-results/',
 | ||||||
| 
 | 
 | ||||||
|     /* Run your local dev server before starting the tests */ |     /* Run your local dev server before starting the tests */ | ||||||
|     webServer: { |     webServer: [ | ||||||
|         /** |         { | ||||||
|          * Use the dev server by default for faster feedback loop. |  | ||||||
|          * Use the preview server on CI for more realistic testing. |  | ||||||
|          * Playwright will re-use the local server if there is already a dev-server running. |  | ||||||
|          */ |  | ||||||
|             command: process.env.CI ? "npm run preview" : "npm run dev", |             command: process.env.CI ? "npm run preview" : "npm run dev", | ||||||
|             port: process.env.CI ? 4173 : 5173, |             port: process.env.CI ? 4173 : 5173, | ||||||
|             reuseExistingServer: !process.env.CI, |             reuseExistingServer: !process.env.CI, | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             command: "cd ../ && docker compose up", | ||||||
|  |             reuseExistingServer: false, | ||||||
|  |             gracefulShutdown: { | ||||||
|  |                 signal: "SIGTERM", | ||||||
|  |                 timeout: 5000, | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             command: process.env.CI ? "cd ../ && npm run dev -w backend" : "cd ../ && npm run start -w backend", | ||||||
|  |             port: 3000, | ||||||
|  |             reuseExistingServer: !process.env.CI, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             command: "wait-on http://localhost:7080", | ||||||
|  |             timeout: 120000, | ||||||
|  |         }, | ||||||
|  |     ], | ||||||
| }); | }); | ||||||
|  |  | ||||||
							
								
								
									
										3302
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										3302
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Reference in a new issue