adds playwright tests

This commit is contained in:
Christian Beutel
2024-03-11 18:15:27 +01:00
parent 02c82d3e46
commit 2e3c3abbe4
17 changed files with 4689 additions and 4425 deletions

View File

@@ -0,0 +1,19 @@
import { test as setup } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('/login', { waitUntil: 'networkidle' });
await page.locator('input[name="username"]').fill('Test');
await page.locator('input[name="password"]').fill('password');
await page.getByRole('button', { name: 'Login' }).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.waitForURL('/');
// End of authentication steps.
await page.context().storageState({ path: authFile });
});

View File

@@ -0,0 +1,8 @@
import { expect, test } from '@playwright/test';
import { IndexPage } from '../../pages/index_page';
test('index page does not show error', async ({ page }) => {
const indexPage = new IndexPage(page);
await indexPage.goto()
await indexPage.hasNoError()
});

View File

@@ -0,0 +1,30 @@
import { test as base, expect } from '@playwright/test';
import { ListsPage } from '../../pages/lists_page';
const test = base.extend<{ listsPage: ListsPage }>({
listsPage: async ({ page }, use) => {
const listsPage = new ListsPage(page);
await listsPage.goto();
await listsPage.create();
await use(listsPage);
await listsPage.removeAll();
},
});
test('shows a list card', async ({ listsPage }) => {
const listItemCount = await listsPage.listItems.count();
await listsPage.create();
expect(listsPage.listItems).toHaveCount(listItemCount + 1);
expect(listsPage.listItemsImage).toBeVisible();
});
test('update a list card', async ({ listsPage }) => {
await listsPage.update();
expect(listsPage.listItems.first()).toContainText("Updated List");
expect(listsPage.listItems.first()).toContainText("New Description");
});
test('delete a list card', async ({ listsPage }) => {
await listsPage.delete();
expect(listsPage.listItems).toHaveCount(0);
});

View File

@@ -0,0 +1,11 @@
import { test, expect } from '@playwright/test';
test('logs the user out', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'avatar' }).click();
await page.locator(".menu .menu-item").filter({ hasText: "Logout" }).click();
const cookies = await page.context().cookies();
const pbAuthCookie = cookies.find(cookie => cookie.name === 'pb_auth');
expect(pbAuthCookie).toBeFalsy();
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

View File

@@ -0,0 +1,19 @@
import { expect, type Locator, type Page } from '@playwright/test';
export class IndexPage {
readonly page: Page;
readonly error: Locator;
constructor(page: Page) {
this.page = page;
this.error = page.getByText("Internal Error");
}
async goto() {
await this.page.goto('/');
}
async hasNoError() {
await expect(this.error).toHaveCount(0);
}
}

View File

@@ -0,0 +1,83 @@
import { type Locator, type Page } from '@playwright/test';
export class ListsPage {
readonly page: Page;
readonly createListButton: Locator;
readonly listItems: Locator;
readonly listItemsImage: Locator;
readonly listModal: Locator;
readonly listModalAvatar: Locator;
readonly listModalName: Locator;
readonly listModalDescription: Locator;
readonly listModalSaveButton: Locator;
readonly confirmModal: Locator;
readonly confirmModalConfirmButton: Locator;
constructor(page: Page) {
this.page = page;
this.createListButton = page.locator("#create-list-button");
this.listModal = page.locator("#list-modal");
this.listModalAvatar = this.listModal.locator('input[name="avatar"]')
this.listModalName = this.listModal.locator('input[name="name"]')
this.listModalDescription = this.listModal.locator('textarea[name="description"]')
this.listModalSaveButton = this.listModal.getByText('Save');
this.listItems = page.locator('.list-list-item');
this.listItemsImage = page.locator('.list-list-item img');
this.confirmModal = page.locator("#confirm-modal");
this.confirmModalConfirmButton = this.confirmModal.locator("button").filter({ hasText: "Delete" });
}
async goto() {
await this.page.goto('/lists', { waitUntil: 'networkidle' });
}
async create(name: string = "Test List") {
await this.createListButton.click();
await this.listModalName.fill(name);
await this.listModalAvatar.setInputFiles([
"./tests/playwright/fixtures/avatar.webp"
]);
await Promise.all([
this.page.waitForResponse(resp => resp.url().includes('/api/v1/list') && resp.status() === 200),
this.listModalSaveButton.click()
]);
}
async update(name: string = "Updated List", description = "New Description") {
await this.listItems.first().locator(".dropdown button").click();
await this.listItems.first().locator(".menu .menu-item").filter({ hasText: "Edit" }).click();
await this.listModalName.fill(name);
await this.listModalDescription.fill(description);
await Promise.all([
this.page.waitForResponse(resp => resp.url().includes('/api/v1/list') && resp.status() === 200),
this.listModalSaveButton.click()
]);
}
async delete() {
await this.listItems.first().locator(".dropdown button").click();
await this.listItems.first().locator(".menu .menu-item").filter({ hasText: "Delete" }).click();
await Promise.all([
this.page.waitForResponse(resp => resp.url().includes('/api/v1/list') && resp.status() === 200),
this.confirmModalConfirmButton.click()
]);
}
async removeAll() {
while ((await this.listItems.count()) > 0) {
await this.delete();
}
}
}