Skip to content

Commit

Permalink
created import page and spec and use page returns
Browse files Browse the repository at this point in the history
  • Loading branch information
jaggarnaut committed Nov 14, 2024
1 parent 41b9bd2 commit 1eb944c
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 31 deletions.
51 changes: 33 additions & 18 deletions docs/struktur.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# Struktur der Playwright-Tests

In diesem Dokument wird beschrieben, wie wir unsere Playwright-Tests strukturieren.

## Zielsetzung

Das Ziel bei unseren Tests ist es, die Benutzerinteraktionen so abstrakt wie möglich zu beschreiben.
Das heißt zum Beispiel, dass wir nicht in jedem Test erneut beschreiben wollen, wie man eine ComboBox bedient.
Stattdessen soll der Test lediglich fordern, dass aus einer ComboBox ein Eintrag ausgewählt wird.
Expand All @@ -19,31 +17,48 @@ Tests werden parallel ausgeführt, wenn sie in verschiedenen Dateien stehen.
Es ist daher ratsam, Testdateien klein zu halten und entsprechend sinnvoll zu schneiden.

## Technische Umsetzung

### Verzeichnisstruktur

`/base`: Helper für Tests

`/elements`: wiederkehrende Seitenelemente (TODO: über Umbenennung in "components" nachdenken)

`/pages`: Seitenobjekte

`/tests`: Die eigentlichen Tests

### Verzeichnis `base`
#### Verzeichnis `base`: Helper für Tests
Im `base`-Verzeichnis befinden sich Helper, die von den Tests aufgerufen werden aber nicht direkt zu den Tests gehören.
Darunter fällt zum Beispiel die Erzeugung von Testdaten direkt über API.

### Verzeichnis `elements`
In Elements befinden sich semantische Wrapper um Locators, die wiederkehrende Elemente auf den Seiten testen.
#### Verzeichnis `elements`: wiederkehrende Seitenelemente (TODO: über Umbenennung in "components" nachdenken)
In `elements` befinden sich semantische Wrapper um Locators, die wiederkehrende Elemente auf den Seiten testen.
Beispielsweise legen wir hier eine Klasse für "Comboboxen" ab, die die nötigen Schritte zur Auswahl von Elementen kapselt.

### Verzeichnis `pages`
#### Verzeichnis `pages`: Seitenobjekte
Im Verzeichnis `pages` liegen Seitenrepräsentationen.
Eine Seite hat dabei high-level-Funktionen, zur Navigation und zum Aufruf von Seitenfunktionalitäten.

### Tags
#### Verzeichnis `/tests`: Die eigentlichen Tests

### Tags
Wir verwenden Tags, um diejenigen Tests auszuwählen, die ausgeführt werden sollen.
Zur Zeit unterscheiden wir nach Ausführungslänge (@long, @short).
Das muss aber nicht die einzige Unterscheidungskategorie bleiben.
Das muss aber nicht die einzige Unterscheidungskategorie bleiben.

### Return Pages statt Import
Für die Verwendung von mehreren Pages in den einzelnen Tests müssen sie nicht in jedem Test importiert und einzeln navigiert werden. Stattdessen ist es sinnvoll, verlinkte Pages in dedizierten Funktionen der einzelnen Pages zu returnen und die Funktionen im Test aufzurufen. Das spart Code und Wartungsaufwand.

#### Beispiel
```
// tests/Import.spec.ts
import { LandingPage } from "../pages/LandingView.page";
const loginPage = await landingPage.goToLogin();
const startPage = await loginPage.login(ADMIN, PW);
const menuPage = await startPage.goToAdministration();
const importPage = await menuPage.goToBenutzerImport();
// pages/LandingView.page.ts
public async goToLogin(): Promise<LoginPage> {
await this.button_Anmelden.click();
return new LoginPage(this.page);
}
// pages/StartView.page.ts
public async goToAdministration(): Promise<MenuPage> {
await this.card_item_schulportal_administration.click();
return new MenuPage(this.page);
}
```
2 changes: 1 addition & 1 deletion pages/LandingView.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class LandingPage {
this.button_Anmelden = page.getByTestId("login-button");
}

