From 152f5fa1b20b9d6a3c4a48cdd4272d06fc58bf6a Mon Sep 17 00:00:00 2001 From: alexeh Date: Thu, 10 Oct 2024 10:48:35 +0200 Subject: [PATCH] WIP e2e test env --- api/package.json | 5 +- api/src/modules/config/app-config.module.ts | 2 + api/src/modules/config/path-resolver.ts | 7 +- client/.env.development | 3 +- client/.env.test | 2 +- e2e/package.json | 4 +- e2e/playwright.config.ts | 83 +++++++++++---------- e2e/tests/auth/auth.spec.ts | 76 +++++++++++++------ pnpm-lock.yaml | 21 +----- shared/lib/e2e-test-manager.ts | 38 +++++----- 10 files changed, 129 insertions(+), 112 deletions(-) diff --git a/api/package.json b/api/package.json index 1e5e39c8..1bb9b79c 100644 --- a/api/package.json +++ b/api/package.json @@ -26,8 +26,10 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.2", "@ts-rest/nest": "^3.51.0", + "@types/multer": "^1.4.12", "bcrypt": "catalog:", "class-transformer": "catalog:", + "dotenv": "16.4.5", "lodash": "^4.17.21", "nodemailer": "^6.9.15", "passport": "^0.7.0", @@ -38,8 +40,7 @@ "rxjs": "^7.8.1", "typeorm": "catalog:", "xlsx": "^0.18.5", - "zod": "catalog:", - "@types/multer": "^1.4.12" + "zod": "catalog:" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/api/src/modules/config/app-config.module.ts b/api/src/modules/config/app-config.module.ts index de6c3806..0fdec226 100644 --- a/api/src/modules/config/app-config.module.ts +++ b/api/src/modules/config/app-config.module.ts @@ -5,6 +5,8 @@ import { DatabaseModule } from '@api/modules/config/database/database.module'; import { resolveConfigPath } from '@api/modules/config/path-resolver'; import { JwtConfigHandler } from '@api/modules/config/auth-config.handler'; +const DEFAULT_RELATIVE_PATH = '../../../../../../'; + @Global() @Module({ imports: [ diff --git a/api/src/modules/config/path-resolver.ts b/api/src/modules/config/path-resolver.ts index 01909aad..1e225098 100644 --- a/api/src/modules/config/path-resolver.ts +++ b/api/src/modules/config/path-resolver.ts @@ -6,12 +6,9 @@ const TEST_RELATIVE_PATH = '../../../../'; const DEFAULT_RELATIVE_PATH = '../../../../../../'; /** - * @description: Resolve the path of the config file depending on the environment + * @description: Resolve the path of the dotenv config file relative to shared folder */ export function resolveConfigPath(relativePath: string): string { - const rootDir = - process.env.NODE_ENV === 'test' - ? TEST_RELATIVE_PATH - : DEFAULT_RELATIVE_PATH; + const rootDir = DEFAULT_RELATIVE_PATH; return join(__dirname, rootDir, relativePath); } diff --git a/client/.env.development b/client/.env.development index 5acc95e2..9c6a2e7e 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1,3 +1,4 @@ NEXTAUTH_URL=http://localhost:$PORT NEXTAUTH_SECRET=WAzjpS46vFxp17TsRDU3FXo+TF0vrfy6uhCXwGMBUE8= -NEXT_PUBLIC_API_URL=https://dev.blue-carbon-cost-tool.dev-vizzuality.com/api \ No newline at end of file +#NEXT_PUBLIC_API_URL=https://dev.blue-carbon-cost-tool.dev-vizzuality.com/api +NEXT_PUBLIC_API_URL=http://localhost:4000 \ No newline at end of file diff --git a/client/.env.test b/client/.env.test index 5acc95e2..d99a62f9 100644 --- a/client/.env.test +++ b/client/.env.test @@ -1,3 +1,3 @@ NEXTAUTH_URL=http://localhost:$PORT NEXTAUTH_SECRET=WAzjpS46vFxp17TsRDU3FXo+TF0vrfy6uhCXwGMBUE8= -NEXT_PUBLIC_API_URL=https://dev.blue-carbon-cost-tool.dev-vizzuality.com/api \ No newline at end of file +NEXT_PUBLIC_API_URL=http://localhost:4000 \ No newline at end of file diff --git a/e2e/package.json b/e2e/package.json index d99c4107..7006a817 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -15,6 +15,8 @@ "pretest": "tsc && tsc-alias", "test": "playwright test -c ./dist/e2e", "test:ui": "pnpm pretest && playwright test --ui -c ./dist/e2e", - "codegen": "pnpm --filter api start:dev & pnpm --filter client dev & playwright codegen localhost:3000" + "codegen": "pnpm --filter api start:dev & pnpm --filter client dev & playwright codegen localhost:3000", + "codegen:prod": "pnpm --filter api run build & NODE_ENV=test pnpm --filter api run start:prod & NODE_ENV=test pnpm --filter client run build & NODE_ENV=test pnpm --filter client run start & playwright codegen localhost:3000", + "test2": "playwright test" } } diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 984e86b4..67a6abc9 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -1,48 +1,51 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices } from "@playwright/test"; -const API_URL = 'http://localhost:4000'; -const APP_URL = 'http://localhost:3000'; +// +const API_URL = "http://localhost:4000"; +const APP_URL = "http://localhost:3000"; /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - /* Run your local dev server before starting the tests */ - webServer: [ - { - command: 'pnpm --filter api run build && NODE_ENV=test pnpm --filter api run start:prod', - url: API_URL, - reuseExistingServer: !process.env.CI, - }, - { - command: 'NODE_ENV=test pnpm --filter client run build && NODE_ENV=test pnpm --filter client run start', - url: APP_URL, - reuseExistingServer: !process.env.CI, - }, - ], - testDir: './tests', - /* Run tests in files in parallel */ - fullyParallel: false, - workers: 1, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: process.env.CI ? 'list' : 'html', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: APP_URL, - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + /* Run your local dev server before starting the tests */ + webServer: [ + { + command: + "pnpm --filter api run build && NODE_ENV=test pnpm --filter api run start:prod", + url: API_URL, + reuseExistingServer: !process.env.CI, }, - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - ], + { + command: + "NODE_ENV=test pnpm --filter client run build && NODE_ENV=test pnpm --filter client run start", + url: APP_URL, + reuseExistingServer: !process.env.CI, + }, + ], + testDir: "./tests", + /* Run tests in files in parallel */ + fullyParallel: false, + workers: 1, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: process.env.CI ? "list" : "html", + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: APP_URL, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], }); diff --git a/e2e/tests/auth/auth.spec.ts b/e2e/tests/auth/auth.spec.ts index 1702007e..11f6375d 100644 --- a/e2e/tests/auth/auth.spec.ts +++ b/e2e/tests/auth/auth.spec.ts @@ -1,6 +1,6 @@ -import { test, expect, Page } from '@playwright/test'; -import { E2eTestManager } from '@shared/lib/e2e-test-manager'; -import { User } from '@shared/entities/users/user.entity'; +import { test, expect, Page } from "@playwright/test"; +import { E2eTestManager } from "@shared/lib/e2e-test-manager"; +import { User } from "@shared/entities/users/user.entity"; let testManager: E2eTestManager; let page: Page; @@ -15,48 +15,76 @@ test.beforeEach(async () => { }); test.afterEach(async () => { - await testManager.clearDatabase(); + //await testManager.clearDatabase(); }); test.afterAll(async () => { + await testManager.logout(); await testManager.close(); }); -// test('an user signs up successfully', async ({ page }) => { -// const user: Pick = { -// email: 'johndoe@test.com', -// password: 'password', +// test("an user signs up successfully", async ({ page }) => { +// const user: Pick = { +// email: "johndoe@test.com", +// password: "password", // }; // -// await page.goto('/auth/signup'); +// await page.goto("/auth/signup"); // -// await page.getByLabel('Email').fill(user.email); +// await page.getByLabel("Email").fill(user.email); // await page.locator('input[type="password"]').fill(user.password); -// await page.getByRole('checkbox').check(); +// await page.getByRole("checkbox").check(); // -// await page.getByRole('button', { name: /sign up/i }).click(); +// await page.getByRole("button", { name: /sign up/i }).click(); // -// await page.waitForURL('/auth/signin'); +// await page.waitForURL("/auth/signin"); // -// await page.getByLabel('Email').fill(user.email); +// await page.getByLabel("Email").fill(user.email); // await page.locator('input[type="password"]').fill(user.password); // -// await page.getByRole('button', { name: /log in/i }).click(); +// await page.getByRole("button", { name: /log in/i }).click(); // -// await page.waitForURL('/profile'); +// await page.waitForURL("/profile"); // await expect(await page.locator('input[type="email"]')).toHaveValue( -// user.email +// user.email, // ); // }); -test('an user signs in successfully', async ({ page }) => { - const user: Pick = { - email: 'jhondoe@test.com', - password: '12345678', - partnerName: 'partner-test' +test("an user signs in successfully", async ({ page }) => { + const user: Pick = { + email: "jhondoe@test.com", + password: "12345678", + partnerName: "admin", }; await testManager.mocks().createUser(user); - await testManager.login(user as User) - await expect(page.getByText(`Email: ${user.email}`)).toBeVisible(); + await page.goto("/auth/signin"); + await page.getByLabel("Email").fill(user.email); + await page.locator('input[type="password"]').fill(user.password); + await page.getByRole("button", { name: /log in/i }).click(); +}); + +test("test", async ({ page }) => { + await page.goto("http://localhost:3000/"); + await page.getByRole("link", { name: "Sign in" }).click(); + await page.getByPlaceholder("Enter your email").click(); + await page.getByPlaceholder("Enter your email").fill("jhondoe@test.com"); + await page.getByPlaceholder("*******").click(); + await page.getByPlaceholder("*******").fill("12345678"); + await page.getByRole("button", { name: "Log in" }).click(); }); +// test("test", async ({ page }) => { +// const user: Pick = { +// email: "jhondoe@test.com", +// password: "12345678", +// partnerName: "admin", +// }; +// await page.goto("http://localhost:3000/"); +// await page.getByRole("link", { name: "Sign in" }).click(); +// await page.getByPlaceholder("Enter your email").click(); +// await page.getByPlaceholder("Enter your email").fill(user.email); +// await page.getByPlaceholder("*******").click(); +// await page.getByPlaceholder("*******").fill(user.password); +// await page.getByRole("button", { name: "Log in" }).click(); +// await expect(page.getByText(`Email: ${user.email}`)).toBeVisible(); +// }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eda800f9..181c1003 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,27 +9,9 @@ catalogs: '@types/node': specifier: 20.14.2 version: 20.14.2 - bcrypt: - specifier: 5.1.1 - version: 5.1.1 - class-transformer: - specifier: 0.5.1 - version: 0.5.1 - pg: - specifier: 8.12.0 - version: 8.12.0 - reflect-metadata: - specifier: ^0.2.0 - version: 0.2.2 - typeorm: - specifier: 0.3.20 - version: 0.3.20 typescript: specifier: 5.4.5 version: 5.4.5 - zod: - specifier: 3.23.8 - version: 3.23.8 importers: @@ -128,6 +110,9 @@ importers: class-transformer: specifier: 'catalog:' version: 0.5.1 + dotenv: + specifier: 16.4.5 + version: 16.4.5 lodash: specifier: ^4.17.21 version: 4.17.21 diff --git a/shared/lib/e2e-test-manager.ts b/shared/lib/e2e-test-manager.ts index 0ebc3b83..1876112e 100644 --- a/shared/lib/e2e-test-manager.ts +++ b/shared/lib/e2e-test-manager.ts @@ -1,19 +1,17 @@ -import { DataSource } from 'typeorm'; +import { DataSource } from "typeorm"; import { User } from "@shared/entities/users/user.entity"; -import { - createUser, -} from '@shared/lib/entity-mocks'; -import { clearTestDataFromDatabase } from '@shared/lib/db-helpers'; -import { DB_ENTITIES } from '@shared/lib/db-entities'; -import { sign } from 'jsonwebtoken'; +import { createUser } from "@shared/lib/entity-mocks"; +import { clearTestDataFromDatabase } from "@shared/lib/db-helpers"; +import { DB_ENTITIES } from "@shared/lib/db-entities"; +import { sign } from "jsonwebtoken"; const AppDataSource = new DataSource({ - type: 'postgres', - host: 'localhost', + type: "postgres", + host: "localhost", port: 5432, - username: 'blue-carbon-cost', - password: 'blue-carbon-cost', - database: 'blc', + username: "blue-carbon-cost", + password: "blue-carbon-cost", + database: "blc", entities: DB_ENTITIES, }); @@ -59,7 +57,7 @@ export class E2eTestManager { } getPage() { - if (!this.page) throw new Error('Playwright Page is not initialized'); + if (!this.page) throw new Error("Playwright Page is not initialized"); return this.page; } @@ -67,21 +65,21 @@ export class E2eTestManager { if (!user) { user = await this.mocks().createUser(); } - await this.page.goto('/auth/signin'); - await this.page.getByLabel('Email').fill(user.email); + await this.page.goto("/auth/signin"); + await this.page.getByLabel("Email").fill(user.email); await this.page.locator('input[type="password"]').fill(user.password); - await this.page.getByRole('button', { name: /log in/i }).click(); - await this.page.waitForURL('/profile'); + await this.page.getByRole("button", { name: /log in/i }).click(); + await this.page.waitForURL("/profile"); return user; } async logout() { - await this.page.goto('/auth/api/signout'); - await this.page.getByRole('button', { name: 'Sign out' }).click(); + await this.page.goto("/auth/api/signout"); + await this.page.getByRole("button", { name: "Sign out" }).click(); } async generateToken(user: User) { // the secret must match the provided for the api when built for e2e tests - return sign({ id: user.id }, 'mysupersecretfortests'); + return sign({ id: user.id }, "mysupersecretfortests"); } }