Improve Playwright/E2E testing setup (#5056)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
parent
01210ce88d
commit
3d9bbe1a62
|
@ -1,11 +1,11 @@
|
||||||
import { defineConfig, devices } from "@playwright/test";
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
const port = 30001;
|
const port = 30001;
|
||||||
const url = `http://localhost:${port}`;
|
export const url = `http://localhost:${port}`;
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
// Look for test files in the "tests" directory, relative to this configuration file.
|
// Look for test files in the "tests" directory, relative to this configuration file.
|
||||||
testDir: "../test/e2e",
|
testDir: "../test/e2e/specs",
|
||||||
outputDir: "../private/playwright-test-results",
|
outputDir: "../private/playwright-test-results",
|
||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
locale: "en-US",
|
locale: "en-US",
|
||||||
|
@ -40,9 +40,15 @@ export default defineConfig({
|
||||||
// Configure projects for major browsers.
|
// Configure projects for major browsers.
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: "chromium",
|
name: "run-once setup",
|
||||||
|
testMatch: /setup-process\.once\.js/,
|
||||||
use: { ...devices["Desktop Chrome"] },
|
use: { ...devices["Desktop Chrome"] },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "specs",
|
||||||
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
dependencies: [ "run-once setup" ],
|
||||||
|
},
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
name: "firefox",
|
name: "firefox",
|
||||||
|
|
|
@ -246,6 +246,36 @@ let needSetup = false;
|
||||||
log.debug("test", request.body);
|
log.debug("test", request.body);
|
||||||
response.send("OK");
|
response.send("OK");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
app.get("/_e2e/take-sqlite-snapshot", async (request, response) => {
|
||||||
|
await Database.close();
|
||||||
|
try {
|
||||||
|
fs.cpSync(Database.sqlitePath, `${Database.sqlitePath}.e2e-snapshot`);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error("Unable to copy SQLite DB.");
|
||||||
|
}
|
||||||
|
await Database.connect();
|
||||||
|
|
||||||
|
response.send("Snapshot taken.");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/_e2e/restore-sqlite-snapshot", async (request, response) => {
|
||||||
|
if (!fs.existsSync(`${Database.sqlitePath}.e2e-snapshot`)) {
|
||||||
|
throw new Error("Snapshot doesn't exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Database.close();
|
||||||
|
try {
|
||||||
|
fs.cpSync(`${Database.sqlitePath}.e2e-snapshot`, Database.sqlitePath);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error("Unable to copy snapshot file.");
|
||||||
|
}
|
||||||
|
await Database.connect();
|
||||||
|
|
||||||
|
response.send("Snapshot restored.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Robots.txt
|
// Robots.txt
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { test } from "@playwright/test";
|
|
||||||
import { login, screenshot } from "./util-test";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup
|
|
||||||
*/
|
|
||||||
|
|
||||||
test("setup sqlite", async ({ page }, testInfo) => {
|
|
||||||
await page.goto("./");
|
|
||||||
await page.getByText("SQLite").click();
|
|
||||||
await page.getByRole("button", { name: "Next" }).click();
|
|
||||||
await screenshot(testInfo, page);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("setup admin", async ({ page }, testInfo) => {
|
|
||||||
await page.goto("./");
|
|
||||||
await page.getByPlaceholder("Username").click();
|
|
||||||
await page.getByPlaceholder("Username").fill("admin");
|
|
||||||
await page.getByPlaceholder("Username").press("Tab");
|
|
||||||
await page.getByPlaceholder("Password", { exact: true }).fill("admin123");
|
|
||||||
await page.getByPlaceholder("Password", { exact: true }).press("Tab");
|
|
||||||
await page.getByPlaceholder("Repeat Password").fill("admin123");
|
|
||||||
await page.getByRole("button", { name: "Create" }).click();
|
|
||||||
await screenshot(testInfo, page);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All other tests should be run after setup
|
|
||||||
*/
|
|
||||||
|
|
||||||
test("login", async ({ page }, testInfo) => {
|
|
||||||
await page.goto("./dashboard");
|
|
||||||
await login(page);
|
|
||||||
await screenshot(testInfo, page);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("logout", async ({ page }, testInfo) => {
|
|
||||||
await page.goto("./dashboard");
|
|
||||||
await login(page);
|
|
||||||
await page.getByText("A", { exact: true }).click();
|
|
||||||
await page.getByRole("button", { name: "Log out" }).click();
|
|
||||||
await screenshot(testInfo, page);
|
|
||||||
});
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
import { login, restoreSqliteSnapshot, screenshot } from "../util-test";
|
||||||
|
|
||||||
|
test.describe("Example Spec", () => {
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await restoreSqliteSnapshot(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dashboard", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./dashboard");
|
||||||
|
await login(page);
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("change display timezone", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./settings/general");
|
||||||
|
await login(page);
|
||||||
|
await page.getByLabel("Display Timezone").selectOption("Pacific/Fiji");
|
||||||
|
await page.getByRole("button", { name: "Save" }).click();
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
|
||||||
|
await page.goto("./dashboard");
|
||||||
|
await page.goto("./settings/general");
|
||||||
|
await expect(page.getByLabel("Display Timezone")).toHaveValue("Pacific/Fiji");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("database is reset after previous test", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./settings/general");
|
||||||
|
await login(page);
|
||||||
|
|
||||||
|
const timezoneEl = page.getByLabel("Display Timezone");
|
||||||
|
await expect(timezoneEl).toBeVisible();
|
||||||
|
await expect(timezoneEl).toHaveValue("auto");
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { test } from "@playwright/test";
|
||||||
|
import { getSqliteDatabaseExists, login, screenshot, takeSqliteSnapshot } from "../util-test";
|
||||||
|
|
||||||
|
test.describe("Uptime Kuma Setup", () => {
|
||||||
|
|
||||||
|
test.skip(() => getSqliteDatabaseExists(), "Must only run once per session");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup
|
||||||
|
*/
|
||||||
|
|
||||||
|
test("setup sqlite", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./");
|
||||||
|
await page.getByText("SQLite").click();
|
||||||
|
await page.getByRole("button", { name: "Next" }).click();
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
await page.waitForURL("/setup"); // ensures the server is ready to continue to the next test
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("setup admin", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./");
|
||||||
|
await page.getByPlaceholder("Username").click();
|
||||||
|
await page.getByPlaceholder("Username").fill("admin");
|
||||||
|
await page.getByPlaceholder("Username").press("Tab");
|
||||||
|
await page.getByPlaceholder("Password", { exact: true }).fill("admin123");
|
||||||
|
await page.getByPlaceholder("Password", { exact: true }).press("Tab");
|
||||||
|
await page.getByPlaceholder("Repeat Password").fill("admin123");
|
||||||
|
await page.getByRole("button", { name: "Create" }).click();
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All other tests should be run after setup
|
||||||
|
*/
|
||||||
|
|
||||||
|
test("login", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./dashboard");
|
||||||
|
await login(page);
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("logout", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./dashboard");
|
||||||
|
await login(page);
|
||||||
|
await page.getByText("A", { exact: true }).click();
|
||||||
|
await page.getByRole("button", { name: "Log out" }).click();
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("take sqlite snapshot", async ({ page }, testInfo) => {
|
||||||
|
await takeSqliteSnapshot(page);
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -1,3 +1,9 @@
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const serverUrl = require("../../config/playwright.config.js").url;
|
||||||
|
|
||||||
|
const dbPath = "./../../data/playwright-test/kuma.db";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TestInfo} testInfo Test info
|
* @param {TestInfo} testInfo Test info
|
||||||
* @param {Page} page Page
|
* @param {Page} page Page
|
||||||
|
@ -25,3 +31,32 @@ export async function login(page) {
|
||||||
await page.getByRole("button", { name: "Log in" }).click();
|
await page.getByRole("button", { name: "Log in" }).click();
|
||||||
await page.isVisible("text=Add New Monitor");
|
await page.isVisible("text=Add New Monitor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the SQLite database has been created. This indicates setup has completed.
|
||||||
|
* @returns {boolean} True if exists
|
||||||
|
*/
|
||||||
|
export function getSqliteDatabaseExists() {
|
||||||
|
return fs.existsSync(path.resolve(__dirname, dbPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a request to the server to take a snapshot of the SQLite database.
|
||||||
|
* @param {Page|null} page Page
|
||||||
|
* @returns {Promise<Response>} Promise of response from snapshot request.
|
||||||
|
*/
|
||||||
|
export async function takeSqliteSnapshot(page = null) {
|
||||||
|
if (page) {
|
||||||
|
return page.goto("./_e2e/take-sqlite-snapshot");
|
||||||
|
} else {
|
||||||
|
return fetch(`${serverUrl}/_e2e/take-sqlite-snapshot`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a request to the server to restore the snapshot of the SQLite database.
|
||||||
|
* @returns {Promise<Response>} Promise of response from restoration request.
|
||||||
|
*/
|
||||||
|
export async function restoreSqliteSnapshot() {
|
||||||
|
return fetch(`${serverUrl}/_e2e/restore-sqlite-snapshot`);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue