mirror of
https://github.com/zadam/trilium.git
synced 2026-05-06 16:26:59 +02:00
e2e: make tests reusable for standalone
This commit is contained in:
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@@ -154,7 +154,7 @@ pnpm desktop:build # Build desktop application
|
||||
### Test Organization
|
||||
- **Server tests** (`apps/server/spec/`): Must run sequentially (shared database state)
|
||||
- **Client tests** (`apps/client/src/`): Can run in parallel
|
||||
- **E2E tests** (`apps/server-e2e/`): Use Playwright for integration testing
|
||||
- **E2E tests** (`packages/trilium-e2e/`): Shared Playwright tests, run via `pnpm --filter server e2e` or `pnpm --filter client-standalone e2e`
|
||||
- **ETAPI tests** (`apps/server/spec/etapi/`): External API contract tests
|
||||
|
||||
**Pattern**: When adding new API endpoints, add tests in `spec/etapi/` following existing patterns (see `search.spec.ts`).
|
||||
|
||||
2
.github/workflows/main-docker.yml
vendored
2
.github/workflows/main-docker.yml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
require-healthy: true
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm --filter=server-e2e e2e
|
||||
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm --filter=server e2e
|
||||
|
||||
- name: Upload Playwright trace
|
||||
if: failure()
|
||||
|
||||
4
.github/workflows/playwright.yml
vendored
4
.github/workflows/playwright.yml
vendored
@@ -73,14 +73,14 @@ jobs:
|
||||
sleep 10
|
||||
|
||||
- name: Server end-to-end tests
|
||||
run: pnpm --filter server-e2e e2e
|
||||
run: pnpm --filter server e2e
|
||||
|
||||
- name: Upload test report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: e2e report ${{ matrix.arch }}
|
||||
path: apps/server-e2e/test-output
|
||||
path: apps/server/test-output
|
||||
|
||||
- name: Kill the server
|
||||
if: always()
|
||||
|
||||
@@ -59,7 +59,6 @@ apps/
|
||||
desktop/ # Electron (bundles server + client)
|
||||
client-standalone/ # Standalone client (WASM + service workers, no Node.js)
|
||||
standalone-desktop/ # Standalone desktop variant
|
||||
server-e2e/ # Playwright E2E tests for server
|
||||
web-clipper/ # Browser extension
|
||||
website/ # Project website
|
||||
db-compare/, dump-db/, edit-docs/, build-docs/, icon-pack-builder/
|
||||
@@ -67,6 +66,7 @@ apps/
|
||||
packages/
|
||||
trilium-core/ # Core business logic: entities, services, SQL, sync
|
||||
commons/ # Shared interfaces and utilities
|
||||
trilium-e2e/ # Shared Playwright E2E tests
|
||||
ckeditor5/ # Custom rich text editor bundle
|
||||
codemirror/ # Code editor integration
|
||||
highlightjs/ # Syntax highlighting
|
||||
@@ -248,7 +248,7 @@ Use `note.getOwnedAttribute()` for direct, `note.getAttribute()` for inherited.
|
||||
|
||||
- **Server tests** (`apps/server/spec/`): Vitest, must run sequentially (shared DB), forks pool, max 6 workers
|
||||
- **Client tests** (`apps/client/src/`): Vitest with happy-dom environment, can run in parallel
|
||||
- **E2E tests** (`apps/server-e2e/`): Playwright, Chromium, server started automatically on port 8082
|
||||
- **E2E tests** (`packages/trilium-e2e/`): Shared Playwright tests, run via `pnpm --filter server e2e` or `pnpm --filter client-standalone e2e`
|
||||
- **ETAPI tests** (`apps/server/spec/etapi/`): External API contract tests
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"dev": "vite dev",
|
||||
"test": "vitest",
|
||||
"start-prod": "pnpm build && pnpm vite preview --port 8888",
|
||||
"coverage": "vitest --coverage"
|
||||
"coverage": "vitest --coverage",
|
||||
"e2e": "pnpm build && playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@excalidraw/excalidraw": "0.18.0",
|
||||
|
||||
16
apps/client-standalone/playwright.config.ts
Normal file
16
apps/client-standalone/playwright.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { createBaseConfig } from "../../packages/trilium-e2e/src/base-config";
|
||||
|
||||
const port = process.env["TRILIUM_PORT"] ?? "8082";
|
||||
const baseURL = process.env["BASE_URL"] || `http://127.0.0.1:${port}`;
|
||||
|
||||
export default createBaseConfig({
|
||||
appDir: __dirname,
|
||||
projectName: "standalone",
|
||||
webServer: !process.env.TRILIUM_DOCKER ? {
|
||||
command: "pnpm vite preview --port " + port,
|
||||
url: baseURL,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
cwd: __dirname,
|
||||
timeout: 5 * 60 * 1000
|
||||
} : undefined,
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { join } from 'path';
|
||||
|
||||
// For CI, you may want to set BASE_URL to the deployed application.
|
||||
const port = process.env['TRILIUM_PORT'] ?? "8082";
|
||||
const baseURL = process.env['BASE_URL'] || `http://127.0.0.1:${port}`;
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "src",
|
||||
reporter: [["list"], ["html", { outputFolder: "test-output" }]],
|
||||
outputDir: "test-output",
|
||||
retries: 3,
|
||||
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
baseURL,
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: !process.env.TRILIUM_DOCKER ? {
|
||||
command: 'pnpm start-prod-no-dir',
|
||||
url: baseURL,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
cwd: join(__dirname, "../server"),
|
||||
env: {
|
||||
TRILIUM_DATA_DIR: "spec/db",
|
||||
TRILIUM_PORT: port,
|
||||
TRILIUM_INTEGRATION_TEST: "memory"
|
||||
},
|
||||
timeout: 5 * 60 * 1000
|
||||
} : undefined,
|
||||
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import App from "./support/app";
|
||||
import App, { getBaseUrl } from "../../../packages/trilium-e2e/src/support/app";
|
||||
|
||||
const BASE_URL = "http://127.0.0.1:8082";
|
||||
const BASE_URL = getBaseUrl();
|
||||
|
||||
/**
|
||||
* E2E tests for exact search functionality using the leading "=" operator.
|
||||
@@ -1,5 +1,5 @@
|
||||
import { test, expect, Page } from "@playwright/test";
|
||||
import App from "./support/app";
|
||||
import App from "../../../packages/trilium-e2e/src/support/app";
|
||||
|
||||
test("Goes to share root", async ({ page, context }) => {
|
||||
const app = new App(page, context);
|
||||
@@ -16,6 +16,7 @@
|
||||
"test-build": "vitest --config vitest.build.config.mts",
|
||||
"start-prod": "cross-env TRILIUM_DATA_DIR=data pnpm start-prod-no-dir",
|
||||
"start-prod-no-dir": "pnpm build && cross-env TRILIUM_ENV=production TRILIUM_PORT=8082 node dist/main.cjs",
|
||||
"e2e": "playwright test",
|
||||
"circular-deps": "dpdm -T src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular",
|
||||
"docker-build-debian": "pnpm build && docker build . -t triliumnext-debian -f Dockerfile",
|
||||
"docker-build-alpine": "pnpm build && docker build . -t triliumnext-alpine -f Dockerfile.alpine",
|
||||
|
||||
22
apps/server/playwright.config.ts
Normal file
22
apps/server/playwright.config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createBaseConfig } from "../../packages/trilium-e2e/src/base-config";
|
||||
|
||||
const port = process.env["TRILIUM_PORT"] ?? "8082";
|
||||
const baseURL = process.env["BASE_URL"] || `http://127.0.0.1:${port}`;
|
||||
|
||||
export default createBaseConfig({
|
||||
appDir: __dirname,
|
||||
localTestDir: "e2e",
|
||||
projectName: "server",
|
||||
webServer: !process.env.TRILIUM_DOCKER ? {
|
||||
command: "pnpm start-prod-no-dir",
|
||||
url: baseURL,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
cwd: __dirname,
|
||||
env: {
|
||||
TRILIUM_DATA_DIR: "spec/db",
|
||||
TRILIUM_PORT: port,
|
||||
TRILIUM_INTEGRATION_TEST: "memory"
|
||||
},
|
||||
timeout: 5 * 60 * 1000
|
||||
} : undefined,
|
||||
});
|
||||
10
docs/Developer Guide/Developer Guide/Testing.md
vendored
10
docs/Developer Guide/Developer Guide/Testing.md
vendored
@@ -26,11 +26,13 @@ apps/
|
||||
│ └── src/**/*.spec.ts # Server tests
|
||||
├── client/
|
||||
│ └── src/**/*.spec.ts # Client tests
|
||||
└── server-e2e/
|
||||
│ └── tests/**/*.spec.ts # E2E tests
|
||||
├── server/
|
||||
│ └── e2e/**/*.spec.ts # Server-specific E2E tests
|
||||
└── desktop/
|
||||
└── e2e
|
||||
└── tests/**/*.spec.ts # E2E tests
|
||||
└── e2e/**/*.spec.ts # Desktop E2E tests
|
||||
packages/
|
||||
└── trilium-e2e/
|
||||
└── src/**/*.spec.ts # Shared E2E tests
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
* Playwright with Electron
|
||||
* Tests some basic functionality such as creating a new document.
|
||||
|
||||
These can be found in `apps/server-e2e` and `apps/desktop/e2e`.
|
||||
Shared E2E tests live in `packages/trilium-e2e/`. Server-specific tests are in `apps/server/e2e/`, desktop tests in `apps/desktop/e2e/`.
|
||||
|
||||
Run E2E tests via:
|
||||
- `pnpm --filter server e2e` (server)
|
||||
- `pnpm --filter client-standalone e2e` (standalone)
|
||||
- `pnpm --filter desktop e2e` (desktop/Electron)
|
||||
|
||||
## First-time run
|
||||
|
||||
|
||||
@@ -81,7 +81,9 @@ const mainConfig = [
|
||||
|
||||
const playwrightConfig = {
|
||||
files: [
|
||||
"apps/server-e2e/src/**/*.spec.ts",
|
||||
"packages/trilium-e2e/src/**/*.spec.ts",
|
||||
"apps/server/e2e/**/*.spec.ts",
|
||||
"apps/client-standalone/e2e/**/*.spec.ts",
|
||||
"apps/desktop/e2e/**/*.spec.ts"
|
||||
],
|
||||
plugins: { playwright },
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
"apps/dump-db"
|
||||
"apps/edit-docs"
|
||||
"apps/server"
|
||||
"apps/server-e2e"
|
||||
"packages/trilium-e2e"
|
||||
];
|
||||
|
||||
desktopItems = lib.optionals (app == "desktop") [
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"name": "@triliumnext/server-e2e",
|
||||
"name": "@triliumnext/trilium-e2e",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"e2e": "playwright test"
|
||||
},
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"dotenv": "17.4.1"
|
||||
}
|
||||
63
packages/trilium-e2e/src/base-config.ts
Normal file
63
packages/trilium-e2e/src/base-config.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { defineConfig, devices, type PlaywrightTestConfig } from "@playwright/test";
|
||||
import { join } from "path";
|
||||
|
||||
interface BaseConfigOptions {
|
||||
/**
|
||||
* The directory of the calling app (i.e. `__dirname` from the app's playwright.config.ts).
|
||||
*/
|
||||
appDir: string;
|
||||
|
||||
/**
|
||||
* Optional local test directory for app-specific tests (relative to appDir).
|
||||
* If provided, a second project is added for app-specific tests.
|
||||
*/
|
||||
localTestDir?: string;
|
||||
|
||||
/**
|
||||
* The name for the app-specific test project (e.g. "server", "standalone").
|
||||
*/
|
||||
projectName: string;
|
||||
|
||||
/**
|
||||
* Optional webServer configuration to start the app before tests.
|
||||
*/
|
||||
webServer?: PlaywrightTestConfig["webServer"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a base Playwright configuration that includes the shared trilium-e2e
|
||||
* tests and optionally app-specific tests.
|
||||
*/
|
||||
export function createBaseConfig({ appDir, localTestDir, projectName, webServer }: BaseConfigOptions) {
|
||||
const port = process.env["TRILIUM_PORT"] ?? "8082";
|
||||
const baseURL = process.env["BASE_URL"] || `http://127.0.0.1:${port}`;
|
||||
const sharedTestDir = join(__dirname);
|
||||
|
||||
const projects: PlaywrightTestConfig["projects"] = [
|
||||
{
|
||||
name: `${projectName}-shared`,
|
||||
testDir: sharedTestDir,
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
}
|
||||
];
|
||||
|
||||
if (localTestDir) {
|
||||
projects.push({
|
||||
name: projectName,
|
||||
testDir: join(appDir, localTestDir),
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
});
|
||||
}
|
||||
|
||||
return defineConfig({
|
||||
reporter: [["list"], ["html", { outputFolder: join(appDir, "test-output") }]],
|
||||
outputDir: join(appDir, "test-output"),
|
||||
retries: 3,
|
||||
use: {
|
||||
baseURL,
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
webServer,
|
||||
projects,
|
||||
});
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import App from "./support/app";
|
||||
test("Can duplicate note with broken links", async ({ page, context }) => {
|
||||
const app = new App(page, context);
|
||||
await app.goto({
|
||||
url: "http://localhost:8082/#root/Q5abPvymDH6C/2VammGGdG6Ie"
|
||||
url: "/#root/Q5abPvymDH6C/2VammGGdG6Ie"
|
||||
});
|
||||
|
||||
await app.noteTree.getByText("Note map").first().click({ button: "right" });
|
||||
@@ -4,21 +4,21 @@ import App from "./support/app";
|
||||
|
||||
test("Native Title Bar not displayed on web", async ({ page, context }) => {
|
||||
const app = new App(page, context);
|
||||
await app.goto({ url: "http://localhost:8082/#root/_hidden/_options/_optionsAppearance" });
|
||||
await app.goto({ url: "/#root/_hidden/_options/_optionsAppearance" });
|
||||
await expect(app.currentNoteSplitContent.getByRole("heading", { name: "Theme" })).toBeVisible();
|
||||
await expect(app.currentNoteSplitContent.getByRole("heading", { name: "Native Title Bar (requires" })).toBeHidden();
|
||||
});
|
||||
|
||||
test("Tray settings not displayed on web", async ({ page, context }) => {
|
||||
const app = new App(page, context);
|
||||
await app.goto({ url: "http://localhost:8082/#root/_hidden/_options/_optionsOther" });
|
||||
await app.goto({ url: "/#root/_hidden/_options/_optionsOther" });
|
||||
await expect(app.currentNoteSplitContent.getByRole("heading", { name: "Note Erasure Timeout" })).toBeVisible();
|
||||
await expect(app.currentNoteSplitContent.getByRole("heading", { name: "Tray" })).toBeHidden();
|
||||
});
|
||||
|
||||
test("Spellcheck settings not displayed on web", async ({ page, context }) => {
|
||||
const app = new App(page, context);
|
||||
await app.goto({ url: "http://localhost:8082/#root/_hidden/_options/_optionsSpellcheck" });
|
||||
await app.goto({ url: "/#root/_hidden/_options/_optionsSpellcheck" });
|
||||
await expect(app.currentNoteSplitContent.getByText("These options apply only for desktop builds")).toBeVisible();
|
||||
await expect(app.currentNoteSplitContent.getByText("Check spelling")).toBeHidden();
|
||||
});
|
||||
@@ -7,7 +7,10 @@ interface GotoOpts {
|
||||
preserveTabs?: boolean;
|
||||
}
|
||||
|
||||
const BASE_URL = "http://127.0.0.1:8082";
|
||||
export function getBaseUrl(): string {
|
||||
const port = process.env["TRILIUM_PORT"] ?? "8082";
|
||||
return process.env["BASE_URL"] || `http://127.0.0.1:${port}`;
|
||||
}
|
||||
|
||||
interface DropdownLocator extends Locator {
|
||||
selectOptionByText: (text: string) => Promise<void>;
|
||||
@@ -48,7 +51,7 @@ export default class App {
|
||||
|
||||
await this.context.addCookies([
|
||||
{
|
||||
url: BASE_URL,
|
||||
url: getBaseUrl(),
|
||||
name: "trilium-device",
|
||||
value: isMobile ? "mobile" : "desktop"
|
||||
}
|
||||
@@ -165,7 +168,7 @@ export default class App {
|
||||
|
||||
expect(csrfToken).toBeTruthy();
|
||||
await expect(
|
||||
await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, {
|
||||
await this.page.request.put(`${getBaseUrl()}/api/options/${key}/${value}`, {
|
||||
headers: {
|
||||
"x-csrf-token": csrfToken
|
||||
}
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -1070,12 +1070,6 @@ importers:
|
||||
specifier: 3.3.0
|
||||
version: 3.3.0
|
||||
|
||||
apps/server-e2e:
|
||||
devDependencies:
|
||||
dotenv:
|
||||
specifier: 17.4.1
|
||||
version: 17.4.1
|
||||
|
||||
apps/web-clipper:
|
||||
dependencies:
|
||||
cash-dom:
|
||||
@@ -1702,6 +1696,12 @@ importers:
|
||||
specifier: 2.16.1
|
||||
version: 2.16.1
|
||||
|
||||
packages/trilium-e2e:
|
||||
devDependencies:
|
||||
dotenv:
|
||||
specifier: 17.4.1
|
||||
version: 17.4.1
|
||||
|
||||
packages/turndown-plugin-gfm:
|
||||
devDependencies:
|
||||
happy-dom:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"path": "./apps/server"
|
||||
},
|
||||
{
|
||||
"path": "./apps/server-e2e"
|
||||
"path": "./packages/trilium-e2e"
|
||||
},
|
||||
{
|
||||
"path": "./apps/client"
|
||||
|
||||
Reference in New Issue
Block a user