Add Playwright database E2E inspections
This commit is contained in:
@@ -9,6 +9,8 @@ tmp/
|
|||||||
*.log
|
*.log
|
||||||
*.sql
|
*.sql
|
||||||
*.sql.gz
|
*.sql.gz
|
||||||
|
test-results/
|
||||||
|
playwright-report/
|
||||||
|
|
||||||
# Keep source Excel files local unless we explicitly decide otherwise.
|
# Keep source Excel files local unless we explicitly decide otherwise.
|
||||||
*.xlsm
|
*.xlsm
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ This project replaces the Excel/VBA workflow named `Catalog maker - 20` with a N
|
|||||||
- Before the first push, initialize Git if needed, configure the project remote and create an initial baseline commit from safe files only.
|
- Before the first push, initialize Git if needed, configure the project remote and create an initial baseline commit from safe files only.
|
||||||
- If a push fails, do not expose credentials in the terminal output or in chat; report the failure without printing secrets.
|
- If a push fails, do not expose credentials in the terminal output or in chat; report the failure without printing secrets.
|
||||||
|
|
||||||
|
## UI inspection workflow
|
||||||
|
|
||||||
|
- Use Playwright for browser inspections and visual verification of the local application.
|
||||||
|
- Run `npm run test:ui` after frontend or layout changes.
|
||||||
|
- Check for page errors, visible controls, modal behavior and important responsive states.
|
||||||
|
- Keep screenshots and traces in Playwright's ignored `test-results/` and `playwright-report/` directories; do not commit them.
|
||||||
|
- A UI change is not complete until the relevant Playwright checks pass.
|
||||||
|
|
||||||
## Runtime
|
## Runtime
|
||||||
|
|
||||||
- Start the web app with `npm run dev`.
|
- Start the web app with `npm run dev`.
|
||||||
|
|||||||
Generated
+50
@@ -12,6 +12,9 @@
|
|||||||
"mariadb": "^3.5.3",
|
"mariadb": "^3.5.3",
|
||||||
"selenium-webdriver": "^4.46.0"
|
"selenium-webdriver": "^4.46.0"
|
||||||
},
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"playwright": "^1.62.1"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
}
|
}
|
||||||
@@ -141,6 +144,21 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/geckodriver": {
|
"node_modules/geckodriver": {
|
||||||
"version": "6.1.1",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.1.tgz",
|
||||||
@@ -308,6 +326,38 @@
|
|||||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
"license": "(MIT AND Zlib)"
|
"license": "(MIT AND Zlib)"
|
||||||
},
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.62.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
|
||||||
|
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.62.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.62.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
|
||||||
|
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process-nextick-args": {
|
"node_modules/process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
|||||||
+6
-1
@@ -8,7 +8,9 @@
|
|||||||
"start": "node src/cli.js",
|
"start": "node src/cli.js",
|
||||||
"dev": "node src/server.js",
|
"dev": "node src/server.js",
|
||||||
"catalog:dry-run": "node src/cli.js catalog-maker --dry-run",
|
"catalog:dry-run": "node src/cli.js catalog-maker --dry-run",
|
||||||
"test": "node --test"
|
"test": "node --test",
|
||||||
|
"test:ui": "playwright test",
|
||||||
|
"test:e2e": "playwright test tests/ui/database.e2e.spec.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
@@ -17,5 +19,8 @@
|
|||||||
"geckodriver": "^6.1.1",
|
"geckodriver": "^6.1.1",
|
||||||
"mariadb": "^3.5.3",
|
"mariadb": "^3.5.3",
|
||||||
"selenium-webdriver": "^4.46.0"
|
"selenium-webdriver": "^4.46.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"playwright": "^1.62.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { defineConfig, devices } from "playwright/test";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./tests/ui",
|
||||||
|
timeout: 30_000,
|
||||||
|
expect: {
|
||||||
|
timeout: 5_000,
|
||||||
|
},
|
||||||
|
fullyParallel: true,
|
||||||
|
forbidOnly: Boolean(process.env.CI),
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
reporter: [["list"], ["html", { open: "never" }]],
|
||||||
|
use: {
|
||||||
|
baseURL: "http://127.0.0.1:3404",
|
||||||
|
trace: "retain-on-failure",
|
||||||
|
screenshot: "only-on-failure",
|
||||||
|
video: "retain-on-failure",
|
||||||
|
...devices["Desktop Chrome"],
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
command: "node src/server.js",
|
||||||
|
url: "http://127.0.0.1:3404/api/status",
|
||||||
|
reuseExistingServer: true,
|
||||||
|
timeout: 30_000,
|
||||||
|
},
|
||||||
|
});
|
||||||
+1
-1
@@ -131,7 +131,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<label class="settings-field">
|
<label class="settings-field">
|
||||||
<span>Database</span>
|
<span>Database</span>
|
||||||
<input id="localDbName" type="text" autocomplete="off" value="catalog_maker_test" />
|
<input id="localDbName" type="text" autocomplete="off" value="9bplus" />
|
||||||
</label>
|
</label>
|
||||||
<label class="settings-field">
|
<label class="settings-field">
|
||||||
<span>User</span>
|
<span>User</span>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { test, expect } from "playwright/test";
|
||||||
|
|
||||||
|
test("catalog maker shell loads without page errors", async ({ page }) => {
|
||||||
|
const pageErrors = [];
|
||||||
|
page.on("pageerror", (error) => pageErrors.push(error.message));
|
||||||
|
|
||||||
|
await page.goto("/");
|
||||||
|
await expect(page.locator("#settingsButton")).toBeVisible();
|
||||||
|
await expect(page.locator("#recordId")).toBeVisible();
|
||||||
|
await expect(page.locator("#variantHeader")).toBeVisible();
|
||||||
|
|
||||||
|
await page.screenshot({ path: "test-results/catalog-maker-shell.png", fullPage: true });
|
||||||
|
expect(pageErrors).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("settings modal opens and shows local database controls", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await page.locator("#settingsButton").click();
|
||||||
|
|
||||||
|
await expect(page.locator("#settingsModal")).toBeVisible();
|
||||||
|
await expect(page.locator("#localDbHost")).toBeVisible();
|
||||||
|
await expect(page.locator("#testLocalDbButton")).toBeVisible();
|
||||||
|
});
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { test, expect } from "playwright/test";
|
||||||
|
|
||||||
|
test.describe("database connection", () => {
|
||||||
|
test("tests the configured Local DB connection", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await page.locator("#settingsButton").click();
|
||||||
|
|
||||||
|
await expect(page.locator('input[name="databaseMode"][value="local"]')).toBeChecked();
|
||||||
|
await page.locator("#localDbHost").fill("127.0.0.1");
|
||||||
|
await page.locator("#localDbPort").fill("3306");
|
||||||
|
await page.locator("#localDbName").fill("9bplus");
|
||||||
|
await page.locator("#localDbUser").fill("root");
|
||||||
|
await page.locator("#localDbPassword").fill("root");
|
||||||
|
|
||||||
|
await page.locator("#testLocalDbButton").click();
|
||||||
|
await expect(page.locator("#localDbTestResult")).toContainText("Connected:");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("loads manufacturers from Local DB without Live endpoint fallback", async ({ page }) => {
|
||||||
|
const manufacturersResponse = page.waitForResponse("**/api/manufacturers");
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
const response = await manufacturersResponse;
|
||||||
|
expect(response.ok()).toBeTruthy();
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data.items.length).toBeGreaterThan(0);
|
||||||
|
expect(data.items.some((item) => item.name === "La Sportiva")).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user