Files
xinmi/scripts/verify_lazy_platform_modals.cjs
yawan f7fc992f57
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

161 lines
8.7 KiB
JavaScript

const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const { pathToFileURL } = require('node:url')
const { chromium } = require('/Users/macclaw/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright')
async function main() {
const directory = '/Volumes/Extreme SSD/yawan-storage/weknora/acceptance'
const root = path.resolve(__dirname, '../frontend')
const apiOrigin = process.env.VERIFY_API_ORIGIN || 'http://127.0.0.1:18080'
const checks = [], requestedAssets = new Set(), unexpectedMutations = [], pageErrors = []
const reportPath = path.join(directory, 'lazy-platform-modals-20260919.json')
const credentials = JSON.parse(fs.readFileSync(path.join(directory, 'super-admin-login.json'), 'utf8'))
const login = await fetch(`${apiOrigin}/api/v1/auth/login`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: credentials.email, password: credentials.password }),
})
assert.equal(login.status, 200, 'Read-only verification login')
const auth = await login.json()
process.env.VITE_DEV_PROXY_TARGET = apiOrigin
const { preview } = await import(pathToFileURL(path.join(root, 'node_modules/vite/dist/node/index.js')).href)
const server = await preview({ root, configFile: path.join(root, 'vite.config.ts'), preview: { host: '127.0.0.1', port: 15174, strictPort: true } })
const origin = server.resolvedUrls.local[0].replace(/\/$/, '')
let browser
async function check(name, fn) {
const started = Date.now()
try {
await fn()
checks.push({ name, passed: true, duration_ms: Date.now() - started })
console.log(JSON.stringify(checks.at(-1)))
} catch (error) {
checks.push({ name, passed: false, duration_ms: Date.now() - started, error: error.name, location: error.stack?.split('\n').filter(line => line.trim().startsWith('at ')).slice(0, 3) })
console.log(JSON.stringify(checks.at(-1)))
throw error
}
}
try {
browser = await chromium.launch({ headless: true, channel: 'chrome' })
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } })
await context.addInitScript(a => {
const values = {
weknora_user: JSON.stringify({ ...a.user, tenant_id: String(a.user.tenant_id) }),
weknora_tenant: JSON.stringify({ ...a.active_tenant, id: '10059' }),
weknora_token: a.token, weknora_refresh_token: a.refresh_token,
weknora_memberships: JSON.stringify(a.memberships),
}
for (const [key, value] of Object.entries(values)) localStorage.setItem(key, value)
for (const key of ['weknora:new-user-guide-done:v1', 'weknora:contextual-guide-kb-list:v2', 'weknora:contextual-guide-kb-create:v3', 'weknora:contextual-guide-tenant-models:v1']) localStorage.setItem(key, '1')
}, auth)
await context.route('**/api/**', async route => {
const request = route.request()
if (!['GET', 'HEAD', 'OPTIONS'].includes(request.method())) {
unexpectedMutations.push({ method: request.method(), path: new URL(request.url()).pathname })
return route.abort('blockedbyclient')
}
await route.continue()
})
const page = await context.newPage()
page.setDefaultTimeout(20000)
page.on('pageerror', error => pageErrors.push({ name: error.name }))
page.on('request', request => {
const pathname = new URL(request.url()).pathname
if (pathname.startsWith('/assets/')) requestedAssets.add(pathname)
})
const editor = page.locator('.settings-overlay').filter({ has: page.locator('[data-guide="kb-editor-sidebar"]') })
const settings = page.locator('.settings-overlay').filter({ has: page.locator('[data-guide="settings-models"]') })
const nameInput = editor.locator('[data-guide="kb-create-name"] input')
const create = async () => {
await page.locator('[data-guide="kb-list-create"]').first().click()
await nameInput.waitFor({ state: 'visible' })
}
const closeEditor = async () => {
await editor.locator('.close-btn').click()
await editor.waitFor({ state: 'hidden' })
}
await check('cold_knowledge_list_defers_hidden_modal_chunks', async () => {
await page.goto(`${origin}/platform/knowledge-bases`)
await page.locator('[data-guide="kb-list-create"]').first().waitFor({ state: 'visible' })
await page.waitForLoadState('networkidle')
assert.equal([...requestedAssets].filter(asset => /\/(Settings|KnowledgeBaseEditorModal)-.*\.js$/.test(asset)).length, 0)
})
await check('command_palette_keyboard_shortcut_remains_registered', async () => {
await page.keyboard.press('Meta+k')
await page.locator('.cmdk').waitFor({ state: 'visible' })
await page.keyboard.press('Escape')
await page.locator('.cmdk').waitFor({ state: 'hidden' })
})
await check('first_create_initializes_and_only_loads_editor', async () => {
await create()
assert.equal(await nameInput.inputValue(), '')
assert.ok([...requestedAssets].some(asset => /\/KnowledgeBaseEditorModal-.*\.js$/.test(asset)))
assert.equal([...requestedAssets].filter(asset => /\/Settings-.*\.js$/.test(asset)).length, 0)
})
await check('create_close_reopen_discards_unsaved_form', async () => {
await nameInput.fill('UNSAVED_BROWSER_CHECK')
await closeEditor()
await create()
assert.equal(await nameInput.inputValue(), '')
})
async function openModelsSettings() {
await editor.locator('[data-guide="kb-editor-nav-models"]').click()
await editor.locator('[data-guide="kb-create-llm"] .model-selector').click()
await page.locator('.add-model-option:visible').click()
await settings.waitFor({ state: 'visible' })
}
await check('lazy_settings_opens_models_from_editor', async () => {
await openModelsSettings()
assert.ok([...requestedAssets].some(asset => /\/Settings-.*\.js$/.test(asset)))
assert.equal(await page.locator('.settings-overlay').count(), 2)
})
await check('settings_escape_closes_and_reopens_without_losing_editor', async () => {
await page.keyboard.press('Escape')
await settings.waitFor({ state: 'hidden' })
assert.ok(await editor.isVisible())
await openModelsSettings()
await settings.locator('.close-btn').click()
await settings.waitFor({ state: 'hidden' })
await closeEditor()
})
await check('existing_knowledge_editor_loads_and_reopens_correct_resource', async () => {
const card = page.locator('.kb-card').filter({ has: page.locator('.more-wrap') }).first()
await card.waitFor({ state: 'visible' })
const expectedName = await card.locator('.card-title').getAttribute('title')
const edit = async () => {
const settingsItem = page.locator('.card-more-popup:visible .popup-menu-item').filter({ has: page.locator('.t-icon-setting') })
if (!await settingsItem.isVisible()) await card.locator('.more-wrap').click()
await settingsItem.click()
await nameInput.waitFor({ state: 'visible' })
const actualName = await nameInput.inputValue()
if (actualName !== expectedName) {
console.log(JSON.stringify({ diagnostic: 'editor_name_mismatch', expected_length: expectedName?.length, actual_length: actualName.length, blank: !actualName, normalized_match: actualName.trim() === expectedName?.trim() }))
}
assert.equal(actualName, expectedName)
}
await edit()
await nameInput.fill('UNSAVED_BROWSER_CHECK')
await closeEditor()
await edit()
await closeEditor()
})
await check('direct_settings_models_route_has_one_initialized_overlay', async () => {
await page.goto(`${origin}/platform/settings?section=models`)
await settings.waitFor({ state: 'visible' })
assert.equal(await page.locator('.settings-overlay').count(), 1)
await page.waitForLoadState('networkidle')
})
await check('no_browser_errors_or_business_mutations', async () => {
assert.equal(pageErrors.length, 0)
assert.equal(unexpectedMutations.length, 0)
})
} finally {
if (browser) await browser.close()
await new Promise((resolve, reject) => server.httpServer.close(error => error ? reject(error) : resolve()))
const report = { checked_at: new Date().toISOString(), origin, api_origin: apiOrigin, checks, passed: checks.length === 9 && checks.every(check => check.passed), requested_assets: [...requestedAssets], page_errors: pageErrors, unexpected_mutations: unexpectedMutations }
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2))
console.log(JSON.stringify({ report: reportPath, passed: report.passed, checks: checks.length }))
}
}
main().catch(error => { console.error(error.name); process.exitCode = 1 })