Skip to content

Commit 7a2f97a

Browse files
committed
#3414 webpage: start with playwright tests
Signed-off-by: Patrizio Bekerle <[email protected]>
1 parent 5afdfbd commit 7a2f97a

File tree

10 files changed

+311
-3
lines changed

10 files changed

+311
-3
lines changed

webpage/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ basement_dist
1414
.shared
1515
src/.vuepress/.cache
1616
src/.vuepress/.temp
17+
test-results
18+
playwright-report/
1719

1820
# Devenv
1921
.devenv*

webpage/config.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import process from "node:process";
2+
import { test } from "@playwright/test";
3+
import fs from "node:fs";
4+
5+
if (fs.existsSync(".env")) {
6+
process.loadEnvFile(".env");
7+
}
8+
9+
let defaultConfig = {
10+
baseURL: "https://www.qownnotes.org",
11+
};
12+
13+
let localOverride = {
14+
baseURL: "http://localhost:8080/",
15+
};
16+
17+
function deepMerge(target, source) {
18+
const result = structuredClone(target);
19+
20+
function merge(obj, src) {
21+
for (const key in src) {
22+
if (
23+
src[key] &&
24+
typeof src[key] === "object" &&
25+
!Array.isArray(src[key])
26+
) {
27+
obj[key] = obj[key] || {};
28+
merge(obj[key], src[key]);
29+
} else {
30+
obj[key] = src[key];
31+
}
32+
}
33+
}
34+
35+
merge(result, source);
36+
return result;
37+
}
38+
39+
const configs = {
40+
dev: defaultConfig,
41+
local: deepMerge(defaultConfig, localOverride),
42+
};
43+
44+
/**
45+
* @returns {defaultConfig}
46+
*/
47+
export function getConfig() {
48+
// Only call test.info() to make sure we are running in a test context, in case
49+
// we want to incorporate test-specific logic in the future.
50+
test.info();
51+
const env = process.env.TEST_ENV || "dev";
52+
const config = configs[env];
53+
if (!config) {
54+
throw new Error(
55+
`Unknown environment: ${env}. Available: ${Object.keys(configs).join(", ")}`,
56+
);
57+
}
58+
return config;
59+
}

webpage/devenv.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@
101101
"shared": {
102102
"flake": false,
103103
"locked": {
104-
"lastModified": 1766494448,
104+
"lastModified": 1766581418,
105105
"owner": "pbek",
106106
"repo": "nix-shared",
107-
"rev": "fa960706b6c038b33cf9b425f186cc94f000e6c1",
107+
"rev": "c6815e7f9959b7f4a0801697875775db75ae3bf3",
108108
"type": "github"
109109
},
110110
"original": {

webpage/devenv.nix

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,55 @@
88
zellij
99
];
1010

11+
env = {
12+
# We need to set PLAYWRIGHT_BROWSERS_PATH for Playwright to find it's ffmpeg binary
13+
PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright.browsers}";
14+
};
15+
16+
# Disable the hint that are printed when the dotenv module is not enabled, but .env is present
17+
# https://devenv.sh/reference/options/#dotenvdisablehint
18+
dotenv.disableHint = true;
19+
20+
# https://devenv.sh/scripts/
21+
scripts = {
22+
get_playwright_test_version = {
23+
exec = ''
24+
jq -r '.dependencies["@playwright/test"] // .devDependencies["@playwright/test"]' package.json
25+
'';
26+
packages = [ pkgs.jq ];
27+
description = "Get the Playwright Test npm version from package.json";
28+
};
29+
get_playwright_core_version = {
30+
exec = ''
31+
jq -r '.dependencies["playwright-core"] // .devDependencies["playwright-core"]' package.json
32+
'';
33+
packages = [ pkgs.jq ];
34+
description = "Get the Playwright Core npm version from package.json";
35+
};
36+
};
37+
1138
enterShell = ''
1239
echo "🛠️ QOwnNotes Webpage Dev Shell"
40+
playwrightTestNpmVersion="$(get_playwright_test_version)"
41+
playwrightCoreNpmVersion="$(get_playwright_core_version)"
42+
echo "❄️ Playwright nix version: ${pkgs.playwright.version}"
43+
echo "📦 Playwright Test npm version: $playwrightTestNpmVersion"
44+
echo "📦 Playwright Core npm version: $playwrightCoreNpmVersion"
45+
echo "📂 Playwright browsers path: $PLAYWRIGHT_BROWSERS_PATH"
46+
echo "📦 Chromium binary: $CHROMIUM_BIN"
47+
echo "📦 Firefox binary: $FIREFOX_BIN"
48+
49+
if [ "${pkgs.playwright.version}" != "$playwrightTestNpmVersion" ]; then
50+
echo "❌ Playwright Test versions in nix (in devenv.yaml) and npm (in package.json) are not the same! Please adapt the configuration."
51+
else
52+
echo "✅ Playwright Test versions in nix and npm are the same"
53+
fi
54+
55+
if [ "${pkgs.playwright.version}" != "$playwrightCoreNpmVersion" ]; then
56+
echo "❌ Playwright Core versions in nix (in devenv.yaml) and npm (in package.json) are not the same! Please adapt the configuration."
57+
else
58+
echo "✅ Playwright Core versions in nix and npm are the same"
59+
fi
1360
'';
1461

