test: stabilize the smoke lane

This commit is contained in:
lucaronin
2026-04-08 09:50:14 +02:00
parent f66c8cf8ca
commit be57656d75
6 changed files with 100 additions and 92 deletions

View File

@@ -13,7 +13,7 @@
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "playwright test",
"playwright:smoke": "playwright test --config playwright.smoke.config.ts tests/smoke/example.spec.ts tests/smoke/fix-ai-chat-empty-body-v3.spec.ts tests/smoke/raw-editor-sync-to-blocknote.spec.ts tests/integration/vault-workflows.spec.ts",
"playwright:smoke": "playwright test --config playwright.smoke.config.ts tests/smoke/example.spec.ts tests/smoke/fix-ai-chat-empty-body-v3.spec.ts tests/smoke/create-note-crash.spec.ts tests/smoke/raw-editor-sync-to-blocknote.spec.ts tests/smoke/rename-wikilink-update.spec.ts",
"playwright:regression": "playwright test tests/smoke/",
"playwright:integration": "playwright test --config playwright.integration.config.ts",
"test:coverage": "vitest run --coverage",

View File

@@ -0,0 +1,82 @@
import { expect, type Page } from '@playwright/test'
import fs from 'fs'
import os from 'os'
import path from 'path'
const FIXTURE_VAULT = path.resolve('tests/fixtures/test-vault')
function copyDirSync(src: string, dest: string): void {
fs.mkdirSync(dest, { recursive: true })
for (const item of fs.readdirSync(src, { withFileTypes: true })) {
const sourcePath = path.join(src, item.name)
const destinationPath = path.join(dest, item.name)
if (item.isDirectory()) {
copyDirSync(sourcePath, destinationPath)
continue
}
fs.copyFileSync(sourcePath, destinationPath)
}
}
export function createFixtureVaultCopy(): string {
const tempVaultDir = fs.mkdtempSync(path.join(os.tmpdir(), 'laputa-test-vault-'))
copyDirSync(FIXTURE_VAULT, tempVaultDir)
return tempVaultDir
}
export function removeFixtureVaultCopy(tempVaultDir: string | null | undefined): void {
if (!tempVaultDir) return
fs.rmSync(tempVaultDir, { recursive: true, force: true })
}
export async function openFixtureVault(
page: Page,
vaultPath: string,
): Promise<void> {
await page.addInitScript((resolvedVaultPath: string) => {
localStorage.clear()
const nativeFetch = window.fetch.bind(window)
window.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
const requestUrl = typeof input === 'string'
? input
: input instanceof Request
? input.url
: input.toString()
if (requestUrl.endsWith('/api/vault/ping') || requestUrl.includes('/api/vault/ping?')) {
return Promise.resolve(new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
}))
}
return nativeFetch(input, init)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let ref: any = null
Object.defineProperty(window, '__mockHandlers', {
configurable: true,
set(value) {
ref = value
ref.load_vault_list = () => ({
vaults: [{ label: 'Test Vault', path: resolvedVaultPath }],
active_vault: resolvedVaultPath,
hidden_defaults: [],
})
ref.check_vault_exists = () => true
ref.get_last_vault_path = () => resolvedVaultPath
ref.get_default_vault_path = () => resolvedVaultPath
ref.save_vault_list = () => null
},
get() {
return ref
},
})
}, vaultPath)
await page.goto('/')
await page.locator('[data-testid="note-list-container"]').waitFor({ timeout: 15_000 })
await expect(page.getByText('Alpha Project', { exact: true }).first()).toBeVisible({ timeout: 15_000 })
}

View File

