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
102 lines
6.9 KiB
JavaScript
102 lines
6.9 KiB
JavaScript
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
const { chromium } = require('/Users/macclaw/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright')
|
|
|
|
const root = path.resolve(__dirname, '../..')
|
|
const origin = process.env.VERIFY_FRONTEND_ORIGIN || 'https://agent.pducv.cn'
|
|
const reportName = process.env.VERIFY_REPORT_NAME || 'public-first-entry-loading'
|
|
assert.match(reportName, /^[a-zA-Z0-9_-]+$/)
|
|
const started_at = new Date().toISOString()
|
|
const checks = [], failures = [], timings = {}, requests = []
|
|
let browser
|
|
function check(name, value) {
|
|
checks.push({ name, passed: !!value })
|
|
assert.ok(value, name)
|
|
}
|
|
async function main() {
|
|
const credentials = JSON.parse(fs.readFileSync(path.join(root, 'acceptance/super-admin-login.json'), 'utf8'))
|
|
browser = await chromium.launch({ headless: true, channel: 'chrome' })
|
|
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } })
|
|
await page.addInitScript(() => {
|
|
localStorage.setItem('locale', 'zh-CN')
|
|
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')
|
|
})
|
|
page.on('pageerror', error => failures.push(error.message))
|
|
page.on('request', request => requests.push({ path: new URL(request.url()).pathname, method: request.method() }))
|
|
await page.goto(`${origin}/login`)
|
|
await page.locator('input[autocomplete="username"]').fill(credentials.username)
|
|
await page.locator('input[type="password"]').first().fill(credentials.password)
|
|
const loginStarted = Date.now()
|
|
await page.locator('button[type="submit"]').click()
|
|
await page.waitForURL('**/platform/knowledge-bases', { timeout: 30000 })
|
|
timings.login_to_route_ms = Date.now() - loginStarted
|
|
await page.locator('[data-guide="kb-list-create"]').waitFor({ state: 'visible' })
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 })
|
|
check('cold_form_login_completes_within_30_seconds', timings.login_to_route_ms < 30000)
|
|
check('hidden_settings_module_not_requested_on_entry', !requests.some(item => /\/Settings-[^/]+\.js$/.test(item.path)))
|
|
check('hidden_knowledge_editor_not_requested_on_entry', !requests.some(item => /\/KnowledgeBaseEditorModal-[^/]+\.js$/.test(item.path)))
|
|
check('chat_agent_list_not_prefetched_on_knowledge_entry', !requests.some(item => item.path === '/api/v1/agents'))
|
|
check('chat_search_providers_not_prefetched_on_knowledge_entry', !requests.some(item => item.path === '/api/v1/web-search-providers'))
|
|
|
|
const editorStarted = Date.now()
|
|
await page.locator('[data-guide="kb-list-create"]').click()
|
|
const editor = page.locator('.settings-overlay').filter({ has: page.locator('[data-guide="kb-editor-sidebar"]') })
|
|
await editor.waitFor({ state: 'visible' })
|
|
await editor.locator('.editor-initializing').waitFor({ state: 'hidden' })
|
|
const name = editor.locator('[data-guide="kb-create-name"] input')
|
|
await name.fill('Unsaved loading verification')
|
|
timings.editor_ready_ms = Date.now() - editorStarted
|
|
check('first_open_editor_initializes_and_accepts_input', await name.inputValue() === 'Unsaved loading verification')
|
|
check('editor_module_requested_only_when_opened', requests.some(item => /\/KnowledgeBaseEditorModal-[^/]+\.js$/.test(item.path)))
|
|
await page.locator('.t-message').first().waitFor({ state: 'hidden' })
|
|
await page.locator('.t-notification').first().waitFor({ state: 'hidden' })
|
|
await page.screenshot({ path: path.join(root, `acceptance/${reportName}-editor-desktop.png`) })
|
|
await editor.locator('.close-btn').click()
|
|
await editor.waitFor({ state: 'hidden' })
|
|
await page.locator('[data-guide="kb-list-create"]').click()
|
|
await editor.waitFor({ state: 'visible' })
|
|
await editor.locator('.editor-initializing').waitFor({ state: 'hidden' })
|
|
check('reopened_editor_does_not_reuse_discarded_draft', await name.inputValue() === '')
|
|
for (const width of [390, 320]) {
|
|
await page.setViewportSize({ width, height: 844 })
|
|
await page.screenshot({ path: path.join(root, `acceptance/${reportName}-editor-mobile-${width}.png`) })
|
|
check(`editor_mobile_${width}_no_page_overflow`, await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 2))
|
|
check(`editor_mobile_${width}_form_uses_available_width_without_clipping`, await editor.evaluate(element => {
|
|
const content = element.querySelector('.content-wrapper')
|
|
const input = element.querySelector('[data-guide="kb-create-name"]')
|
|
if (!content || !input) return false
|
|
const bounds = content.getBoundingClientRect(), field = input.getBoundingClientRect()
|
|
return bounds.width >= innerWidth - 40 && content.scrollWidth <= content.clientWidth + 2
|
|
&& field.width >= 250 && field.left >= bounds.left && field.right <= bounds.right
|
|
}))
|
|
check(`editor_mobile_${width}_footer_buttons_visible`, await editor.locator('.settings-footer-actions').evaluate(element => {
|
|
const bounds = element.getBoundingClientRect()
|
|
return bounds.left >= 0 && bounds.right <= innerWidth && bounds.bottom <= innerHeight && bounds.top > 0
|
|
}))
|
|
}
|
|
await editor.locator('.close-btn').click()
|
|
await editor.waitFor({ state: 'hidden' })
|
|
await page.setViewportSize({ width: 1440, height: 1000 })
|
|
await page.locator('[data-guide="user-menu"]').click()
|
|
await page.locator('.menu-item').filter({ hasText: '全部设置' }).click()
|
|
await page.waitForURL('**/platform/settings**')
|
|
await page.locator('.settings-overlay').waitFor({ state: 'visible' })
|
|
check('settings_menu_opens_one_settings_dialog', await page.locator('.settings-overlay').count() === 1)
|
|
check('settings_module_requested_when_opened', requests.some(item => /\/Settings-[^/]+\.js$/.test(item.path)))
|
|
const chatRequestStart = requests.length
|
|
await page.goto(`${origin}/platform/creatChat`)
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 })
|
|
check('chat_route_still_loads_its_agents_and_models', ['/api/v1/agents', '/api/v1/models'].every(path => requests.slice(chatRequestStart).some(item => item.path === path)))
|
|
check('chat_route_loads_without_redirect', new URL(page.url()).pathname === '/platform/creatChat')
|
|
check('no_business_resource_writes', !requests.some(item => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(item.method) && item.path.startsWith('/api/') && !item.path.startsWith('/api/v1/auth/')))
|
|
check('no_page_errors', failures.length === 0)
|
|
}
|
|
main().catch(error => { failures.push(error.message) }).finally(async () => {
|
|
if (browser) await browser.close()
|
|
const report = { started_at, checked_at: new Date().toISOString(), origin, checks, timings, failures, passed: checks.length === 20 && checks.every(item => item.passed) && failures.length === 0 }
|
|
fs.writeFileSync(path.join(root, `acceptance/${reportName}.json`), JSON.stringify(report, null, 2))
|
|
console.log(JSON.stringify(report))
|
|
if (!report.passed) process.exitCode = 1
|
|
})
|