1562
git-hooks.hooks = {

webpage/devenv.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ inputs:
99
imports:
1010
- shared/common
1111
- shared/javascript
12+
- shared/playwright

webpage/justfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,12 @@ info:
7979
outdated:
8080
npm outdated
8181

82+
# Open the playwright test UI
83+
[group('dev')]
84+
playwright-test-ui:
85+
npm run playwright:test:ui
86+
87+
# Run the playwright tests
88+
[group('dev')]
89+
playwright-test test='':
90+
npm run playwright:test -- tests/{{ test }}

webpage/package-lock.json

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webpage/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@
1111
"repository": "/qownnotes-webpage",
1212
"scripts": {
1313
"dev": "./scripts/run-dev.sh",
14-
"build": "./scripts/run-build.sh"
14+
"build": "./scripts/run-build.sh",
15+
"playwright:test": "TEST_ENV=prod playwright test",
16+
"playwright:test:ui": "TEST_ENV=prod playwright test --ui",
17+
"playwright:test:local": "TEST_ENV=local playwright test",
18+
"playwright:test:local:ui": "TEST_ENV=local playwright test --ui"
1519
},
1620
"license": "MIT",
1721
"devDependencies": {
22+
"@playwright/test": "1.54.1",
1823
"@vuepress/bundler-vite": "next",
1924
"@vuepress/plugin-feed": "next",
2025
"@vuepress/plugin-markdown-chart": "^2.0.0-rc.109",
@@ -25,6 +30,7 @@
2530
"@vuepress/plugin-seo": "next",
2631
"@vuepress/plugin-sitemap": "next",
2732
"@vuepress/theme-default": "next",
33+
"playwright-core": "1.54.1",
2834
"glob": "^10.3.10",
2935
"lodash-es": "^4.17.21",
3036
"markdown-it": "^14.1.0",

webpage/playwright.config.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// @ts-check
2+
import process from "node:process";
3+
import { devices } from "@playwright/test";
4+
import {
5+
installBrowsersForNpmInstall,
6+
registry,
7+
} from "playwright-core/lib/server";
8+
9+
async function setup() {
10+
const browsersToInstall = [];
11+
if (!process.env.FIREFOX_BIN) {
12+
browsersToInstall.push("firefox");
13+
browsersToInstall.push("ffmpeg");
14+
}
15+
if (!process.env.CHROMIUM_BIN) {
16+
browsersToInstall.push("chromium");
17+
browsersToInstall.push("ffmpeg");
18+
}
19+
if (browsersToInstall.length > 0) {
20+
await installBrowsersForNpmInstall(browsersToInstall);
21+
}
22+
if (!process.env.FIREFOX_BIN) {
23+
process.env.FIREFOX_BIN = registry
24+
.findExecutable("firefox")
25+
.executablePath();
26+
}
27+
if (!process.env.CHROMIUM_BIN) {
28+
process.env.CHROMIUM_BIN = registry
29+
.findExecutable("chromium")
30+
.executablePath();
31+
}
32+
}
33+
34+
// In CI things can take longer in case there is too much load on the system.
35+
let timeoutMultiplier = process.env.CI ? 3 : 1;
36+
37+
await setup();
38+
39+
/**
40+
* @see https://playwright.dev/docs/test-configuration
41+
* @type {import('@playwright/test').PlaywrightTestConfig}
42+
*/
43+
const config = {
44+
testDir: "./",
45+
/* Maximum time one test can run for. */
46+
timeout: 30 * 1000 * timeoutMultiplier,
47+
expect: {
48+
/**
49+
* Maximum time expect() should wait for the condition to be met.
50+
* For example in `await expect(locator).toHaveText();`
51+
*/
52+
timeout: 5 * 1000 * timeoutMultiplier,
53+
},
54+
/* Run tests in files in parallel */
55+
fullyParallel: false,
56+
/* Fail the build on CI if you accidentally left test.only in the source code. */
57+
forbidOnly: !!process.env.CI,
58+
/* Retry on CI only */
59+
retries: process.env.CI ? 2 : 0,
60+
workers: 1,
61+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
62+
reporter: "html",
63+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
64+
use: {
65+
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
66+
actionTimeout: 0,
67+
/* Base URL to use in actions like `await page.goto('/')`. */
68+
// baseURL: 'http://localhost:3000',
69+
// ignoreHTTPSErrors: true,
70+
71+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
72+
trace: "on-first-retry",
73+
video: "on-first-retry",
74+
},
75+
76+
/* Configure projects for major browsers */
77+
projects: [
78+
{
79+
name: "chromium",
80+
use: {
81+
...devices["Desktop Chrome"],
82+
launchOptions: {
83+
executablePath: process.env.CHROMIUM_BIN,
84+
args: [
85+
"--ignore-certificate-errors",
86+
"--allow-file-access-from-files",
87+
"--use-fake-ui-for-media-stream",
88+
"--use-fake-device-for-media-stream",
89+
"--force-color-profile=srgb",
90+
],
91+
},
92+
},
93+
},
94+
{
95+
name: "firefox",
96+
use: {
97+
...devices["Desktop Firefox"],
98+
launchOptions: {
99+
executablePath: process.env.FIREFOX_BIN,
100+
},
101+
},
102+
},
103+
],
104+
105+
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
106+
outputDir: "test-results/",
107+
};
108+
109+
export default config;

webpage/tests/basic.spec.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @ts-check
2+
import { test, expect } from "@playwright/test";
3+
import { getConfig } from "../config.js";
4+
5+
// Test that all activities show
6+
test("basic", async ({ page }) => {
7+
const config = getConfig();
8+
await page.goto(config.baseURL);
9+
await expect(page.locator("title")).toContainText(/QOwnNotes/);
10+
});

0 commit comments

Comments
 (0)