@@ -8,58 +8,18 @@
import { test, expect } from '@playwright/test'
import fs from 'fs'
import path from 'path'
import os from 'os'
const FIXTURE_VAULT = path.resolve('tests/fixtures/test-vault')
function copyDirSync(src: string, dest: string): void {
fs.mkdirSync(dest, { recursive: true })
for (const item of fs.readdirSync(src, { withFileTypes: true })) {
const s = path.join(src, item.name)
const d = path.join(dest, item.name)
if (item.isDirectory()) copyDirSync(s, d)
else fs.copyFileSync(s, d)
}
}
import { createFixtureVaultCopy, openFixtureVault, removeFixtureVaultCopy } from '../helpers/fixtureVault'
let tempVaultDir: string
test.beforeEach(async ({ page }) => {
// Fresh vault copy for each test
tempVaultDir = fs.mkdtempSync(path.join(os.tmpdir(), 'laputa-test-vault-'))
copyDirSync(FIXTURE_VAULT, tempVaultDir)
// Intercept window.__mockHandlers assignment to override vault path handlers.
// The Object.defineProperty setter fires when mock-tauri/index.ts sets
// window.__mockHandlers = mockHandlers, letting us patch the same object
// reference that mockInvoke reads from.
await page.addInitScript((vaultPath: string) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let ref: any = null
Object.defineProperty(window, '__mockHandlers', {
set(val) {
ref = val
ref.load_vault_list = () => ({
vaults: [{ label: 'Test Vault', path: vaultPath }],
active_vault: vaultPath,
})
ref.check_vault_exists = () => true
ref.get_last_vault_path = () => vaultPath
ref.get_default_vault_path = () => vaultPath
ref.save_vault_list = () => null
},
get() { return ref },
configurable: true,
})
}, tempVaultDir)
await page.goto('/')
// Wait until the real vault entries are loaded in the sidebar
await page.getByText('Alpha Project', { exact: true }).first().waitFor({ timeout: 10_000 })
test.beforeEach(async ({ page }, testInfo) => {
testInfo.setTimeout(60_000)
tempVaultDir = createFixtureVaultCopy()
await openFixtureVault(page, tempVaultDir)
})
test.afterEach(async () => {
fs.rmSync(tempVaultDir, { recursive: true, force: true })
removeFixtureVaultCopy(tempVaultDir)
})
/** Helper: locate the note-list container. */

View File

@@ -5,7 +5,7 @@ function isCrashError(msg: string): boolean {
return msg.includes('Maximum update depth') || msg.includes('Invalid hook call') || msg.includes('#185')
}
test('create note via Cmd+N does not crash', async ({ page }) => {
test('create note via Cmd+N does not crash @smoke', async ({ page }) => {
const errors: string[] = []
page.on('pageerror', (err) => { if (isCrashError(err.message)) errors.push(err.message) })
@@ -17,7 +17,9 @@ test('create note via Cmd+N does not crash', async ({ page }) => {
await page.waitForTimeout(2000)
expect(errors).toHaveLength(0)
await expect(page.locator('[data-testid="title-field-input"]')).toBeVisible()
const noteList = page.locator('[data-testid="note-list-container"]')
await expect(noteList.getByText(/Untitled Note/i).first()).toBeVisible({ timeout: 5000 })
await expect(page.getByRole('textbox').last()).toBeVisible()
})
test('create note via sidebar + button does not crash', async ({ page }) => {

View File

@@ -1,52 +1,16 @@
import { test, expect } from '@playwright/test'
import fs from 'fs'
import path from 'path'
import os from 'os'
const FIXTURE_VAULT = path.resolve('tests/fixtures/test-vault')
function copyDirSync(src: string, dest: string): void {
fs.mkdirSync(dest, { recursive: true })
for (const item of fs.readdirSync(src, { withFileTypes: true })) {
const s = path.join(src, item.name)
const d = path.join(dest, item.name)
if (item.isDirectory()) copyDirSync(s, d)
else fs.copyFileSync(s, d)
}
}
import { createFixtureVaultCopy, openFixtureVault, removeFixtureVaultCopy } from '../helpers/fixtureVault'
let tempVaultDir: string
test.beforeEach(async ({ page }) => {
tempVaultDir = fs.mkdtempSync(path.join(os.tmpdir(), 'laputa-test-vault-'))
copyDirSync(FIXTURE_VAULT, tempVaultDir)
await page.addInitScript((vaultPath: string) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let ref: any = null
Object.defineProperty(window, '__mockHandlers', {
set(val) {
ref = val
ref.load_vault_list = () => ({
vaults: [{ label: 'Test Vault', path: vaultPath }],
active_vault: vaultPath,
})
ref.check_vault_exists = () => true
ref.get_last_vault_path = () => vaultPath
ref.get_default_vault_path = () => vaultPath
ref.save_vault_list = () => null
},
get() { return ref },
configurable: true,
})
}, tempVaultDir)
await page.goto('/')
await page.getByText('Alpha Project', { exact: true }).first().waitFor({ timeout: 10_000 })
test.beforeEach(async ({ page }, testInfo) => {
testInfo.setTimeout(60_000)
tempVaultDir = createFixtureVaultCopy()
await openFixtureVault(page, tempVaultDir)
})
test.afterEach(async () => {
fs.rmSync(tempVaultDir, { recursive: true, force: true })
removeFixtureVaultCopy(tempVaultDir)
})
test('creating an untitled draft hides the legacy title section in the editor', async ({ page }) => {

View File

@@ -7,7 +7,7 @@ test.describe('Renaming a note updates wikilinks across the vault', () => {
await page.waitForLoadState('networkidle')
})
test('title field rename triggers rename flow and shows toast', async ({ page }) => {
test('title field rename triggers rename flow and shows toast @smoke', async ({ page }) => {
// 1. Click the first note in the list to open it
const noteListContainer = page.locator('[data-testid="note-list-container"]')
await noteListContainer.waitFor({ timeout: 5000 })