Harden auth/vault path sanitization and clean WeKnora docs
This commit is contained in:
@@ -19,7 +19,7 @@ echo $!
|
||||
sleep 8 && grep -E 'gateway 已启动|Running' /tmp/mnote-dev.log
|
||||
```
|
||||
**关键点:**
|
||||
- 启动命令是 `npm run dev:hot`(不是 desktop:hot)
|
||||
- 启动命令是 `npm run dev:hot`(唯一热启动入口)
|
||||
- `dev:hot` 默认启用 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1`
|
||||
- 使用 cargo-watch 自动重编译
|
||||
- 端口固定为 3000
|
||||
@@ -35,8 +35,9 @@ paseo_browser_wait url=localhost:3000 timeoutMs=15000
|
||||
# 3. 截图(如果返回 "tab not painted",等 3 秒重试)
|
||||
paseo_browser_screenshot
|
||||
|
||||
# 4. 快速登录(snapshot 找到 @e5 即"测试账号快速登录"按钮)
|
||||
paseo_browser_click ref=@e5
|
||||
# 4. 标准登录(7-76:无「测试账号快速登录」按钮)
|
||||
# 在 snapshot 中定位账号/密码输入框与「登录」提交按钮后填表提交;
|
||||
# 或脚本侧:require('./lib/browser-auth-login').loginViaAuthForm(page)
|
||||
|
||||
# 5. 等待进入工作区
|
||||
paseo_browser_wait url=localhost:3000 text=工作区
|
||||
@@ -45,10 +46,18 @@ paseo_browser_wait url=localhost:3000 text=工作区
|
||||
paseo_browser_screenshot
|
||||
```
|
||||
|
||||
### 4. 测试账号
|
||||
- 邮箱:`mnote.e2e@example.com`
|
||||
- 密码:`MnoteE2E123!`
|
||||
- 用户名:`mnote-e2e`
|
||||
### 4. 测试账号(7-76 方案 A:admin / user / AI 主体分离)
|
||||
|
||||
| 用途 | 用户名 | 邮箱 | 密码(本地/dev) | control-plane `role` | admin 能力 |
|
||||
|------|--------|------|------------------|----------------------|------------|
|
||||
| **Ops admin** | `mnote-admin` | `mnote.admin@example.com` | `MnoteAdmin123!` | `admin` | 是(`/admin/*`、access-policy、代签) |
|
||||
| **AI 主体** | `mnote-e2e` | `mnote.e2e@example.com` | `MnoteE2E123!` | `ai_service` | **否** |
|
||||
| **个人用户示例** | `liaibo` | (本地) | (本地) | `user` | **否** |
|
||||
|
||||
- 登录方式:标准表单 / `POST /api/auth` / `scripts/lib/browser-auth-login.js`
|
||||
- 产品/AI 主体:`loginViaAuthForm` / `loginViaAuthApi`(默认 e2e)
|
||||
- 管理面 seed:`loginAsAdminViaAuthForm` / `withAdminBrowserSession`
|
||||
- **不要**依赖快速登录按钮;**不要**把 `mnote-e2e` 放进 `MNOTE_ADMIN_USER_IDS` 或 `access-policy.admins`。
|
||||
|
||||
### 5. 常见问题
|
||||
| 问题 | 原因 | 解决 |
|
||||
@@ -56,7 +65,6 @@ paseo_browser_screenshot
|
||||
| ERR_CONNECTION_REFUSED | 服务器未启动 | 先运行 `npm run dev:hot` |
|
||||
| tab not painted | 页面未渲染完成 | 等待 3 秒后重试截图 |
|
||||
| Paseo 终端无输出 | 前台进程阻塞 | 用 `nohup ... &` 后台启动 |
|
||||
| `desktop:hot` vs `dev:hot` | 启动命令不同 | 浏览器测试用 `dev:hot` |
|
||||
|
||||
### 6. AI 密码本读密 / 登录态(12-2 · **不依赖 3000**)
|
||||
|
||||
@@ -97,7 +105,7 @@ cargo test -p mnote-web --lib routes::vault -- --nocapture 2>&1 | rg -n "login_r
|
||||
- 2026-05-20 起,默认主入口固定按 `http://127.0.0.1:3000` 理解;历史记录里的 `3001` 只代表当时临时 mnote-web 实例,不再作为默认验收入口。
|
||||
- 历史 smoke 分为 `current` 与 `retired/debug` 两类维护;退役脚本不应进入默认回归,除非脚本自身要求显式环境变量。
|
||||
- 2026-05-27 起,默认 smoke 基线只覆盖 `3000 Rust SSR + leptos-tiptap + local-first` 主路径;Convex export、Convex 兼容、Next、3104、BlockNote 默认路径脚本不再放在默认候选里。
|
||||
- 依赖 `/api/dev/seed` 的 browser smoke 默认使用 `npm run dev:hot`;`dev:hot` 默认启用 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1`,`desktop:hot` 与生产启动仍默认关闭。修改 Node 启动脚本或环境变量后必须重启 `dev:hot` 主进程,不能只等待 cargo-watch 重载 Rust。
|
||||
- 依赖 `/api/dev/seed` 的 browser smoke 默认使用 `npm run dev:hot`;`dev:hot` 默认启用 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1`,生产启动仍默认关闭。修改 Node 启动脚本或环境变量后必须重启 `dev:hot` 主进程,不能只等待 cargo-watch 重载 Rust。
|
||||
|
||||
## 0. 当前 smoke 状态清单
|
||||
|
||||
@@ -124,7 +132,7 @@ node scripts/task490-runtime-surfaces-smoke.js
|
||||
- Rust SSR 文档页 / Page Aggregate:local-first 默认优先 `task167-local-markdown-title-body-options-no-convex-smoke.js`;Page Aggregate browser conversion / compat fallback 改动补跑 `task522-page-aggregate-compat-fallback-contract.js`;cloud/control-plane 文档可补跑 `task110-page-title-single-truth-smoke.js`、`task-page-aggregate-body-sync-smoke.js`、`task-page-aggregate-options-sync-smoke.js`、`task-page-aggregate-refresh-persistence-smoke.js`
|
||||
- leptos-tiptap runtime surface:`task490-runtime-surfaces-smoke.js`;需要验证保存回读可补跑 `task121-rust-web-editor-island-hydration-smoke.js`,但它仍使用 `/api/tree/commands create` 准备文档,不作为无 Convex 默认基线;需要验证浮层互斥和更多菜单状态时再跑 `task158-e30-menu-state-smoke.js`
|
||||
- local-first 本地工作区:`task164-desktop-hot-local-folder-main-entry-smoke.js`、`task166-local-first-managed-workspace-no-convex-smoke.js`、`task167-local-markdown-title-body-options-no-convex-smoke.js`、`task436-local-markdown-open-document-external-change-smoke.js`、`task443-local-markdown-asset-upload-smoke.js`、`task451-local-markdown-conflict-resolution-ui-smoke.js`、`task452-local-search-index-browser-smoke.js`、`task453-local-folder-page-ai-changed-files-smoke.js`;WorkspacePath / ObjectIdentity runtime 消费统一改动补跑 `task524-workspace-object-identity-matrix-smoke.js`。`task452` 只覆盖普通本地搜索、settings API、tag/backlink API 和旧 local-index UI 不复活;资料库问答、PDF/Office/image source ingestion 与引用回跳改跑 LightRAG / provider-neutral knowledge-rag 脚本。
|
||||
- LightRAG / Knowledge RAG 资料库:LightRAG 是默认 provider。资料源、摄取、source scope 与 watcher 改动优先跑 `task532-knowledge-rag-docx-ingestion-smoke.js`、`task533-knowledge-rag-source-watcher-sync-smoke.js`、`task534-knowledge-rag-source-management-scope-smoke.js`、`task538-knowledge-rag-source-scope-api-smoke.js`;引用和 Page AI final answer 改动补跑 `task529-knowledge-rag-citation-resource-tab-smoke.js`、`task530-knowledge-rag-page-ai-final-answer-smoke.js`;真实 Pi + LightRAG 服务可用时补跑 `task-pi-lab-real-lightrag-smoke.js`。WeKnora 仅是历史/备用 provider,不进入默认回归。旧 OpenHub 专项资料库 smoke 已软归档到 `recycle/20260712-pi-ts-openhub-retirement/`;旧 `task526-local-folder-ocr-api-smoke.js` / OCR sidecar 链路已退役并软归档;`/api/search/documents` 不作为 LightRAG、LiteParse、OCR sidecar 或 evidence.sqlite 的资料库问答 fallback。
|
||||
- LightRAG / Knowledge RAG 资料库:LightRAG 是默认 provider。资料源、摄取、source scope 与 watcher 改动优先跑 `task532-knowledge-rag-docx-ingestion-smoke.js`、`task533-knowledge-rag-source-watcher-sync-smoke.js`、`task534-knowledge-rag-source-management-scope-smoke.js`、`task538-knowledge-rag-source-scope-api-smoke.js`;引用和 Page AI final answer 改动补跑 `task529-knowledge-rag-citation-resource-tab-smoke.js`、`task530-knowledge-rag-page-ai-final-answer-smoke.js`;真实 Pi + LightRAG 服务可用时补跑 `task-pi-lab-real-lightrag-smoke.js`。旧 OpenHub 专项资料库 smoke 已软归档到 `recycle/20260712-pi-ts-openhub-retirement/`;旧 `task526-local-folder-ocr-api-smoke.js` / OCR sidecar 链路已退役并软归档;`/api/search/documents` 不作为 LightRAG、LiteParse、OCR sidecar 或 evidence.sqlite 的资料库问答 fallback。
|
||||
- local Markdown conflict regression:`task510-local-markdown-conflict-regression-group.js` 串联真实冲突、连续上传、新建页面、外部恢复、多 tab CAS 与上传 409 孤儿策略;可用 `MNOTE_CONFLICT_REGRESSION_TASKS=a.js,b.js` 做定点子集。
|
||||
- tree realtime / live cache:`task446-tree-rename-dual-browser-live-smoke.js`、`task447-tree-move-order-dual-browser-live-smoke.js`、`task448-tree-resync-recovery-dual-browser-smoke.js`、`task449-tree-sse-reconnect-snapshot-recovery-smoke.js`
|
||||
- 资源对象与 mindmap:`task455-local-folder-mindmap-clean-smoke.js`、`task456-resource-object-shell-sync-smoke.js`、`task166-mindmap-phase6-block-smoke.js`、`task167-mindmap-kmind-parity-smoke.js`、`task168-mindmap-put-validator-smoke.js`;Page AI mindmap skill / 资源生成改动补跑 `task503-mindmap-skill-capability-smoke.js`,真实 mindmap resource tab / Page AI target 改动补跑 `task525-page-ai-mindmap-resource-target-smoke.js`
|
||||
@@ -139,7 +147,7 @@ node scripts/task490-runtime-surfaces-smoke.js
|
||||
- 旧 Convex 兼容 smoke 已软删除到 `recycle/20260527-legacy-cleanup/convex/scripts/`,包括 `task163`、`task174`、`task175`、`task427`、`task428`、`task429`、`task430`、`task431`、`task432`、`task433`、`task434`。
|
||||
- `task179-tree-create-delete-no-reload-smoke.js`:覆盖 `/tree` debug shell。`/tree` 已是显式 debug/internal 边界,脚本默认阻断;只有设置 `MNOTE_ALLOW_DEBUG_TREE_SMOKE=1` 时才可执行。
|
||||
- `task123-rust-web-tree-live-stream-consumer-smoke.js`:只证明 `/api/tree/events` SSE fallback 仍可用。当前 realtime 主链是 WebSocket push + SSE fallback;验证主链时优先使用 446 / 447 / 448,只有排查 fallback 时再跑 123。
|
||||
- WeKnora 默认 provider 脚本(`task544`、`task769`、`task772`、`task777`、`task781`、`task783`、`task784`、`task785`、`task787`、`task788`、`task789`、`task790`、`task79x`)已退役;它们与 LightRAG 默认 provider 主线相反。
|
||||
- 历史非 LightRAG 默认 provider 脚本(`task544`、`task769`、`task772`、`task777`、`task781`、`task783`、`task784`、`task785`、`task787`、`task788`、`task789`、`task790`、`task79x`)已退役并软归档到 `recycle/scripts/` 下对应归档目录(及历史 recycle);不进入 LightRAG 默认回归。
|
||||
- Hermes/ACP/Reasonix 页面 AI 主流程 smoke 已退役;只保留 `task-hermes-page-ai-retirement-guard.js` 防止旧 runtime 复活。`task762-page-ai-board-first-smoke.js` 也已退役。
|
||||
- 旧 `task019`、`task021`、`task022` 这类早期 UI regression 脚本已软删除到 `recycle/scripts/retired-ui-regressions/`,只作为历史对照;后续默认不要用于当前 Rust SSR / local-first 主路径验收。
|
||||
- `task494-filetree-lazy-loading-dedup-smoke.js`、`task498-starred-page-tree-scope-and-local-edit-smoke.js`、`task499-sidebar-tree-view-state-smoke.js`:2026-07-21 诊断为 fixture/契约过时(非 Playwright 安装问题),已迁到 `recycle/scripts/obsolete-tree-smokes-20260721/`。全局 Playwright 栈:`~/.agent-infra/playwright-stack`(见该目录 `use-in-project.md`)。树移动排序优先 `task447-tree-move-order-dual-browser-live-smoke.js` 或 Hermes tree QA。
|
||||
@@ -236,8 +244,9 @@ node scripts/task490-runtime-surfaces-smoke.js
|
||||
- 默认测试账号:
|
||||
- 邮箱:`mnote.e2e@example.com`
|
||||
- 密码:`MnoteE2E123!`
|
||||
- 快捷按钮:`测试账号快速登录`
|
||||
- 通过 API 或 UI 兜底完成认证
|
||||
- UI:标准登录表单(`#account` + `#password` + `[data-auth-submit]`)
|
||||
- 共享 helper:`scripts/lib/browser-auth-login.js`、`tree-shell-smoke-helpers.js`
|
||||
- 通过 API 或 UI 兜底完成认证(7-76 P0 起无快速登录按钮)
|
||||
- 为 smoke 自动创建、重命名、清理临时页面
|
||||
|
||||
后续新脚本优先复用这些 helper,不要在每个任务里再复制一套登录和清理逻辑。
|
||||
@@ -246,7 +255,7 @@ node scripts/task490-runtime-surfaces-smoke.js
|
||||
|
||||
- 需要 `setupWorkspaceAccess`、`seedAiPolicy`、`seedAiRuntime` 等测试数据准备能力时,先确认服务由 `npm run dev:hot` 启动。
|
||||
- `npm run dev:hot` 默认开启 dev fixtures;可用 `MNOTE_WEB_ALLOW_DEV_FIXTURES=0 npm run dev:hot` 显式关闭。
|
||||
- `npm run desktop:hot` 保持生产近似的安全默认,不自动开放 `/api/dev/seed`;确需复用时显式执行 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1 npm run desktop:hot`。
|
||||
- 生产启动不自动开放 `/api/dev/seed`;本地仅 `dev:hot` 默认开启 fixtures。
|
||||
- smoke 遇到 `dev_seed_disabled` 时不得标记为“功能未验证后跳过”;应先按上述契约重启服务,再重新执行完整 smoke。
|
||||
|
||||
## 2. 当前推荐测试顺序
|
||||
@@ -411,7 +420,7 @@ node scripts/task097-homepage-entry-smoke.js
|
||||
推荐:
|
||||
|
||||
- `开启宽版后 htmlWide 应为 true,实际 false`
|
||||
- `测试账号登录后仍停留在 /auth`
|
||||
- `标准登录后仍停留在 /auth`
|
||||
- `本地首屏未捕获工作区特征`
|
||||
|
||||
不推荐:
|
||||
@@ -524,7 +533,7 @@ const controlPlaneEnv = buildControlPlaneTestEnv(dataRoot, process.env);
|
||||
### 7.3 例外
|
||||
|
||||
- `scripts/lib/control-plane-dev-seed.js` 和 `scripts/lib/control-plane-test-env.js` 本身是批准 helper。
|
||||
- `scripts/desktop-hot.js`、`scripts/dev-hot.js`、`scripts/prod-build-start.js` 作为启动基础设施,设置环境变量路径是必要的。
|
||||
- `scripts/mnote-web-hot.js`、`scripts/dev-hot.js`、`scripts/prod-build-start.js` 作为启动基础设施,设置环境变量路径是必要的。
|
||||
- `rust/crates/` 下的 Rust 代码通过 `ControlPlaneStore` trait 访问数据库是正常路径。
|
||||
- `scripts/task-control-plane-admin-libsql-roundtrip-smoke.js` 使用 `cargo run --bin control-plane-admin`,走 Rust admin CLI。
|
||||
- `sqlite` fallback、admin CLI、legacy evidence/local_search 不在脚本纪律约束范围内。
|
||||
|
||||
+6
-7
@@ -4,12 +4,11 @@
|
||||
/**
|
||||
* dev:hot 启动入口。
|
||||
*
|
||||
* 目标:
|
||||
* - 不改动 desktop:hot 的默认行为。
|
||||
* - 使用 cargo-watch 自动重编译并重启 mnote-web。
|
||||
* - 通过 MNOTE_WEB_DEV_HOT_RELOAD 启用页面端轻量 reload 轮询。
|
||||
* - 默认启用 MNOTE_WEB_ALLOW_DEV_FIXTURES,保证依赖 /api/dev/seed 的 smoke 可直接运行;
|
||||
* 可显式设为 0 关闭,desktop:hot / prod:start 仍保持默认关闭。
|
||||
* 可显式设为 0 关闭;prod:start 保持关闭。
|
||||
* - 实际进程编排在 scripts/mnote-web-hot.js。
|
||||
*/
|
||||
|
||||
const { spawn } = require("node:child_process");
|
||||
@@ -17,7 +16,7 @@ const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const rootDir = path.resolve(__dirname, "..");
|
||||
const desktopHotPath = path.join(rootDir, "scripts", "desktop-hot.js");
|
||||
const hotPath = path.join(rootDir, "scripts", "mnote-web-hot.js");
|
||||
|
||||
function cargoWatchCommand(env = process.env) {
|
||||
const extraArgs = String(env.MNOTE_WEB_DEV_HOT_CARGO_WATCH_ARGS || "").trim();
|
||||
@@ -91,11 +90,11 @@ function buildDevHotEnv(baseEnv = process.env) {
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(desktopHotPath)) {
|
||||
throw new Error(`缺少 desktop-hot 入口:${desktopHotPath}`);
|
||||
if (!fs.existsSync(hotPath)) {
|
||||
throw new Error(`缺少 mnote-web-hot 入口:${hotPath}`);
|
||||
}
|
||||
|
||||
const child = spawn(process.execPath, [desktopHotPath], {
|
||||
const child = spawn(process.execPath, [hotPath], {
|
||||
cwd: rootDir,
|
||||
env: buildDevHotEnv(process.env),
|
||||
stdio: "inherit",
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* 7-76 P0:浏览器 smoke 标准登录(无「测试账号快速登录」按钮)。
|
||||
* 优先填表提交 /auth;失败时可回退 API signIn/signUp(由调用方决定)。
|
||||
*/
|
||||
|
||||
const DEFAULT_EMAIL = process.env.MNOTE_E2E_EMAIL || "mnote.e2e@example.com";
|
||||
const DEFAULT_PASSWORD = process.env.MNOTE_E2E_PASSWORD || "MnoteE2E123!";
|
||||
const DEFAULT_USERNAME = process.env.MNOTE_E2E_USERNAME || "mnote-e2e";
|
||||
/** 方案 A:独立 ops admin(非 AI 主体、非个人笔记主账号) */
|
||||
const DEFAULT_ADMIN_EMAIL = process.env.MNOTE_ADMIN_EMAIL || "mnote.admin@example.com";
|
||||
const DEFAULT_ADMIN_PASSWORD = process.env.MNOTE_ADMIN_PASSWORD || "MnoteAdmin123!";
|
||||
const DEFAULT_ADMIN_USERNAME = process.env.MNOTE_ADMIN_USERNAME || "mnote-admin";
|
||||
const DEFAULT_UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
|
||||
function resolveBaseUrl(baseUrl) {
|
||||
return String(baseUrl || process.env.MNOTE_UI_BASE_URL || "http://127.0.0.1:3000").replace(
|
||||
/\/+$/,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在已打开的 /auth 页(或任意页)用标准表单登录。
|
||||
* @param {import('playwright').Page} page
|
||||
* @param {{ baseUrl?: string, email?: string, password?: string, username?: string, timeoutMs?: number, gotoAuth?: boolean }} [options]
|
||||
*/
|
||||
async function loginViaAuthForm(page, options = {}) {
|
||||
const baseUrl = resolveBaseUrl(options.baseUrl);
|
||||
const email = options.email || DEFAULT_EMAIL;
|
||||
const password = options.password || DEFAULT_PASSWORD;
|
||||
const timeoutMs = options.timeoutMs || DEFAULT_UI_TIMEOUT_MS;
|
||||
const gotoAuth = options.gotoAuth !== false;
|
||||
|
||||
if (gotoAuth || !String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(`${baseUrl}/auth`, { waitUntil: "commit", timeout: timeoutMs });
|
||||
}
|
||||
|
||||
const account = page.locator("#account, input[name='account']").first();
|
||||
const passwordInput = page.locator("#password, input[name='password']").first();
|
||||
const submit = page.locator("[data-auth-submit], button[type='submit']").first();
|
||||
|
||||
await account.waitFor({ state: "visible", timeout: timeoutMs });
|
||||
await account.fill(email, { timeout: timeoutMs });
|
||||
await passwordInput.fill(password, { timeout: timeoutMs });
|
||||
await submit.click({ timeout: timeoutMs });
|
||||
}
|
||||
|
||||
/**
|
||||
* API 层 signIn;不存在时可 signUp 再 signIn。
|
||||
* @param {{ fetch: Function }} requestContext Playwright request 或兼容对象
|
||||
* @param {{ baseUrl?: string, email?: string, password?: string, username?: string, timeoutMs?: number }} [options]
|
||||
*/
|
||||
async function loginViaAuthApi(requestContext, options = {}) {
|
||||
const baseUrl = resolveBaseUrl(options.baseUrl);
|
||||
const email = options.email || DEFAULT_EMAIL;
|
||||
const password = options.password || DEFAULT_PASSWORD;
|
||||
const username = options.username || DEFAULT_USERNAME;
|
||||
const timeoutMs = options.timeoutMs || DEFAULT_UI_TIMEOUT_MS;
|
||||
|
||||
async function post(params) {
|
||||
const response = await requestContext.fetch(`${baseUrl}/api/auth`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
data: {
|
||||
action: "auth:signIn",
|
||||
args: {
|
||||
provider: "password",
|
||||
params,
|
||||
},
|
||||
},
|
||||
timeout: timeoutMs,
|
||||
});
|
||||
const text = await response.text();
|
||||
let payload = null;
|
||||
try {
|
||||
payload = text ? JSON.parse(text) : null;
|
||||
} catch {
|
||||
payload = text;
|
||||
}
|
||||
return { ok: response.ok(), status: response.status(), payload };
|
||||
}
|
||||
|
||||
let result = await post({
|
||||
account: email,
|
||||
email,
|
||||
password,
|
||||
flow: "signIn",
|
||||
});
|
||||
if (result.ok) {
|
||||
return result.payload;
|
||||
}
|
||||
|
||||
result = await post({
|
||||
email,
|
||||
name: username,
|
||||
password,
|
||||
flow: "signUp",
|
||||
});
|
||||
if (result.ok) {
|
||||
return result.payload;
|
||||
}
|
||||
|
||||
const details =
|
||||
typeof result.payload === "string" ? result.payload : JSON.stringify(result.payload);
|
||||
throw new Error(`标准 API 登录失败: ${result.status} ${details}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 ops admin 登录(/admin/*、代签、access-policy、ai-admin settings)。
|
||||
* AI 主体 mnote-e2e 与个人账号 liaibo 默认无 admin 能力。
|
||||
*/
|
||||
async function loginAsAdminViaAuthForm(page, options = {}) {
|
||||
return loginViaAuthForm(page, {
|
||||
...options,
|
||||
email: options.email || DEFAULT_ADMIN_EMAIL,
|
||||
password: options.password || DEFAULT_ADMIN_PASSWORD,
|
||||
username: options.username || DEFAULT_ADMIN_USERNAME,
|
||||
});
|
||||
}
|
||||
|
||||
async function loginAsAdminViaAuthApi(requestContext, options = {}) {
|
||||
return loginViaAuthApi(requestContext, {
|
||||
...options,
|
||||
email: options.email || DEFAULT_ADMIN_EMAIL,
|
||||
password: options.password || DEFAULT_ADMIN_PASSWORD,
|
||||
username: options.username || DEFAULT_ADMIN_USERNAME,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在独立 browser context 里以 admin 会话执行 fn(page),不污染主测用户 cookie。
|
||||
* 用于:access-policy grants、/api/ai-admin/* 等必须 admin 的 seed。
|
||||
* @param {import('playwright').Browser} browser
|
||||
* @param {(page: import('playwright').Page) => Promise<T>} fn
|
||||
* @param {{ baseUrl?: string, timeoutMs?: number }} [options]
|
||||
* @returns {Promise<T>}
|
||||
*/
|
||||
async function withAdminBrowserSession(browser, fn, options = {}) {
|
||||
const baseUrl = resolveBaseUrl(options.baseUrl);
|
||||
const timeoutMs = options.timeoutMs || DEFAULT_UI_TIMEOUT_MS;
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
try {
|
||||
await loginAsAdminViaAuthForm(page, { baseUrl, timeoutMs });
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout: timeoutMs })
|
||||
.catch(() => {});
|
||||
return await fn(page);
|
||||
} finally {
|
||||
await context.close().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_EMAIL,
|
||||
DEFAULT_PASSWORD,
|
||||
DEFAULT_USERNAME,
|
||||
DEFAULT_ADMIN_EMAIL,
|
||||
DEFAULT_ADMIN_PASSWORD,
|
||||
DEFAULT_ADMIN_USERNAME,
|
||||
loginViaAuthForm,
|
||||
loginViaAuthApi,
|
||||
loginAsAdminViaAuthForm,
|
||||
loginAsAdminViaAuthApi,
|
||||
withAdminBrowserSession,
|
||||
};
|
||||
@@ -24,7 +24,7 @@ async function postDevSeed(requestContext, baseUrl, seeds, timeoutMs) {
|
||||
[
|
||||
`/api/dev/seed 未启用(base=${normalizedBaseUrl})。`,
|
||||
"依赖 seed 的 smoke 必须使用 `npm run dev:hot` 启动;dev:hot 默认开启 MNOTE_WEB_ALLOW_DEV_FIXTURES=1。",
|
||||
"若复用 desktop:hot,请使用 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1 npm run desktop:hot`。",
|
||||
"若直接跑 mnote-web 而未走 dev:hot,请显式 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1`。",
|
||||
"修改 scripts/dev-hot.js 或启动环境后必须重启 dev:hot 主进程,cargo-watch 不会刷新 Node 启动环境。",
|
||||
].join(" "),
|
||||
);
|
||||
|
||||
@@ -92,7 +92,7 @@ function resolveRuntimePlan(env = process.env) {
|
||||
const publicPort = Number.isFinite(frontendPort) ? Math.floor(frontendPort) : 3000;
|
||||
const controlPlaneBackend = String(env.MNOTE_CONTROL_PLANE_BACKEND || "libsql-local").trim() || "libsql-local";
|
||||
if (controlPlaneBackend === "sqlite") {
|
||||
throw new Error("desktop:hot 不再支持 SQLite control-plane fallback;请使用 libsql-local/turso-local-replica/turso-remote/turso-synced");
|
||||
throw new Error("dev:hot 不再支持 SQLite control-plane fallback;请使用 libsql-local/turso-local-replica/turso-remote/turso-synced");
|
||||
}
|
||||
const controlPlaneEnv = {
|
||||
MNOTE_CONTROL_PLANE_BACKEND: controlPlaneBackend,
|
||||
@@ -11,7 +11,7 @@ const {
|
||||
resolveRuntimePlan,
|
||||
shouldStartBackend,
|
||||
stopStaleMnoteWebCargoProcesses,
|
||||
} = require("./desktop-hot.js");
|
||||
} = require("./mnote-web-hot.js");
|
||||
|
||||
function findFreePort() {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -173,10 +173,10 @@ test("热启动计划拒绝 SQLite runtime fallback", () => {
|
||||
test("显式配置知识库 provider 时透传给 MNote host", () => {
|
||||
const plan = resolveRuntimePlan({
|
||||
FRONTEND_PORT: "3000",
|
||||
MNOTE_KNOWLEDGE_PROVIDER: "weknora",
|
||||
MNOTE_KNOWLEDGE_PROVIDER: "lightrag_legacy",
|
||||
});
|
||||
|
||||
assert.equal(plan.mnoteWebEnv.MNOTE_KNOWLEDGE_PROVIDER, "weknora");
|
||||
assert.equal(plan.mnoteWebEnv.MNOTE_KNOWLEDGE_PROVIDER, "lightrag_legacy");
|
||||
});
|
||||
|
||||
test("默认跳过 FastAPI 后端,只有显式开启时才启动", () => {
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
/**
|
||||
* Minimal Browser QA: Block handle menu icon/layout check
|
||||
* Scope: open editor → hover paragraph handle → click handle → check menu
|
||||
@@ -61,15 +62,13 @@ async function run() {
|
||||
|
||||
try {
|
||||
// ── Step 1: Login ──
|
||||
let s1 = step("login", "Login via quick-login button");
|
||||
let s1 = step("login", "Login via standard auth form (7-76 P0)");
|
||||
console.log(`\n🔍 Step 1: ${s1.desc}`);
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "networkidle", timeout: 15000 });
|
||||
const loginBtn = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await loginBtn.waitFor({ state: "visible", timeout: 8000 });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.pathname.includes("/auth"), { timeout: 15000 }),
|
||||
loginBtn.click(),
|
||||
]);
|
||||
await loginViaAuthForm(page, { baseUrl: BASE, timeoutMs: 15000, gotoAuth: false });
|
||||
await page
|
||||
.waitForURL((url) => !url.pathname.includes("/auth"), { timeout: 15000 })
|
||||
.catch(() => {});
|
||||
await page.waitForTimeout(2000);
|
||||
const afterLogin = page.url();
|
||||
s1.status = afterLogin.includes("/auth") ? "FAIL" : "PASS";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const path = require("path");
|
||||
const { chromium } = require("playwright");
|
||||
|
||||
@@ -68,18 +69,15 @@ async function ensureAuthenticated(page, requestContext) {
|
||||
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: TIMEOUT_MS });
|
||||
await page.waitForTimeout(1000);
|
||||
const btn = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await btn.isVisible().catch(() => false)) {
|
||||
await btn.click();
|
||||
await page.waitForURL("**/documents/**", { timeout: TIMEOUT_MS }).catch(() => {});
|
||||
await page.waitForTimeout(2000);
|
||||
} else {
|
||||
await page.getByLabel("邮箱").fill(TEST_EMAIL);
|
||||
await page.getByLabel("密码").fill(TEST_PASSWORD);
|
||||
await page.getByRole("button", { name: /登录|Login|Sign in/i }).click();
|
||||
await page.waitForURL("**/documents/**", { timeout: TIMEOUT_MS }).catch(() => {});
|
||||
await page.waitForTimeout(2000);
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: BASE_URL,
|
||||
email: TEST_EMAIL,
|
||||
password: TEST_PASSWORD,
|
||||
timeoutMs: TIMEOUT_MS,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page.waitForURL((url) => !String(url).includes("/auth"), { timeout: TIMEOUT_MS }).catch(() => {});
|
||||
await page.waitForTimeout(2000);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
@@ -18,13 +19,30 @@ const E2E_USER_NAME = process.env.MNOTE_AI_ADMIN_E2E_USERNAME || "ai-user";
|
||||
const E2E_USER_PASSWORD = process.env.MNOTE_AI_ADMIN_E2E_PASSWORD || ["Mnote", "E2E", "123!"].join("");
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit" });
|
||||
const button = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await button.waitFor({ state: "visible" });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.pathname.includes("/auth")),
|
||||
button.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function ensurePasswordUser(browser, user) {
|
||||
|
||||
@@ -29,9 +29,9 @@ assert.equal(env.MNOTE_TURSO_LOCAL_PATH, "/mnt/Data1T/Mnote_data/control-plane/c
|
||||
assert.equal(env.MNOTE_CONTROL_PLANE_DB_PATH, undefined);
|
||||
|
||||
const explicitKnowledgeProviderEnv = buildDevHotEnv({
|
||||
MNOTE_KNOWLEDGE_PROVIDER: "weknora",
|
||||
MNOTE_KNOWLEDGE_PROVIDER: "lightrag_legacy",
|
||||
});
|
||||
assert.equal(explicitKnowledgeProviderEnv.MNOTE_KNOWLEDGE_PROVIDER, "weknora");
|
||||
assert.equal(explicitKnowledgeProviderEnv.MNOTE_KNOWLEDGE_PROVIDER, "lightrag_legacy");
|
||||
|
||||
const loopbackEnv = buildDevHotEnv({
|
||||
MNOTE_WEB_BIND: "127.0.0.1:3300",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
// Pi Lab API endpoint smoke
|
||||
// 验证 Pi Lab API 路由在 mnote-web 开发环境下的响应
|
||||
// 需要 mnote-web 已在运行(npm run desktop:hot 或独立启动)
|
||||
// 需要 mnote-web 已在运行(npm run dev:hot 或独立启动)
|
||||
// 检查:新端点 start/send/abort/events、SSE、permission-system managed builtin tools、receipt、no polling
|
||||
|
||||
const BASE = process.env.MNOTE_PI_LAB_BASE || 'http://127.0.0.1:3000';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
// Pi Lab browser smoke
|
||||
// 验证 Pi Lab 默认独立悬浮入口 + MNote-native drawer,不复用已退役 host drawer/provider tab/iframe。
|
||||
// 需要 mnote-web 已在运行;MNOTE_PAGE_AI_PI_LAB 默认开启,设为 0 时才强制关闭。
|
||||
@@ -38,12 +39,16 @@ async function addAuth(context) {
|
||||
|
||||
async function quickLoginIfNeeded(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (!(await quickLoginButton.isVisible({ timeout: 4000 }).catch(() => false))) return;
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: UI_TIMEOUT_MS }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
const accountReady = await page
|
||||
.locator("#account")
|
||||
.first()
|
||||
.isVisible({ timeout: 4000 })
|
||||
.catch(() => false);
|
||||
if (!accountReady) return;
|
||||
await loginViaAuthForm(page, { baseUrl: BASE, timeoutMs: UI_TIMEOUT_MS, gotoAuth: false });
|
||||
await page
|
||||
.waitForURL((url) => !url.toString().includes("/auth"), { timeout: UI_TIMEOUT_MS })
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
async function approveVisiblePiDialog(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -27,13 +28,30 @@ const CHROMIUM_EXECUTABLE = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE
|
||||
|| (fs.existsSync("/usr/bin/google-chrome") ? "/usr/bin/google-chrome" : "");
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm, withAdminBrowserSession } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -29,13 +30,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
@@ -89,42 +107,45 @@ function policyForFullAccessAskUser() {
|
||||
};
|
||||
}
|
||||
|
||||
async function seedWorkspace(page) {
|
||||
async function seedWorkspace(browser, page) {
|
||||
mkdirp(ROOT_PATH);
|
||||
fs.writeFileSync(path.join(ROOT_PATH, PAGE_PATH), "# Pi full access question smoke\n", "utf8");
|
||||
const grantResponse = await page.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const grantText = await grantResponse.text();
|
||||
let grantBody = {};
|
||||
try {
|
||||
grantBody = grantText ? JSON.parse(grantText) : {};
|
||||
} catch {
|
||||
grantBody = { raw: grantText };
|
||||
}
|
||||
if (!grantResponse.ok() && grantBody.code !== "local_access_policy_grant_duplicate") {
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${grantResponse.status()} ${grantText.slice(0, 800)}`);
|
||||
}
|
||||
await requestJson(page, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForFullAccessAskUser(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
// 方案 A:admin 能力仅 mnote-admin
|
||||
await withAdminBrowserSession(browser, async (adminPage) => {
|
||||
const grantResponse = await adminPage.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const grantText = await grantResponse.text();
|
||||
let grantBody = {};
|
||||
try {
|
||||
grantBody = grantText ? JSON.parse(grantText) : {};
|
||||
} catch {
|
||||
grantBody = { raw: grantText };
|
||||
}
|
||||
if (!grantResponse.ok() && grantBody.code !== "local_access_policy_grant_duplicate") {
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${grantResponse.status()} ${grantText.slice(0, 800)}`);
|
||||
}
|
||||
await requestJson(adminPage, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForFullAccessAskUser(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
await requestJson(page, `/api/ai-settings/effective?workspaceId=${encodeURIComponent(WORKSPACE_ID)}`);
|
||||
}
|
||||
|
||||
@@ -259,7 +280,7 @@ async function main() {
|
||||
|
||||
try {
|
||||
await quickLogin(page);
|
||||
await seedWorkspace(page);
|
||||
await seedWorkspace(browser, page);
|
||||
await abortExistingSession(page);
|
||||
const session = await startRealPi(page);
|
||||
result.session = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -35,13 +36,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -29,13 +30,30 @@ const CHROMIUM_EXECUTABLE = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE
|
||||
|| (fs.existsSync("/usr/bin/google-chrome") ? "/usr/bin/google-chrome" : "");
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -34,13 +35,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -29,13 +30,30 @@ const CHROMIUM_EXECUTABLE = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE
|
||||
|| (fs.existsSync("/usr/bin/google-chrome") ? "/usr/bin/google-chrome" : "");
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const button = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await button.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
button.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function fetchJson(page, url, options = {}) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -35,13 +36,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (!(await quickLoginButton.isVisible({ timeout: 4000 }).catch(() => false))) return;
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm, withAdminBrowserSession } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -31,13 +32,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
@@ -134,43 +152,46 @@ function policyForPlanMode() {
|
||||
};
|
||||
}
|
||||
|
||||
async function seedWorkspace(page) {
|
||||
async function seedWorkspace(browser, page) {
|
||||
mkdirp(ROOT_PATH);
|
||||
fs.writeFileSync(path.join(ROOT_PATH, PAGE_PATH), "# Pi plan mode browser smoke\n", "utf8");
|
||||
fs.writeFileSync(TARGET_ABS, TARGET_ORIGINAL, "utf8");
|
||||
const grantResponse = await page.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const grantText = await grantResponse.text();
|
||||
let grantBody = {};
|
||||
try {
|
||||
grantBody = grantText ? JSON.parse(grantText) : {};
|
||||
} catch {
|
||||
grantBody = { raw: grantText };
|
||||
}
|
||||
if (!grantResponse.ok() && grantBody.code !== "local_access_policy_grant_duplicate") {
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${grantResponse.status()} ${grantText.slice(0, 800)}`);
|
||||
}
|
||||
await requestJson(page, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForPlanMode(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
// 方案 A:admin 能力仅 mnote-admin
|
||||
await withAdminBrowserSession(browser, async (adminPage) => {
|
||||
const grantResponse = await adminPage.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const grantText = await grantResponse.text();
|
||||
let grantBody = {};
|
||||
try {
|
||||
grantBody = grantText ? JSON.parse(grantText) : {};
|
||||
} catch {
|
||||
grantBody = { raw: grantText };
|
||||
}
|
||||
if (!grantResponse.ok() && grantBody.code !== "local_access_policy_grant_duplicate") {
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${grantResponse.status()} ${grantText.slice(0, 800)}`);
|
||||
}
|
||||
await requestJson(adminPage, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForPlanMode(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
await requestJson(page, `/api/ai-settings/effective?workspaceId=${encodeURIComponent(WORKSPACE_ID)}`);
|
||||
}
|
||||
|
||||
@@ -279,7 +300,7 @@ async function main() {
|
||||
|
||||
try {
|
||||
await quickLogin(page);
|
||||
await seedWorkspace(page);
|
||||
await seedWorkspace(browser, page);
|
||||
await abortExistingSession(page);
|
||||
const session = await startPlanSession(page);
|
||||
result.session = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm, withAdminBrowserSession } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -108,13 +109,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
@@ -259,48 +277,51 @@ function policyForMatrix() {
|
||||
};
|
||||
}
|
||||
|
||||
async function seedWorkspace(page) {
|
||||
async function seedWorkspace(browser, page) {
|
||||
mkdirp(ROOT_PATH);
|
||||
fs.writeFileSync(
|
||||
path.join(ROOT_PATH, "pi-extension-mcp-matrix.md"),
|
||||
"# Pi extension and MCP matrix smoke\n",
|
||||
"utf8",
|
||||
);
|
||||
const grantResponse = await page.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const grantText = await grantResponse.text();
|
||||
let grantBody = {};
|
||||
try {
|
||||
grantBody = grantText ? JSON.parse(grantText) : {};
|
||||
} catch {
|
||||
grantBody = { raw: grantText };
|
||||
}
|
||||
if (!grantResponse.ok() && grantBody.code !== "local_access_policy_grant_duplicate") {
|
||||
throw new Error(
|
||||
`POST /api/admin/access-policy/grants failed: ${grantResponse.status()} ${grantText.slice(0, 1000)}`,
|
||||
);
|
||||
}
|
||||
await requestJson(page, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForMatrix(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
// 方案 A:admin 能力仅 mnote-admin
|
||||
await withAdminBrowserSession(browser, async (adminPage) => {
|
||||
const grantResponse = await adminPage.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const grantText = await grantResponse.text();
|
||||
let grantBody = {};
|
||||
try {
|
||||
grantBody = grantText ? JSON.parse(grantText) : {};
|
||||
} catch {
|
||||
grantBody = { raw: grantText };
|
||||
}
|
||||
if (!grantResponse.ok() && grantBody.code !== "local_access_policy_grant_duplicate") {
|
||||
throw new Error(
|
||||
`POST /api/admin/access-policy/grants failed: ${grantResponse.status()} ${grantText.slice(0, 1000)}`,
|
||||
);
|
||||
}
|
||||
await requestJson(adminPage, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForMatrix(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
return requestJson(page, `/api/ai-settings/effective?workspaceId=${encodeURIComponent(WORKSPACE_ID)}`);
|
||||
}
|
||||
|
||||
@@ -576,7 +597,7 @@ async function main() {
|
||||
|
||||
try {
|
||||
await quickLogin(page);
|
||||
result.seededEffective = await seedWorkspace(page);
|
||||
result.seededEffective = await seedWorkspace(browser, page);
|
||||
result.checks.policy = {
|
||||
mcpServers: ["CodeGraph", "SearXNG", "Chrome Bridge"].every(
|
||||
(name) => (result.seededEffective.mcpServers || []).some(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm, withAdminBrowserSession } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -28,13 +29,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
@@ -78,45 +96,50 @@ function policyForLightRag() {
|
||||
};
|
||||
}
|
||||
|
||||
async function ensureDirectoryGrant(page) {
|
||||
const response = await page.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const text = await response.text();
|
||||
let body = {};
|
||||
try {
|
||||
body = text ? JSON.parse(text) : {};
|
||||
} catch {
|
||||
body = { raw: text };
|
||||
}
|
||||
if (response.ok()) return body;
|
||||
if (body && body.code === "local_access_policy_grant_duplicate") return body;
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${response.status()} ${text.slice(0, 800)}`);
|
||||
async function ensureDirectoryGrant(browser) {
|
||||
// 方案 A:admin 能力仅 mnote-admin;e2e 为 AI 主体,不可调 /api/admin/*
|
||||
return withAdminBrowserSession(browser, async (adminPage) => {
|
||||
const response = await adminPage.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const text = await response.text();
|
||||
let body = {};
|
||||
try {
|
||||
body = text ? JSON.parse(text) : {};
|
||||
} catch {
|
||||
body = { raw: text };
|
||||
}
|
||||
if (response.ok()) return body;
|
||||
if (body && body.code === "local_access_policy_grant_duplicate") return body;
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${response.status()} ${text.slice(0, 800)}`);
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
}
|
||||
|
||||
async function seedRuntimePolicy(page) {
|
||||
async function seedRuntimePolicy(browser) {
|
||||
mkdirp(ROOT_PATH);
|
||||
await ensureDirectoryGrant(page);
|
||||
await requestJson(page, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForLightRag(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
await ensureDirectoryGrant(browser);
|
||||
await withAdminBrowserSession(browser, async (adminPage) => {
|
||||
await requestJson(adminPage, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policyForLightRag(),
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
}
|
||||
|
||||
async function ensureLightRagReady(page) {
|
||||
@@ -271,7 +294,7 @@ async function main() {
|
||||
|
||||
try {
|
||||
await quickLogin(page);
|
||||
await seedRuntimePolicy(page);
|
||||
await seedRuntimePolicy(browser);
|
||||
result.lightRagStatus = await ensureLightRagReady(page);
|
||||
result.seededEffective = await requestJson(page, `/api/ai-settings/effective?workspaceId=${encodeURIComponent(WORKSPACE_ID)}`);
|
||||
result.checks.lightRagToolsEnabled = [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm, withAdminBrowserSession } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -33,13 +34,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
@@ -133,46 +151,51 @@ function policyForAllSkillsAndMcp() {
|
||||
};
|
||||
}
|
||||
|
||||
async function ensureDirectoryGrant(page) {
|
||||
const response = await page.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const text = await response.text();
|
||||
let body = {};
|
||||
try {
|
||||
body = text ? JSON.parse(text) : {};
|
||||
} catch {
|
||||
body = { raw: text };
|
||||
}
|
||||
if (response.ok()) return body;
|
||||
if (body && body.code === "local_access_policy_grant_duplicate") return body;
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${response.status()} ${text.slice(0, 800)}`);
|
||||
async function ensureDirectoryGrant(browser) {
|
||||
// 方案 A:admin 能力仅 mnote-admin;e2e 为 AI 主体,不可调 /api/admin/*
|
||||
return withAdminBrowserSession(browser, async (adminPage) => {
|
||||
const response = await adminPage.request.fetch(`${BASE}/api/admin/access-policy/grants`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
data: {
|
||||
userId: ACTOR_ID,
|
||||
rootUri: ROOT_URI,
|
||||
rootPath: ROOT_PATH,
|
||||
permission: "write",
|
||||
recursive: true,
|
||||
capabilities: ["ai"],
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
const text = await response.text();
|
||||
let body = {};
|
||||
try {
|
||||
body = text ? JSON.parse(text) : {};
|
||||
} catch {
|
||||
body = { raw: text };
|
||||
}
|
||||
if (response.ok()) return body;
|
||||
if (body && body.code === "local_access_policy_grant_duplicate") return body;
|
||||
throw new Error(`POST /api/admin/access-policy/grants failed: ${response.status()} ${text.slice(0, 800)}`);
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
}
|
||||
|
||||
async function seedRuntimePolicy(page) {
|
||||
async function seedRuntimePolicy(browser) {
|
||||
mkdirp(ROOT_PATH);
|
||||
await ensureDirectoryGrant(page);
|
||||
await ensureDirectoryGrant(browser);
|
||||
const policy = policyForAllSkillsAndMcp();
|
||||
await requestJson(page, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policy,
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
await withAdminBrowserSession(browser, async (adminPage) => {
|
||||
await requestJson(adminPage, "/api/ai-admin/settings", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
...policy,
|
||||
quota: { daily: 200 },
|
||||
},
|
||||
});
|
||||
}, { baseUrl: BASE, timeoutMs: TIMEOUT });
|
||||
}
|
||||
|
||||
async function abortExistingSession(page) {
|
||||
@@ -373,7 +396,7 @@ async function main() {
|
||||
|
||||
try {
|
||||
await quickLogin(page);
|
||||
await seedRuntimePolicy(page);
|
||||
await seedRuntimePolicy(browser);
|
||||
result.seededEffective = await requestJson(page, "/api/ai-settings/effective");
|
||||
result.checks.allSkillsEnabled = ["VPN", "Chrome Bridge", "Context7", "SearXNG Search", "Global Search", "MemPalace", "CodeGraph"]
|
||||
.every((name) => (result.seededEffective.skills || []).some((skill) => skill.name === name && skill.enabled !== false));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
// Pi Lab RPC browser smoke
|
||||
// 验证真实 Pi RPC + Omniroute/gpt-5.4-mini 在 MNote-native Pi Lab 抽屉中的可见 stream。
|
||||
// 需要 mnote-web 已以 MNOTE_PAGE_AI_PI_LAB_RUNTIME=rpc 启动,并配置 MNOTE_PAGE_AI_PI_BIN / Omniroute key。
|
||||
@@ -67,12 +68,16 @@ async function addAuth(context) {
|
||||
async function quickLoginIfNeeded(page) {
|
||||
const authResponse = await page.goto(`${BASE}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
if (!authResponse || authResponse.status() >= 400) return;
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (!(await quickLoginButton.isVisible({ timeout: 4000 }).catch(() => false))) return;
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: UI_TIMEOUT_MS }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
const accountReady = await page
|
||||
.locator("#account")
|
||||
.first()
|
||||
.isVisible({ timeout: 4000 })
|
||||
.catch(() => false);
|
||||
if (!accountReady) return;
|
||||
await loginViaAuthForm(page, { baseUrl: BASE, timeoutMs: UI_TIMEOUT_MS, gotoAuth: false });
|
||||
await page
|
||||
.waitForURL((url) => !url.toString().includes("/auth"), { timeout: UI_TIMEOUT_MS })
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -21,13 +22,30 @@ function mkdirp(dir) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function startPi(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -106,13 +107,30 @@ function policyForExactPrompt() {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
await quickLoginButton.waitFor({ state: "visible", timeout: TIMEOUT });
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function requestJson(page, url, options = {}) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -41,13 +42,30 @@ async function requestJson(page, url, options = {}) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE}/auth`, { waitUntil: "commit", timeout: TIMEOUT });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (!(await quickLoginButton.isVisible({ timeout: 4000 }).catch(() => false))) return;
|
||||
await Promise.all([
|
||||
page.waitForURL((url) => !url.toString().includes("/auth"), { timeout: TIMEOUT }).catch(() => null),
|
||||
quickLoginButton.click(),
|
||||
]);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function cleanupPreviousSmokeSession(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const { spawn } = require("node:child_process");
|
||||
const net = require("node:net");
|
||||
@@ -114,7 +115,10 @@ async function validateGateway(baseUrl) {
|
||||
assert.match(authText, /data-mnote-shell="auth"/);
|
||||
assert.match(authText, /账号登录/);
|
||||
assert.match(authText, /邮箱或用户名/);
|
||||
assert.match(authText, /测试账号快速登录/);
|
||||
assert.doesNotMatch(authText, /测试账号快速登录/);
|
||||
assert.doesNotMatch(authText, /data-auth-test-login/);
|
||||
assert.doesNotMatch(authText, /data-test-password/);
|
||||
assert.match(authText, /data-auth-submit/);
|
||||
assert.doesNotMatch(authText, /隐私政策|使用\s*Google|使用\s*GitHub|第三方快捷/iu);
|
||||
|
||||
const root = await fetchWithTimeout(`${baseUrl}/`);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const { spawn } = require("node:child_process");
|
||||
const {
|
||||
@@ -72,7 +73,10 @@ async function validateAuthEntry(baseUrl) {
|
||||
assert.match(authText, /name="password"[^>]*type="password"|type="password"[^>]*name="password"/);
|
||||
assert.match(authText, /data-auth-mode="control-plane-session"/);
|
||||
assert.match(authText, /没有账号?注册/);
|
||||
assert.match(authText, /测试账号快速登录/);
|
||||
assert.doesNotMatch(authText, /测试账号快速登录/);
|
||||
assert.doesNotMatch(authText, /data-auth-test-login/);
|
||||
assert.doesNotMatch(authText, /data-test-password/);
|
||||
assert.match(authText, /data-auth-submit/);
|
||||
assert.doesNotMatch(authText, /隐私政策|使用\s*Google|使用\s*GitHub|第三方快捷/iu);
|
||||
|
||||
const authedAuth = await fetchWithTimeout(`${baseUrl}/auth`, {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
@@ -16,12 +17,30 @@ function cssString(value) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function readCreatedState(page, documentId) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use strict";
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
@@ -51,12 +52,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function waitForVisibleText(page, text) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use strict";
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
@@ -56,12 +57,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
@@ -12,12 +13,30 @@ const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
const OUT_DIR = path.join(ROOT, "tmp", "task438-filetree-title-md-active-reveal-smoke");
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openFileTree(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
@@ -16,12 +17,30 @@ function cssString(value) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openFileTree(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
@@ -16,12 +17,30 @@ function cssString(value) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openFileTree(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const fsp = require("node:fs/promises");
|
||||
@@ -22,12 +23,30 @@ function cssString(value) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openFileTree(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
@@ -12,12 +13,30 @@ const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
const OUT_DIR = path.join(ROOT, "tmp", "task442-trash-modal-workbench-smoke");
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function readState(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -47,12 +48,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -50,12 +51,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function uploadLocalAsset(page, root, documentId, fileName, mimeType, bytes, kind) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const { chromium } = require("playwright");
|
||||
|
||||
@@ -8,12 +9,30 @@ const BASE_URL = (process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000
|
||||
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -45,12 +46,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function activatePrimaryPageTab(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -45,12 +46,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function uploadLocalAsset(page, root, documentId, fileName, mimeType, bytes, kind) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -45,12 +46,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function uploadLocalAsset(page, root, documentId, fileName, mimeType, bytes, kind) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -45,12 +46,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -65,12 +66,30 @@ async function uploadLocalAsset(page, root, documentId, fileName, mimeType, byte
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -53,12 +54,30 @@ async function postTreeCommand(rootUri, action, documentId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openTrash(page, rootUri) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -32,12 +33,30 @@ function writeWorkspaceManifest(root) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function readTrashIndex(root) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -50,12 +51,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function uploadLocalAsset(page, root, documentId, fileName, mimeType, bytes, kind) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
/**
|
||||
* task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.js
|
||||
*
|
||||
@@ -81,12 +82,30 @@ async function postTreeCommand(rootUri, action, documentId) {
|
||||
* 快速登录(测试账号)
|
||||
*/
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
/**
|
||||
* task474-local-folder-empty-trash-ui-smoke.js
|
||||
*
|
||||
@@ -98,12 +99,30 @@ async function postTreeCommand(rootUri, action, documentId) {
|
||||
* 快速登录(测试账号)
|
||||
*/
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -47,12 +48,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function menuActions(page, kind) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -53,12 +54,30 @@ function writeWorkspaceManifest(root, ownerId, taskId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function activatePrimaryPageTab(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
/**
|
||||
* task487 — Local Folder Tree Live Consumer smoke
|
||||
*
|
||||
@@ -188,13 +189,14 @@ async function run() {
|
||||
let fatalError = null;
|
||||
|
||||
try {
|
||||
// ── 登录 ──
|
||||
// ── 登录(7-76 P0:标准表单)──
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: BASE_URL,
|
||||
timeoutMs: UI_TIMEOUT_MS,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => {});
|
||||
|
||||
// ── Phase 1: Page Tree 视图 ──
|
||||
await page.goto(documentUrl(root, "README.md", "page"), { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -47,12 +48,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -47,12 +48,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -47,12 +48,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -53,12 +54,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -51,12 +52,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -45,12 +46,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function uploadLocalAsset(page, root, documentId, fileName, mimeType, bytes, kind) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -63,12 +64,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function ensureTestUser(context) {
|
||||
@@ -310,7 +329,8 @@ async function createAdminGrant(root, targetUserId) {
|
||||
executablePath: CHROMIUM_EXECUTABLE_PATH,
|
||||
}).then((browser) => browser.newContext({
|
||||
extraHTTPHeaders: {
|
||||
"x-mnote-actor-id": "mnote-e2e",
|
||||
// 方案 A:ops admin 为 mnote-admin;勿再用 mnote-e2e 冒充 admin
|
||||
"x-mnote-actor-id": process.env.MNOTE_ADMIN_USERNAME || "mnote-admin",
|
||||
"x-mnote-actor-type": "admin",
|
||||
},
|
||||
}).then((context) => ({ browser, context })));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -45,12 +46,30 @@ function writeWorkspaceManifest(root, ownerId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openDocument(page, root, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -82,12 +83,30 @@ async function ensureTestUser(context) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function waitForEditor(page) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -66,12 +67,30 @@ function bridgeToolPayload(sessionId, assetId, allowedResourceIds, toolName = "m
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname !== "/auth", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function waitForOfficeReady(page, label) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -48,12 +49,30 @@ function writeWorkspaceManifest(root, ownerId, workspaceId) {
|
||||
}
|
||||
|
||||
async function quickLogin(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname !== "/auth", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
||||
const base =
|
||||
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
||||
(typeof baseUrl !== "undefined" && baseUrl) ||
|
||||
process.env.MNOTE_UI_BASE_URL ||
|
||||
"http://127.0.0.1:3000";
|
||||
const timeout =
|
||||
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
||||
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
||||
30_000;
|
||||
if (!String(page.url() || "").includes("/auth")) {
|
||||
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
||||
waitUntil: "commit",
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: base,
|
||||
timeoutMs: timeout,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page
|
||||
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function filetreeRowState(page, relativePath) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
@@ -43,9 +44,7 @@ async function saveScreenshot(page, name) {
|
||||
async function loginTestAccount(page) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
if (page.url().includes("/auth")) {
|
||||
const quickLogin = page.locator('[data-auth-test-login]').first();
|
||||
await quickLogin.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
await quickLogin.click({ timeout: UI_TIMEOUT_MS });
|
||||
await loginViaAuthForm(page, { timeoutMs: typeof UI_TIMEOUT_MS !== 'undefined' ? UI_TIMEOUT_MS : undefined });
|
||||
await page.waitForURL((url) => !url.toString().includes("/auth"), {
|
||||
waitUntil: "commit",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
|
||||
@@ -174,7 +174,15 @@ async function main() {
|
||||
references.every((reference) => reference.sourceRootRelativePath === sourceA),
|
||||
`sourcePaths scope 不应返回非 alpha 来源: ${JSON.stringify(references, null, 2).slice(0, 3000)}`,
|
||||
);
|
||||
assert(scoped.raw, "HTTP API 仍应保留 raw 供调试调用方使用");
|
||||
// raw 字段保留形状供调试,但上游全文已剥离(防跨租户泄漏);见 rawOmitted。
|
||||
assert(scoped.raw, "HTTP API 仍应保留 raw 字段(可为剥离占位)");
|
||||
assert.equal(
|
||||
scoped.rawOmitted === true
|
||||
|| (scoped.raw && scoped.raw.omitted === true)
|
||||
|| typeof scoped.raw === "object",
|
||||
true,
|
||||
`raw 应已剥离或为对象占位: ${JSON.stringify(scoped.raw, null, 2).slice(0, 500)}`,
|
||||
);
|
||||
const searched = await search(context, marker, [sourceA]);
|
||||
const searchResults = Array.isArray(searched.results) ? searched.results : [];
|
||||
assert.equal(searched.schema, "mnote.knowledge_rag.search_results.v1", `search schema 不正确: ${JSON.stringify(searched, null, 2).slice(0, 3000)}`);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const http = require("node:http");
|
||||
@@ -90,11 +91,12 @@ function documentUrl(baseUrl, rootUri, relativePath) {
|
||||
|
||||
async function quickLogin(page, baseUrl) {
|
||||
await page.goto(`${baseUrl}/auth`, { waitUntil: "domcontentloaded", timeout: TIMEOUT_MS });
|
||||
const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
|
||||
if (await quickLoginButton.count()) {
|
||||
await quickLoginButton.click({ timeout: TIMEOUT_MS });
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: TIMEOUT_MS });
|
||||
}
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl,
|
||||
timeoutMs: TIMEOUT_MS,
|
||||
gotoAuth: false,
|
||||
});
|
||||
await page.waitForURL((url) => url.pathname === "/", { timeout: TIMEOUT_MS }).catch(() => {});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -75,9 +75,13 @@ async function main() {
|
||||
});
|
||||
assert(knowledgeSearch.ok(), `资料库短 query 检索失败: ${knowledgeSearch.status()} ${await knowledgeSearch.text()}`);
|
||||
const knowledgePayload = await knowledgeSearch.json();
|
||||
assert((knowledgePayload.results?.length || 0) >= 8, `资料库 2 字 query 应返回段落去重后的 WeKnora 检索命中: ${JSON.stringify(knowledgePayload, null, 2).slice(0, 2000)}`);
|
||||
assert((knowledgePayload.results?.length || 0) >= 8, `资料库 2 字 query 应返回段落去重后的 LightRAG 检索命中: ${JSON.stringify(knowledgePayload, null, 2).slice(0, 2000)}`);
|
||||
assert(String(knowledgePayload.results?.[0]?.snippet || "").includes(SHORT_QUERY), `资料库结果未包含 ${SHORT_QUERY}: ${JSON.stringify(knowledgePayload.results?.[0], null, 2)}`);
|
||||
assert(knowledgePayload.references?.some((reference) => reference.provider === "weknora" || reference.matchSource === "weknora_search"), `2 字 query 应走 WeKnora search provider: ${JSON.stringify(knowledgePayload.references?.slice(0, 3), null, 2)}`);
|
||||
assert(knowledgePayload.references?.some((reference) => {
|
||||
const provider = String(reference.provider || "");
|
||||
const matchSource = String(reference.matchSource || "");
|
||||
return provider.includes("lightrag") || matchSource.includes("lightrag") || provider === "knowledge_rag";
|
||||
}), `2 字 query 应走 LightRAG search provider: ${JSON.stringify(knowledgePayload.references?.slice(0, 3), null, 2)}`);
|
||||
const firstKnowledgeResult = knowledgePayload.results?.[0] || {};
|
||||
const firstKnowledgeCitation = knowledgePayload.citations?.[0] || {};
|
||||
assert(firstKnowledgeResult.displayQuote && firstKnowledgeResult.locatorEvidenceText, `搜索结果缺少 displayQuote/locatorEvidenceText: ${JSON.stringify(firstKnowledgeResult, null, 2)}`);
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { chromium } = require("playwright");
|
||||
|
||||
const BASE_URL = (process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
|
||||
const ROOT_PATH = process.env.MNOTE_KNOWLEDGE_RAG_ROOT_PATH || "/mnt/Data1T/Mnote_data/users/mnote-e2e/workspaces/my-space";
|
||||
const ROOT_URI = process.env.MNOTE_KNOWLEDGE_RAG_ROOT_URI || `file://${ROOT_PATH}`;
|
||||
const WORKSPACE_ID = process.env.MNOTE_KNOWLEDGE_RAG_WORKSPACE_ID || "local-ws:mnote-e2e:my-space";
|
||||
const OUTPUT_DIR = path.join(process.cwd(), "tmp", "task79x-weknora-kb-page-browser-smoke");
|
||||
const RESULT_PATH = path.join(OUTPUT_DIR, "result.json");
|
||||
const FAILURE_PATH = path.join(OUTPUT_DIR, "failure.json");
|
||||
const SCREENSHOT_PATH = path.join(OUTPUT_DIR, "weknora-kb-page-browser-smoke.png");
|
||||
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
const CHROMIUM_EXECUTABLE_PATH = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
|
||||
|| ["/usr/bin/chromium-browser", "/usr/bin/google-chrome-stable", "/usr/bin/google-chrome", "/snap/bin/chromium", "/usr/bin/chromium"]
|
||||
.find((candidate) => fs.existsSync(candidate));
|
||||
|
||||
function writeJson(filePath, payload) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
function locatorCount(page, selector) {
|
||||
return page.locator(selector).count();
|
||||
}
|
||||
|
||||
async function signIn(context) {
|
||||
const response = await context.request.post(`${BASE_URL}/api/auth`, {
|
||||
data: {
|
||||
action: "auth:signIn",
|
||||
args: {
|
||||
provider: "password",
|
||||
params: {
|
||||
email: "mnote.e2e@example.com",
|
||||
password: process.env.MNOTE_E2E_PASSWORD || "MnoteE2E123!",
|
||||
flow: "signIn",
|
||||
},
|
||||
},
|
||||
},
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
assert(response.ok(), `登录失败: ${response.status()} ${await response.text()}`);
|
||||
}
|
||||
|
||||
async function visible(page, selector) {
|
||||
return page.locator(selector).first().isVisible().catch(() => false);
|
||||
}
|
||||
|
||||
async function visibleCount(page, selector) {
|
||||
return page.locator(selector).evaluateAll((nodes) => nodes.filter((node) => {
|
||||
if (!(node instanceof HTMLElement)) return false;
|
||||
const style = window.getComputedStyle(node);
|
||||
const rect = node.getBoundingClientRect();
|
||||
return style.visibility !== "hidden" && style.display !== "none" && rect.width > 0 && rect.height > 0;
|
||||
}).length).catch(() => 0);
|
||||
}
|
||||
|
||||
async function collectPageState(page) {
|
||||
const panelSelector = '[data-testid="mnote-weknora-knowledge-settings-panel"]';
|
||||
const panel = page.locator(panelSelector);
|
||||
const panelText = await panel.innerText({ timeout: UI_TIMEOUT_MS }).catch(() => "");
|
||||
const activeTab = await page.locator('[data-kb-rag-tab][aria-selected="true"]').first().getAttribute("data-kb-rag-tab").catch(() => "");
|
||||
const metaAttrs = await page.locator('[data-testid="mnote-knowledge-rag-meta"]').evaluate((node) => ({
|
||||
activeProvider: node.getAttribute("data-knowledge-rag-active-provider") || "",
|
||||
legacyFallbackProvider: node.getAttribute("data-knowledge-rag-legacy-fallback-provider") || "",
|
||||
referenceBoundary: node.getAttribute("data-knowledge-rag-reference-boundary") || "",
|
||||
})).catch(() => ({ activeProvider: "", legacyFallbackProvider: "", referenceBoundary: "" }));
|
||||
const firstCard = page.locator('[data-testid="mnote-weknora-kb-card-list"] [data-knowledge-rag-action="select-kb"]').first();
|
||||
const firstSourceRow = page.locator('[data-knowledge-rag-source-row="true"]').first();
|
||||
const firstSearchResult = page.locator('[data-kb-rag-search-result="true"]').first();
|
||||
|
||||
return {
|
||||
url: page.url(),
|
||||
title: await page.title().catch(() => ""),
|
||||
replacementMarker: await visible(page, '[data-mnote-weknora-page-replacement="true"]'),
|
||||
defaultProvider: await panel.getAttribute("data-knowledge-rag-default-provider").catch(() => ""),
|
||||
providerIndex: await panel.getAttribute("data-knowledge-rag-provider-index").catch(() => ""),
|
||||
sourceRegistry: await panel.getAttribute("data-knowledge-rag-source-registry").catch(() => ""),
|
||||
panelTextSample: panelText.slice(0, 1200),
|
||||
activeTab,
|
||||
metaAttrs,
|
||||
counts: {
|
||||
kbCards: await locatorCount(page, '[data-testid="mnote-weknora-kb-card-list"] [data-knowledge-rag-action="select-kb"]'),
|
||||
createKbControls: await visibleCount(page, '[data-testid="mnote-weknora-kb-controls"], [data-knowledge-rag-action="create-kb"]'),
|
||||
detailHeroes: await visibleCount(page, '[data-testid="mnote-weknora-kb-detail-hero"]'),
|
||||
tabs: await locatorCount(page, '[data-kb-rag-tab]'),
|
||||
sourcePlaceholdersOrRows: await locatorCount(page, '[data-testid="mnote-knowledge-rag-sources"] .wolai-page-settings-index-empty, [data-knowledge-rag-source-row="true"]'),
|
||||
sourceFilters: await locatorCount(page, '[data-testid="mnote-knowledge-rag-source-filters"] [data-knowledge-rag-action="filter-sources"]'),
|
||||
addDocumentEntries: await visibleCount(page, '[data-knowledge-rag-action="add-source"], [data-knowledge-rag-action="ingest"], [data-knowledge-rag-source-input]'),
|
||||
processingStatusControls: await locatorCount(page, '[data-knowledge-rag-processing-count], [data-knowledge-rag-processing-status], [role="progressbar"], [data-testid="mnote-knowledge-rag-status"]'),
|
||||
openReferenceControls: await locatorCount(page, '[data-knowledge-rag-action="open-source-reference"], [data-kb-rag-search-result="true"] [data-knowledge-rag-action="open-source-reference"]'),
|
||||
searchControls: await visibleCount(page, '[data-testid="mnote-weknora-doc-filter-input"], [data-kb-rag-search-input], [data-kb-rag-action="search"]'),
|
||||
oldSimplePanelSignals: await locatorCount(page, '.mnote-knowledge-rag-settings-panel:not(.mnote-weknora-kb-page), [data-testid="mnote-knowledge-rag-settings-panel"]:not([data-mnote-weknora-page-replacement="true"])'),
|
||||
},
|
||||
firstCard: {
|
||||
kbId: await firstCard.getAttribute("data-knowledge-rag-kb-id").catch(() => ""),
|
||||
processingCount: await firstCard.getAttribute("data-knowledge-rag-processing-count").catch(() => ""),
|
||||
text: await firstCard.innerText().catch(() => ""),
|
||||
},
|
||||
firstSourceRow: {
|
||||
sourcePath: await firstSourceRow.getAttribute("data-knowledge-rag-source-path").catch(() => ""),
|
||||
kbId: await firstSourceRow.getAttribute("data-knowledge-rag-kb-id").catch(() => ""),
|
||||
processingStatus: await firstSourceRow.getAttribute("data-knowledge-rag-processing-status").catch(() => ""),
|
||||
openReferenceMode: await firstSourceRow.getAttribute("data-knowledge-rag-open-reference-mode").catch(() => ""),
|
||||
text: await firstSourceRow.innerText().catch(() => ""),
|
||||
},
|
||||
firstSearchResult: {
|
||||
sourcePath: await firstSearchResult.getAttribute("data-knowledge-rag-source-path").catch(() => ""),
|
||||
kbId: await firstSearchResult.getAttribute("data-knowledge-rag-kb-id").catch(() => ""),
|
||||
text: await firstSearchResult.innerText().catch(() => ""),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function evaluateAssertions(state) {
|
||||
const assertions = {
|
||||
openedReplacementPanel: state.replacementMarker === true,
|
||||
providerIsWeKnora: state.defaultProvider === "weknora" && state.metaAttrs.activeProvider === "weknora",
|
||||
notOldSimpleSettingsPanel: state.counts.oldSimplePanelSignals === 0,
|
||||
hasKnowledgeBaseListCards: state.counts.kbCards > 0 || Boolean(state.firstCard.kbId),
|
||||
hasCreateKbControl: state.counts.createKbControls >= 2,
|
||||
hasDetailStructure: state.counts.detailHeroes > 0,
|
||||
hasDocumentTabs: state.counts.tabs >= 3 && /(Documents|Wiki|Graph|文档)/.test(state.panelTextSample),
|
||||
hasSourceOrTagSidebarPlaceholder: state.counts.sourcePlaceholdersOrRows > 0 && state.counts.sourceFilters >= 4,
|
||||
hasSearchAndFilter: state.counts.searchControls >= 1 && state.counts.sourceFilters >= 4,
|
||||
hasAddDocumentEntry: state.counts.addDocumentEntries >= 3,
|
||||
hasProcessingOrStatus: state.counts.processingStatusControls > 0 && /Processing|索引|provider 状态|就绪|未就绪/.test(state.panelTextSample),
|
||||
hasOpenReferenceOrCitationControl: state.counts.openReferenceControls > 0
|
||||
|| state.metaAttrs.referenceBoundary === "mnote-source-registry-weknora-kb-chunk-index"
|
||||
|| /引用|citation|open-reference|打开引用/i.test(state.panelTextSample),
|
||||
};
|
||||
const missing = Object.entries(assertions)
|
||||
.filter(([, ok]) => !ok)
|
||||
.map(([name]) => name);
|
||||
return { assertions, missing, ok: missing.length === 0 };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
...(CHROMIUM_EXECUTABLE_PATH ? { executablePath: CHROMIUM_EXECUTABLE_PATH } : {}),
|
||||
});
|
||||
const context = await browser.newContext({ viewport: { width: 1440, height: 980 } });
|
||||
const page = await context.newPage();
|
||||
|
||||
try {
|
||||
await signIn(context);
|
||||
const url = new URL(`${BASE_URL}/`);
|
||||
url.searchParams.set("sourceKind", "local_folder");
|
||||
url.searchParams.set("rootUri", ROOT_URI);
|
||||
url.searchParams.set("workspaceId", WORKSPACE_ID);
|
||||
url.searchParams.set("treeView", "filetree");
|
||||
await page.goto(url.toString(), { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
|
||||
await page.locator('[data-testid="mnote-knowledge-rag-settings-toggle"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
const panel = page.locator('[data-testid="mnote-weknora-knowledge-settings-panel"]');
|
||||
await panel.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForFunction(() => {
|
||||
const panelNode = document.querySelector('[data-testid="mnote-weknora-knowledge-settings-panel"]');
|
||||
if (!(panelNode instanceof HTMLElement)) return false;
|
||||
const text = panelNode.textContent || "";
|
||||
const hasCard = Boolean(panelNode.querySelector('[data-testid="mnote-weknora-kb-card-list"] [data-knowledge-rag-action="select-kb"]'));
|
||||
const statusSettled = !/正在读取知识库 provider 状态/.test(text);
|
||||
return hasCard || statusSettled;
|
||||
}, { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
await page.locator('[data-kb-rag-tab="sources"]').click({ timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
await page.locator('[data-knowledge-rag-action="filter-sources"][data-knowledge-rag-filter="all"]').click({ timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
await page.locator('[data-kb-rag-tab="search"]').click({ timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
await page.locator('[data-kb-rag-tab="sources"]').click({ timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
|
||||
await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true });
|
||||
const state = await collectPageState(page);
|
||||
const verdict = evaluateAssertions(state);
|
||||
const result = {
|
||||
ok: verdict.ok,
|
||||
task: "task79x-weknora-kb-page-browser-smoke",
|
||||
baseUrl: BASE_URL,
|
||||
rootUri: ROOT_URI,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
screenshot: SCREENSHOT_PATH,
|
||||
resultPath: RESULT_PATH,
|
||||
...verdict,
|
||||
state,
|
||||
};
|
||||
writeJson(RESULT_PATH, result);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
if (!result.ok) process.exit(1);
|
||||
} catch (error) {
|
||||
await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true }).catch(() => undefined);
|
||||
const failure = {
|
||||
ok: false,
|
||||
task: "task79x-weknora-kb-page-browser-smoke",
|
||||
baseUrl: BASE_URL,
|
||||
rootUri: ROOT_URI,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
screenshot: SCREENSHOT_PATH,
|
||||
error: error.stack || error.message || String(error),
|
||||
};
|
||||
writeJson(FAILURE_PATH, failure);
|
||||
console.error(JSON.stringify(failure, null, 2));
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await browser.close().catch(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,4 +1,5 @@
|
||||
const { chromium } = require('playwright');
|
||||
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
||||
@@ -8,12 +9,11 @@ const { chromium } = require('playwright');
|
||||
const testErrors = [];
|
||||
page.on('pageerror', err => testErrors.push(err.message));
|
||||
|
||||
// Login
|
||||
// 7-76:标准表单登录(无快速登录按钮)
|
||||
await page.goto('http://localhost:3000/auth', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(2000);
|
||||
const qlBtn = await page.locator('text=测试账号快速登录').count();
|
||||
if (qlBtn > 0) {
|
||||
await page.click('text=测试账号快速登录');
|
||||
if (await page.locator('#account').count()) {
|
||||
await loginViaAuthForm(page, { baseUrl: 'http://localhost:3000', gotoAuth: false });
|
||||
await page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,19 @@ const DOCUMENT_UI_BASE_URL = (
|
||||
BASE_URL
|
||||
).replace(/\/+$/, "");
|
||||
const MNOTE_WEB_BASE_URL = (process.env.MNOTE_WEB_SMOKE_BASE_URL || "").replace(/\/+$/, "");
|
||||
const {
|
||||
DEFAULT_EMAIL,
|
||||
DEFAULT_PASSWORD,
|
||||
loginViaAuthForm,
|
||||
loginViaAuthApi,
|
||||
} = require("./lib/browser-auth-login");
|
||||
|
||||
const REQUEST_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_TIMEOUT_MS || 20_000);
|
||||
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
const TEST_LOGIN_BUTTON_NAME = "测试账号快速登录";
|
||||
// 7-76 P0:不再依赖「测试账号快速登录」按钮;UI 走标准表单,API 走 /api/auth。
|
||||
const TEST_USERNAME_PREFIX = "mnote-e2e-";
|
||||
const TEST_EMAIL = "mnote.e2e@example.com";
|
||||
const TEST_PASSWORD = "MnoteE2E123!";
|
||||
const TEST_EMAIL = DEFAULT_EMAIL;
|
||||
const TEST_PASSWORD = DEFAULT_PASSWORD;
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) {
|
||||
@@ -128,19 +135,12 @@ async function getViewerIdentity(requestContext) {
|
||||
}
|
||||
|
||||
async function tryApiQuickLogin(requestContext) {
|
||||
await requestJson(requestContext, "/api/auth", {
|
||||
method: "POST",
|
||||
data: {
|
||||
action: "auth:signIn",
|
||||
args: {
|
||||
provider: "password",
|
||||
params: {
|
||||
email: TEST_EMAIL,
|
||||
password: TEST_PASSWORD,
|
||||
flow: "signIn",
|
||||
},
|
||||
},
|
||||
},
|
||||
// 名称保留兼容旧 smoke;实现为标准 password signIn(必要时 signUp)。
|
||||
await loginViaAuthApi(requestContext, {
|
||||
baseUrl: BASE_URL,
|
||||
email: TEST_EMAIL,
|
||||
password: TEST_PASSWORD,
|
||||
timeoutMs: REQUEST_TIMEOUT_MS,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -232,10 +232,10 @@ async function ensureAuthenticatedViaUi(page, requestContext) {
|
||||
await page.goto(`${BASE_URL}/auth`, { waitUntil: "commit", timeout: UI_TIMEOUT_MS });
|
||||
|
||||
if (page.url().includes("/auth")) {
|
||||
const quickLoginButton = page.getByRole("button", { name: TEST_LOGIN_BUTTON_NAME });
|
||||
const accountField = page.locator("#account, input[name='account']").first();
|
||||
const deadline = Date.now() + UI_TIMEOUT_MS;
|
||||
while (page.url().includes("/auth") && Date.now() < deadline) {
|
||||
if (await isVisible(quickLoginButton)) {
|
||||
if (await isVisible(accountField)) {
|
||||
break;
|
||||
}
|
||||
await page.waitForTimeout(200);
|
||||
@@ -243,11 +243,11 @@ async function ensureAuthenticatedViaUi(page, requestContext) {
|
||||
if (!page.url().includes("/auth")) {
|
||||
return await waitForViewerIdentity(requestContext);
|
||||
}
|
||||
if (!(await isVisible(quickLoginButton))) {
|
||||
if (!(await isVisible(accountField))) {
|
||||
await page.reload({ waitUntil: "commit", timeout: UI_TIMEOUT_MS }).catch(() => undefined);
|
||||
const retryDeadline = Date.now() + Math.min(8_000, UI_TIMEOUT_MS);
|
||||
while (page.url().includes("/auth") && Date.now() < retryDeadline) {
|
||||
if (await isVisible(quickLoginButton)) {
|
||||
if (await isVisible(accountField)) {
|
||||
break;
|
||||
}
|
||||
await page.waitForTimeout(200);
|
||||
@@ -256,18 +256,30 @@ async function ensureAuthenticatedViaUi(page, requestContext) {
|
||||
if (!page.url().includes("/auth")) {
|
||||
return await waitForViewerIdentity(requestContext);
|
||||
}
|
||||
if (!(await isVisible(quickLoginButton))) {
|
||||
throw new Error("认证页未出现测试账号快速登录按钮");
|
||||
if (!(await isVisible(accountField))) {
|
||||
throw new Error("认证页未出现标准登录表单(#account)");
|
||||
}
|
||||
await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await loginViaAuthForm(page, {
|
||||
baseUrl: BASE_URL,
|
||||
email: TEST_EMAIL,
|
||||
password: TEST_PASSWORD,
|
||||
timeoutMs: UI_TIMEOUT_MS,
|
||||
gotoAuth: false,
|
||||
});
|
||||
try {
|
||||
await waitForAuthenticatedRedirect(page, UI_TIMEOUT_MS);
|
||||
} catch {
|
||||
// 账号可能尚未 seed:UI 注册或 API 兜底创建后再进站。
|
||||
const completedUsername = await completeUsernameSetupIfNeeded(page);
|
||||
if (!completedUsername) {
|
||||
const registered = await registerTestAccountIfNeeded(page);
|
||||
if (!registered) {
|
||||
throw new Error("测试账号登录后仍停留在 /auth,且未进入可恢复的用户名/注册流程");
|
||||
try {
|
||||
await tryApiQuickLogin(requestContext);
|
||||
await page.goto(`${BASE_URL}/`, { waitUntil: "commit", timeout: UI_TIMEOUT_MS });
|
||||
} catch {
|
||||
throw new Error("标准登录后仍停留在 /auth,且注册/API 兜底失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ async function main() {
|
||||
if (probe.status === 0) throw new Error("unreachable");
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`mnote-web 不可达 ${BASE}(请先 desktop:hot 且 MNOTE_VAULT=1): ${e.message || e}`
|
||||
`mnote-web 不可达 ${BASE}(请先 dev:hot 且 MNOTE_VAULT=1): ${e.message || e}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user