public async login(): Promise<LoginPage> {
public async goToLogin(): Promise<LoginPage> {
await this.button_Anmelden.click();
return new LoginPage(this.page);
}
Expand Down
4 changes: 2 additions & 2 deletions pages/LoginView.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export class LoginPage {
}

async login(
username = process.env.USER,
password = process.env.PW,
username: string = process.env.USER as string,
password: string = process.env.PW as string,
): Promise<StartPage> {
await expect(this.text_h1).toBeVisible();
await this.input_username.click();
Expand Down
8 changes: 8 additions & 0 deletions pages/MenuBar.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RolleCreationViewPage } from "./admin/RolleCreationView.page";
import {RolleManagementViewPage} from "./admin/RolleManagementView.page";
import { SchuleManagementViewPage } from "./admin/SchuleManagementView.page";
import { SchuleCreationViewPage } from "./admin/SchuleCreationView.page";
import { PersonImportViewPage } from "./admin/PersonImportView.page";

export class MenuPage {
readonly page: Page;
Expand All @@ -21,6 +22,7 @@ export class MenuPage {
readonly menueItem_AlleSchulenAnzeigen: Locator;
readonly menueItem_SchuleAnlegen: Locator;
readonly label_Schultraegerverwaltung: Locator;
readonly menuItemBenutzerImportieren: Locator;

constructor(page) {
this.page = page;
Expand All @@ -37,6 +39,7 @@ export class MenuPage {
this.menueItem_BenutzerAnlegen = page.getByTestId(
"person-creation-menu-item",
);
this.menuItemBenutzerImportieren = page.getByTestId("person-import-menu-item");
this.label_Klassenverwaltung = page.locator(
'[data-testid="klasse-management-title"] .v-list-item-title',
);
Expand Down Expand Up @@ -86,4 +89,9 @@ export class MenuPage {
await this.menueItem_SchuleAnlegen.click();
return new SchuleCreationViewPage(this.page);
}

public async goToBenutzerImport(): Promise<PersonImportViewPage> {
await this.menuItemBenutzerImportieren.click();
return new PersonImportViewPage(this.page);
}
}
2 changes: 1 addition & 1 deletion pages/StartView.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class StartPage {
this.card_item_schulportal_administration = page.locator('[data-testid^="service-provider-card"]', { hasText: "Schulportal-Administration" });
}

public async administration(): Promise<MenuPage> {
public async goToAdministration(): Promise<MenuPage> {
await this.card_item_schulportal_administration.click();
return new MenuPage(this.page);
}
Expand Down
14 changes: 14 additions & 0 deletions pages/admin/PersonImportView.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { type Locator, Page } from '@playwright/test';

export class PersonImportViewPage {
readonly page: Page;
readonly body: Locator;
readonly headlineBenutzerImport: Locator;

constructor(page) {
// Benutzerimport
this.page = page;
this.body = page.locator('body');
this.headlineBenutzerImport = page.getByTestId('layout-card-headline');
}
}
45 changes: 45 additions & 0 deletions tests/Import.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { test, expect } from "@playwright/test";
import { LandingPage } from "../pages/LandingView.page";
import { HeaderPage } from "../pages/Header.page";
import { LONG } from "../base/tags";

const PW = process.env.PW;
const ADMIN = process.env.USER;
const FRONTEND_URL = process.env.FRONTEND_URL || "";

test.describe(`Testfälle für den Benutzerimport": Umgebung: ${process.env.UMGEBUNG}: URL: ${process.env.FRONTEND_URL}:`, () => {
test.beforeEach(async ({ page }) => {
await test.step(`Login and navigate to Benutzerimport`, async () => {
const landingPage = new LandingPage(page);

await page.goto(FRONTEND_URL);
const loginPage = await landingPage.goToLogin();
const startPage = await loginPage.login(ADMIN, PW);
const menuPage = await startPage.goToAdministration();
const importPage = await menuPage.goToBenutzerImport();

await expect(importPage.headlineBenutzerImport).toBeVisible();
});
});

test.afterEach(async ({ page }) => {
await test.step(`Testdaten löschen via API`, async () => {
// delete test data
});

await test.step(`Abmelden`, async () => {
const header = new HeaderPage(page);
await header.logout();
});
});

test("Als Landesadmin eine CSV-Datei mit Benutzerdaten hochladen und importieren", {tag: [LONG]}, async ({ page }) => {
const schulname = "";
const rollenname = "";
const csvFile = "";

await test.step(``, async () => {
// upload CSV file
});
});
});
10 changes: 5 additions & 5 deletions tests/Rolle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test.beforeEach(async ({ page }) => {
startseite = await test.step(`Login`, async () => {
const startPage = await FromAnywhere(page)
.start()
.then((landing) => landing.login())
.then((landing) => landing.goToLogin())
.then((login) => login.login());
loggedIn = true;
return startPage;
Expand Down Expand Up @@ -67,7 +67,7 @@ test.describe(`Testfälle für die Administration von Rollen: Umgebung: ${proces
const rolleCreationView =
await test.step(`Dialog Rolle anlegen öffnen`, async () => {
const rolleCreationView = await startseite
.administration()
.goToAdministration()
.then((menu) => menu.rolleAnlegen());
await expect(rolleCreationView.text_h2_RolleAnlegen).toHaveText(
"Neue Rolle hinzufügen",
Expand Down Expand Up @@ -124,7 +124,7 @@ test.describe(`Testfälle für die Administration von Rollen: Umgebung: ${proces

test("Ergebnisliste Rollen auf Vollständigkeit prüfen als Landesadmin", {tag: [LONG, SHORT, STAGE]}, async () => {
await test.step(`Rollenverwaltung öffnen und alle Elemente in der Ergebnisliste auf Existenz prüfen`, async () => {
const menu: MenuPage = await startseite.administration();
const menu: MenuPage = await startseite.goToAdministration();
const rolleManagement = await menu.alleRollenAnzeigen();
await expect(
rolleManagement.text_h1_Administrationsbereich,
Expand Down Expand Up @@ -155,7 +155,7 @@ test.describe(`Testfälle für die Administration von Rollen: Umgebung: ${proces
const rolleCreationView =
await test.step(`Dialog Rolle anlegen öffnen`, async () => {
return await startseite
.administration()
.goToAdministration()
.then((menu) => menu.rolleAnlegen());
});

Expand Down Expand Up @@ -243,7 +243,7 @@ test.describe("Testet die Anlage einer neuen Rolle", () => {
const rolleCreationView: RolleCreationViewPage =
await test.step("Rolle anlegen aufrufen", async () => {
return await startseite
.administration()
.goToAdministration()
.then((menu) => menu.rolleAnlegen());
});

Expand Down
8 changes: 4 additions & 4 deletions tests/Schule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ test.describe(`Testfälle für die Administration von Schulen: Umgebung: ${proce

const { menue, schuleCreationView }: { menue: MenuPage; schuleCreationView: SchuleCreationViewPage } =
await test.step(`Dialog Schule anlegen öffnen`, async (): Promise<{ menue: MenuPage; schuleCreationView: SchuleCreationViewPage }> => {
const menue: MenuPage = await startseite.administration();
const menue: MenuPage = await startseite.goToAdministration();
const schuleCreationView: SchuleCreationViewPage = await menue.schuleAnlegen();
await menue.menueItem_SchuleAnlegen.click();
await expect(schuleCreationView.text_h2_SchuleAnlegen).toHaveText("Neue Schule hinzufügen");
Expand Down Expand Up @@ -127,7 +127,7 @@ test.describe(`Testfälle für die Administration von Schulen: Umgebung: ${proce
const startseite = new StartPage(page);

await test.step(`Schulverwaltung öffnen und Alle Elemente in der Ergebnisliste auf Existenz prüfen`, async () => {
const menue: MenuPage = await startseite.administration();
const menue: MenuPage = await startseite.goToAdministration();
const schuleManagementView: SchuleManagementViewPage = await menue.alleSchulenAnzeigen();
await expect(schuleManagementView.text_h1_Administrationsbereich).toBeVisible();
await expect(schuleManagementView.text_h2_Schulverwaltung).toBeVisible();
Expand Down Expand Up @@ -155,15 +155,15 @@ test.describe(`Testfälle für die Administration von Schulen: Umgebung: ${proce
await addSystemrechtToRolle(page, userInfo.rolleId, 'SCHULEN_VERWALTEN');

await header.logout();
const login = await landing.login();
const login = await landing.goToLogin();
const startseite = await login.login(userInfo.username, userInfo.password);
userInfo.password = await login.UpdatePW();
await expect(startseite.text_h2_Ueberschrift).toBeVisible();
return startseite
});

const schuleCreationView = await test.step(`Dialog Schule anlegen öffnen als Schuladmin`, async () => {
const menue: MenuPage = await startseite.administration();
const menue: MenuPage = await startseite.goToAdministration();
const schuleCreationView: SchuleCreationViewPage = await menue.schuleAnlegen();
return schuleCreationView;
});
Expand Down

0 comments on commit 1eb944c

Please sign in to comment.