diff --git a/.env b/.env index e3001b2..07aa051 100644 --- a/.env +++ b/.env @@ -1 +1,9 @@ -BASE_URL=http://localhost:3000 \ No newline at end of file +BASE_URL=http://localhost:3000 +CONFAC_APP_PATH= ../confac +#TESTCONTAINERS_RYUK_DISABLED=true + +MONGO_HOST=localhost +MONGO_PORT=27017 +MONGO_USERNAME=admin +MONGO_PASSWORD=pwd +MONGO_DB=confac \ No newline at end of file diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 54249b6..120e315 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -23,17 +23,6 @@ jobs: with: node-version: lts/* - - name: Start MongoDB - run: | - docker volume create mongodata - docker run -d \ - -p 27017:27017 \ - -e MONGO_INITDB_ROOT_USERNAME=admin \ - -e MONGO_INITDB_ROOT_PASSWORD=pwd \ - -v mongodata:/data/db \ - --name confac-mongo \ - mongo:3.6.3 - - name: Install confac dependencies run: | cd confac/backend @@ -46,21 +35,6 @@ jobs: cd confac/backend cp .env.sample .env - - name: Start confac backend and frontend - run: | - cd confac/backend - nohup npm start > backend.log 2>&1 & - cd ../frontend - nohup npm start > frontend.log 2>&1 & - shell: bash - - - name: inject dummy data - run: | - cd confac/backend - node ./public/faker/index.js - shell: bash - continue-on-error: true - - name: Install test dependencies run: npm ci @@ -77,26 +51,3 @@ jobs: path: playwright-report/ retention-days: 30 - - - name: Upload backend log - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v4 - with: - name: backend-log - path: confac/backend/backend.log - retention-days: 30 - - - name: Upload frontend log - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v4 - with: - name: frontend-log - path: confac/frontend/frontend.log - retention-days: 30 - - - name: Cleanup containers - if: always() - run: | - docker stop confac-mongo || true - docker rm confac-mongo || true - docker volume rm mongodata || true diff --git a/package-lock.json b/package-lock.json index 983f399..889119c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "dotenv": "^17.2.3" + "dotenv": "^17.2.3", + "redis": "^5.9.0" }, "devDependencies": { "@playwright/test": "^1.56.1", @@ -206,6 +207,67 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@redis/bloom": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0.tgz", + "integrity": "sha512-W9D8yfKTWl4tP8lkC3MRYkMz4OfbuzE/W8iObe0jFgoRmgMfkBV+Vj38gvIqZPImtY0WB34YZkX3amYuQebvRQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@redis/client": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0.tgz", + "integrity": "sha512-EI0Ti5pojD2p7TmcS7RRa+AJVahdQvP/urpcSbK/K9Rlk6+dwMJTQ354pCNGCwfke8x4yKr5+iH85wcERSkwLQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0.tgz", + "integrity": "sha512-Bm2jjLYaXdUWPb9RaEywxnjmzw7dWKDZI4MS79mTWPV16R982jVWBj6lY2ZGelJbwxHtEVg4/FSVgYDkuO/MxA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@redis/search": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0.tgz", + "integrity": "sha512-jdk2csmJ29DlpvCIb2ySjix2co14/0iwIT3C0I+7ZaToXgPbgBMB+zfEilSuncI2F9JcVxHki0YtLA0xX3VdpA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@redis/time-series": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0.tgz", + "integrity": "sha512-W6ILxcyOqhnI7ELKjJXOktIg3w4+aBHugDbVpgVLPZ+YDjObis1M0v7ZzwlpXhlpwsfePfipeSK+KWNuymk52w==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, "node_modules/@types/docker-modem": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", @@ -745,6 +807,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1576,6 +1647,22 @@ "node": ">=10" } }, + "node_modules/redis": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.9.0.tgz", + "integrity": "sha512-E8dQVLSyH6UE/C9darFuwq4usOPrqfZ1864kI4RFbr5Oj9ioB9qPF0oJMwX7s8mf6sPYrz84x/Dx1PGF3/0EaQ==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.9.0", + "@redis/client": "5.9.0", + "@redis/json": "5.9.0", + "@redis/search": "5.9.0", + "@redis/time-series": "5.9.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", diff --git a/package.json b/package.json index 8983f45..73d26c1 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "testcontainers": "^11.7.2" }, "dependencies": { - "dotenv": "^17.2.3" + "dotenv": "^17.2.3", + "redis": "^5.9.0" } } diff --git a/playwright.config.ts b/playwright.config.ts index 44b006b..0bffec7 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -30,7 +30,7 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: "on-first-retry", - + storageState:'playwright/.auth/user.json', screenshot: "only-on-failure" }, diff --git a/src/ccomponents/CreateComponent.ts b/src/components/CreateComponent.ts similarity index 100% rename from src/ccomponents/CreateComponent.ts rename to src/components/CreateComponent.ts diff --git a/src/enum/klantTypes.ts b/src/enum/klantTypes.ts new file mode 100644 index 0000000..82cbd4d --- /dev/null +++ b/src/enum/klantTypes.ts @@ -0,0 +1,5 @@ +export enum klantTypes { + Onderaannemer = 'Onderaannemer', + Klant = 'Klant', + Eindklant = 'Eindklant', +} \ No newline at end of file diff --git a/src/pages/Klanten/CreateKlantPage.ts b/src/pages/Klanten/CreateKlantPage.ts new file mode 100644 index 0000000..5b62030 --- /dev/null +++ b/src/pages/Klanten/CreateKlantPage.ts @@ -0,0 +1,188 @@ +import { Locator, Page } from "playwright"; +import { BasePage } from "../BasePage"; +import { CreateComponent } from "../../components/CreateComponent"; +import { klantTypes } from "../../enum/klantTypes"; + +export class CreateKlantPage extends BasePage { + createComponent: CreateComponent; + + btwNummerInput: Locator; + klantVerderAanvullenButton: Locator; + + raamcontractDropdown: Locator; + klantInput: Locator; + btwInput: Locator; + typeDropdown: Locator; + typeDropdownOptions: Locator; + straatEnNummerInput: Locator; + PostcodeInput: Locator; + stadInput: Locator; + landDropdown: Locator; + landDropdownOptions: Locator; + contactGegevensInput: Locator; + contactEmailInput: Locator; + telefoonNrInput: Locator; + taalDropdown: Locator; + bewarenButton: Locator; + + constructor(page: Page, url: string = "/clients/create") { + super(page, url); + + this.createComponent = new CreateComponent(page); + this.btwNummerInput = page.getByTestId("btw"); + this.klantVerderAanvullenButton = page.getByTestId("btw-continue"); + + this.raamcontractDropdown = page + .locator("div") + .filter({ hasText: /^Type\(s\)$/ }) + .nth(2); + this.klantInput = page.getByTestId("name"); + this.btwInput = page.getByTestId("btw"); + this.typeDropdown = page + .locator( + ".react-select__value-container.react-select__value-container--is-multi > .react-select__input-container" + ) + .first(); + this.typeDropdownOptions = page.locator("#react-select-4-listbox"); + this.straatEnNummerInput = page.getByTestId("address"); + this.PostcodeInput = page.getByTestId("postalCode"); + this.stadInput = page.getByTestId("city"); + this.landDropdown = page + .locator( + ".col-lg-3 > .form-group > .css-b62m3t-container > .react-select__control > .react-select__value-container > .react-select__input-container" + ) + .first(); + this.landDropdownOptions = page.locator("#react-select-5-listbox"); + + this.contactGegevensInput = page.getByTestId("contact"); + this.contactEmailInput = page.getByTestId("contactEmail"); + this.telefoonNrInput = page + .locator("div") + .filter({ hasText: "Telefoon nr" }) + .nth(4); + this.taalDropdown = page.locator( + ".col-lg-3 > .form-group > .react-select-base > .react-select__control > .react-select__value-container > .react-select__input-container" + ); + this.bewarenButton = page.getByRole("button", { name: "Bewaren" }); + } + + /** + * enter the given btw number (in the initial create page) + * @param btwNummer string to repsrent the btw nummer + */ + async btwNummerInvullen(btwNummer: string) { + await this.btwNummerInput.fill(btwNummer); + } + /** + * Click the verder aanvullen button + */ + async ClickKlantVerderAanvullen() { + await this.klantVerderAanvullenButton.click(); + } + + /** + * enter the given klant name + * @param klantNaam name to enter + */ + async klantNaamInvullen(klantNaam: string) { + await this.klantInput.fill(klantNaam); + } + + /** + * enter the btw number (on the full creation page) + * @param btw string to represent the btw number + * @returns + */ + async btwInvullen(btw: string) { + await this.btwInput.fill(btw); + + return this; + } + + /** + * select the given type from the type dropdown + * opens the dropdown and selects from the given options + * @param type see @KlantTypes enum + */ + async typeDropdownSelecteren(type: klantTypes) { + await this.typeDropdown.click(); + await this.typeDropdownOptions + .locator(".react-select__option", { hasText: type }) + .click(); + } + + /** + * enter the straat en nummer input + * @param straatEnNummer string to enter + */ + async straatEnNummerInvullen(straatEnNummer: string) { + await this.straatEnNummerInput.fill(straatEnNummer); + } + + /** + * enter the postcode + * @param postcode string to enter + */ + async postcodeInvullen(postcode: string) { + await this.PostcodeInput.fill(postcode); + } + + /** + * enter the stad + * @param stad string to enter + */ + async stadInvullen(stad: string) { + await this.stadInput.fill(stad); + } + + /** + * select the given land from the land dropdown + * @param land land to select + */ + async landDropdownSelecteren(land: string) { + await this.landDropdown.click(); + await this.landDropdownOptions + .locator(".react-select__option", { hasText: land }) + .click(); + } + + /** + * contact gegevens invullen + * @param contactGegevens string to enter + */ + async contactGegevensInvullen(contactGegevens: string) { + await this.contactGegevensInput.fill(contactGegevens); + } + + /** + * contact email invullen + * @param contactEmail string to enter + */ + async contactEmailInvullen(contactEmail: string) { + await this.contactEmailInput.fill(contactEmail); + } + + /** + * telefoon nr invullen + * @param telefoonNr string to enter + */ + async telefoonNrInvullen(telefoonNr: string) { + await this.telefoonNrInput.fill(telefoonNr); + } + + /** + * select the given taal from the taal dropdown + * @param taal string to select + */ + async taalDropdownSelecteren(taal: string) { + await this.taalDropdown.click(); + await this.page.getByText(taal).click(); + } + + /** + * click the bewaren button. Navigates back to klanten page + */ + async clickBewaren() { + await this.bewarenButton.click(); + } +} diff --git a/src/pages/Klanten/KlantenPage.ts b/src/pages/Klanten/KlantenPage.ts new file mode 100644 index 0000000..9c8d0bd --- /dev/null +++ b/src/pages/Klanten/KlantenPage.ts @@ -0,0 +1,89 @@ +import { Locator, Page } from "playwright"; +import { BasePage } from "../BasePage"; +import { CreateComponent } from "../../components/CreateComponent"; + +export class KlantenPage extends BasePage { + createComponent: CreateComponent; + + nieuweKlantButton: Locator; + zoekenInput: Locator; + filterdropdown: Locator; + toonInactieveToggle: Locator; + deleteButton: Locator; + editButton: Locator; + + constructor(page: Page, url: string = "/clients") { + super(page, url); + + this.createComponent = new CreateComponent(page); + this.nieuweKlantButton = page.getByTestId("add"); + this.zoekenInput = page.getByRole("textbox", { name: "Zoeken" }); + this.filterdropdown = page.getByTestId(""); + this.toonInactieveToggle = page + .locator("div") + .filter({ hasText: "Toon inactieve" }) + .nth(5); + this.deleteButton = page.getByRole("button", { name: "" }); + this.editButton = page.getByRole("button", { name: "" }); + } + + /** + * Click the new klant button + */ + async ClickOnNieuweKlant() { + await this.nieuweKlantButton.click(); + } + + /** + * Enter the given klant name in the search input + * @param klantNaam name to be searched + */ + async search(klantNaam: string) { + await this.zoekenInput.fill(klantNaam); + } + + /** + * check if a klant exists in the klanten list + * @param klantNaam name to validate + * @returns @boolean true for present, false for not present + */ + async klantExists(klantNaam: string): Promise { + const klantCell = this.page.getByRole("cell", { name: klantNaam }); + return (await klantCell.count()) > 0; + } + + /** + * delete the klant at the given row or the given row locator + * @param row row number or row locator to delete + */ + async deleteKlant(row: number | Locator = 0) { + if (typeof row === "number") { + await this.deleteButton.nth(row).click(); + return; + } else { + await row.getByRole("button", { name: "" }).click(); + } + } + + /** + * edit the klant at the given row or the given row locator + * @param row row number or row locator to edit + */ + async editKlant(row: number | Locator = 0) { + if (typeof row === "number") { + await this.editButton.nth(row).click(); + return; + } else { + await row.getByRole("button", { name: "" }).click(); + } + } + + /** + * get klant row by name + * @param klantNaam name of the klant to find + * @returns Locator for the klant row + */ + async getKlantRowbyname(klantNaam: string): Promise { + return this.page.getByRole("row", { name: klantNaam }); + } +} \ No newline at end of file diff --git a/src/pages/projecten/ConsultantsPage.ts b/src/pages/projecten/ConsultantsPage.ts index 38127d4..c24f382 100644 --- a/src/pages/projecten/ConsultantsPage.ts +++ b/src/pages/projecten/ConsultantsPage.ts @@ -1,6 +1,6 @@ import { Locator, Page } from "playwright"; import { BasePage } from "../BasePage"; -import { CreateComponent } from "../../ccomponents/CreateComponent"; +import { CreateComponent } from "../../components/CreateComponent"; export class ConsultantsPage extends BasePage { createComponent: CreateComponent; @@ -12,7 +12,7 @@ export class ConsultantsPage extends BasePage { deleteButton: Locator; constructor(page: Page, url: string = "/consultants") { - super(page, "/consultants"); + super(page, url); this.createComponent = new CreateComponent(page); diff --git a/src/pages/projecten/CreateConsultantsPage.ts b/src/pages/projecten/CreateConsultantsPage.ts index bbc65c5..fdc4d62 100644 --- a/src/pages/projecten/CreateConsultantsPage.ts +++ b/src/pages/projecten/CreateConsultantsPage.ts @@ -1,6 +1,6 @@ import { Locator, Page } from "playwright"; import { BasePage } from "../BasePage"; -import { CreateComponent } from "../../ccomponents/CreateComponent"; +import { CreateComponent } from "../../components/CreateComponent"; export class CreateConsultantsPage extends BasePage { createComponent: CreateComponent; @@ -15,7 +15,7 @@ export class CreateConsultantsPage extends BasePage { readonly saveButton: Locator; constructor(page: Page, url: string = "/consultants") { - super(page, "/consultants"); + super(page, url); this.createComponent = new CreateComponent(page); diff --git a/src/utils/helpers/AlphaNumericHelper.ts b/src/utils/helpers/AlphaNumericHelper.ts new file mode 100644 index 0000000..8f433ed --- /dev/null +++ b/src/utils/helpers/AlphaNumericHelper.ts @@ -0,0 +1,95 @@ +export class AlphaNumericHelper { + /** + * Get random alphabetic string of given length + * @param length Length of string, defaults to 4 + * @returns Random alphabetic string + */ + static randomAlpha(length = 4): string { + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + let result = ""; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; + } + + /** + * Get random numeric string of given length + * @param length Length of string, defaults to 4 + * @returns Random numeric string + */ + static randomNumeric(length = 4): string { + const digits = "0123456789"; + let result = ""; + for (let i = 0; i < length; i++) { + result += digits.charAt(Math.floor(Math.random() * digits.length)); + } + return result; + } + + /** + * Get random alphanumeric string of given length + * @param length Length of string, defaults to 4 + * @returns Random alphanumeric string + */ + static randomAlphanumeric(length = 4): string { + const chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; + } + + /** + * Get random full name + * @returns Random first and last name + */ + static randomName(): string { + const firstNames = ["Lucas", "Emma", "Louis", "Lotte", "Noah"]; + const lastNames = ["Peeters", "Janssens", "Maes", "Wouters", "Claes"]; + const first = firstNames[Math.floor(Math.random() * firstNames.length)]; + const last = lastNames[Math.floor(Math.random() * lastNames.length)]; + return `${first} ${last}`; + } + + /** + * Get random street address + * @returns Random street with number + */ + static randomStraat(): string { + const streets = [ + "Rue de la Loi", + "Kerkstraat", + "Langestraat", + "Koninginnelaan", + "Vrijheidslaan", + ]; + const street = streets[Math.floor(Math.random() * streets.length)]; + const number = Math.floor(Math.random() * 200) + 1; + return `${street} ${number}`; + } + + /** + * Get random phone number + * @returns Random phone number string + */ + static randomPhoneNumber(): string { + let number = "04"; + for (let i = 0; i < 8; i++) { + number += Math.floor(Math.random() * 10).toString(); + } + return number; + } + + /** + * Get random BTW number + * @returns Random 10-digit numeric string + */ + static randomBtw(): string { + const digits = this.randomNumeric(9); + const suffix = "B01"; + return `BE${digits}${suffix}`; + } +} diff --git a/test-setup/setup.ts b/test-setup/setup.ts new file mode 100644 index 0000000..ba36903 --- /dev/null +++ b/test-setup/setup.ts @@ -0,0 +1,85 @@ +import { GenericContainer, StartedTestContainer, Wait } from "testcontainers"; +import { ChildProcess, spawn } from "child_process"; +import path from "path"; + +let mongoContainer: StartedTestContainer; +let backendProcess: ChildProcess; +let frontendProcess: ChildProcess; +let seedProcess: ChildProcess; + +const getAppPath = () => { + if (process.env.CONFAC_APP_PATH) { + return process.env.CONFAC_APP_PATH; + } + return path.resolve(__dirname, "../../../confac"); +}; + +export async function setupTestEnvironment() { + const appPath = getAppPath(); + console.log(`Using confac app path: ${appPath}`); + mongoContainer = await new GenericContainer("mongo:latest") + .withExposedPorts(27017) + .withEnvironment({ + MONGO_INITDB_DATABASE: "confac", + MONGO_INITDB_ROOT_USERNAME: process.env.MONGO_USERNAME?.toString() || "", + MONGO_INITDB_ROOT_PASSWORD: process.env.MONGO_PASSWORD?.toString() || "", + }) + .withStartupTimeout(120000) + .withWaitStrategy(Wait.forLogMessage("Waiting for connections")) + /*.withLogConsumer(stream => { + stream.on("data", (line: Buffer) => console.log(`MongoDB: ${line.toString().trim()}`)); + })*/ + .start(); + console.log( + `MongoDB started at mongodb://${mongoContainer.getHost()}:${mongoContainer.getMappedPort( + 27017 + )}` + ); + + const mongoPort = mongoContainer.getMappedPort(27017); + const mongoUrl = `mongodb://${mongoContainer.getHost()}:${mongoPort}/confac`; + + console.log("Starting Backend process"); + + backendProcess = spawn("npm", ["start"], { + cwd: path.join(appPath, "backend"), + stdio: ["inherit", "pipe", "pipe"], + shell: true, + env: { + ...process.env, + MONGODB_URI: mongoUrl, + MONGO_PORT: mongoPort.toString(), + }, + }); + console.log("Backend process started"); + console.log("Starting seed process"); + + seedProcess = spawn("cd backend/public && node ./faker/index.j", { + shell: true, + stdio: "inherit", + env: process.env, + }); + console.log("Seed process finished"); + + console.log("Frontend process started"); + + frontendProcess = spawn("npm", ["start"], { + cwd: path.join(appPath, "frontend"), + //stdio: ['inherit', 'pipe', 'pipe'] + shell: true, + }); + console.log("Frontend process started"); + await new Promise((resolve) => setTimeout(resolve, 10000)); +} + +export async function teardownTestEnvironment() { + if (mongoContainer) { + await mongoContainer.stop(); + } + if (backendProcess) { + backendProcess.kill(); + } + if (frontendProcess) { + frontendProcess.kill(); + } +} diff --git a/tests/auth/auth.setup.ts b/tests/auth/auth.setup.ts index 5b59b77..6215145 100644 --- a/tests/auth/auth.setup.ts +++ b/tests/auth/auth.setup.ts @@ -1,20 +1,37 @@ -import { test as setup, expect } from '@playwright/test'; -import path from 'path'; -import { LoginPage } from '../../src/pages/LoginPage'; +import test, { test as setup, expect, request } from "@playwright/test"; +import path from "path"; -const authFile = path.join(__dirname, '../../playwright/.auth/user.json'); +import { + setupTestEnvironment, + teardownTestEnvironment, +} from "../../test-setup/setup"; +import { LoginPage } from "../../src/pages/LoginPage"; + +const authFile = path.join(__dirname, "../../playwright/.auth/user.json"); let login: LoginPage; +test.beforeAll(async () => { + await setupTestEnvironment(); +}); + +test.afterAll(async () => { + await teardownTestEnvironment(); +}); + +setup("authenticate", async ({ page }) => { + const api = await request.newContext({ storageState: authFile }); + const response = await api.get(process.env.BASE_URL + "/api/config"); + + if (response.status() === 200) { + return + } + login = new LoginPage(page); -setup('authenticate', async ({ page }) => { - login = new LoginPage(page); - - await login.goto(); - await login.enterName('e2e-test-user'); - await login.submitForm(); + await login.goto(); + await login.enterName("e2e-test-user"); + await login.submitForm(); - await expect(page).toHaveTitle("Maandelijkse facturatie - confac") - - await page.context().storageState({ path: authFile }); + await expect(page).toHaveTitle("Maandelijkse facturatie - confac"); -}) \ No newline at end of file + await page.context().storageState({ path: authFile }); +}); diff --git a/tests/example.spec.ts b/tests/example.spec.ts deleted file mode 100644 index b9bd751..0000000 --- a/tests/example.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { test, expect } from "@playwright/test"; -import { LoginPage } from "../src/pages/LoginPage"; - -let loginPage: LoginPage; - -test.beforeEach(async ({ page }) => { - loginPage = new LoginPage(page); -}); - -test("hello world", async ({ page }) => { - console.log("hello world"); - expect(true).toBeTruthy(); - - await loginPage.goto(); -}); diff --git a/tests/klanten/klanten.spec.ts b/tests/klanten/klanten.spec.ts new file mode 100644 index 0000000..bf0c32e --- /dev/null +++ b/tests/klanten/klanten.spec.ts @@ -0,0 +1,92 @@ +import { test, expect } from "@playwright/test"; +import { + setupTestEnvironment, + teardownTestEnvironment, +} from "../../test-setup/setup"; + +import { KlantenPage } from "../../src/pages/Klanten/KlantenPage"; +import { CreateKlantPage } from "../../src/pages/Klanten/CreateKlantPage"; +import { klantTypes } from "../../src/enum/klantTypes"; +import { AlphaNumericHelper } from "../../src/utils/helpers/AlphaNumericHelper" + + + +let klantenPage: KlantenPage; +let createKlantPage: CreateKlantPage; + +test.beforeAll(async () => { + await setupTestEnvironment(); +}); + +test.afterAll(async () => { + await teardownTestEnvironment(); +}); + +test.beforeEach(async ({ page }) => { + klantenPage = new KlantenPage(page); + createKlantPage = new CreateKlantPage(page); +}); + +test("klant toevoegen", async ({ page }) => { + await klantenPage.goto(); + + await klantenPage.ClickOnNieuweKlant(); + + await createKlantPage.btwNummerInvullen(AlphaNumericHelper.randomBtw()); + await createKlantPage.ClickKlantVerderAanvullen(); + await createKlantPage.klantNaamInvullen("test" + AlphaNumericHelper.randomName()); + //await createKlantPage.btwInvullen("btw nummer"); + await createKlantPage.typeDropdownSelecteren(klantTypes.Eindklant); + await createKlantPage.straatEnNummerInvullen(AlphaNumericHelper.randomStraat() + AlphaNumericHelper.randomNumeric(1)); + await createKlantPage.postcodeInvullen(AlphaNumericHelper.randomNumeric(4)); + await createKlantPage.stadInvullen("stad"); + await createKlantPage.landDropdownSelecteren("België"); + + const [response] = await Promise.all([ + page.waitForResponse( + (res) => + res.url().includes("api/clients") && res.request().method() === "POST" + ), + createKlantPage.clickBewaren(), + ]); + expect(response.status()).toBe(200); +}); + +test("klant zoeken", async ({ page }) => { + let klantNaam = "Wyman LLC"; + await klantenPage.goto(); + + await klantenPage.search(klantNaam); + + expect(await klantenPage.klantExists(klantNaam)).toBeTruthy(); +}); + +test("klant verwijderen", async ({ page }) => { + let klantNaam = "Wyman LLC"; + await klantenPage.goto(); + + await klantenPage.getKlantRowbyname(klantNaam).then(async (row) => { + await klantenPage.deleteKlant(row); + }); + + expect(await klantenPage.klantExists(klantNaam)).toBeFalsy(); +}); + +test("klant aanpassen", async ({ page }) => { + let klantNaam = "test"; + let updatedKlantNaam = "aangepaste klant naam"; + await klantenPage.goto(); + + await klantenPage.search(klantNaam) + await klantenPage.getKlantRowbyname(klantNaam).then(async (row) => { + await klantenPage.editKlant(row); + }); + + await createKlantPage.klantNaamInvullen(updatedKlantNaam); + + await createKlantPage.clickBewaren(); + + expect(await klantenPage.klantExists(updatedKlantNaam)).toBeTruthy(); +}); + + diff --git a/tests/projecten/consultanten/consultanten.spec.ts b/tests/projecten/consultanten/consultanten.spec.ts index 7c64204..08a4fc1 100644 --- a/tests/projecten/consultanten/consultanten.spec.ts +++ b/tests/projecten/consultanten/consultanten.spec.ts @@ -1,13 +1,24 @@ import { test, expect } from "@playwright/test"; import { ConsultantsPage } from "../../../src/pages/projecten/ConsultantsPage"; import { CreateConsultantsPage } from "../../../src/pages/projecten/CreateConsultantsPage"; +import { setupTestEnvironment, teardownTestEnvironment } from "../../../test-setup/setup"; let consultantsPage: ConsultantsPage; let createConsultantsPage: CreateConsultantsPage; +test.beforeAll(async () => { + await setupTestEnvironment(); +}); + +test.afterAll(async () => { + await teardownTestEnvironment(); +}); + test.beforeEach(async ({ page }) => { consultantsPage = new ConsultantsPage(page); - createConsultantsPage = new CreateConsultantsPage(page);}); + createConsultantsPage = new CreateConsultantsPage(page); + +}); test("consultant toevoegen", async ({ page }) => { await consultantsPage.goto(); @@ -28,8 +39,6 @@ test("consultant zoeken", async ({ page }) => { await consultantsPage.search(search); expect(await consultantsPage.consultantExists(search)).toBeTruthy; - - }); test("consultant verwijderen", async ({ page }) => { @@ -41,8 +50,8 @@ test("consultant verwijderen", async ({ page }) => { await consultantsPage.deleteConsultant(rowToDelete); expect(await consultantsPage.consultantExists(name)).toBeFalsy(); - }); + test("consultant aanpassen", async ({ page }) => { let rowToUpdate = 5; let newName = "Aangepaste voornaam"; @@ -55,6 +64,4 @@ test("consultant aanpassen", async ({ page }) => { await createConsultantsPage.setFirstName(newName); await createConsultantsPage.clickSave(); expect(await consultantsPage.consultantExists(newName)).toBeTruthy - -}); - +}); \ No newline at end of file