| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- 'use strict';
- // 新手闯关引导静态 smoke test
- // 无浏览器依赖:校验资源文件、引入顺序、关键锚点与 localStorage 键。
- // 运行:node scripts/onboarding-guide-smoke-test.js
- const assert = require('assert/strict');
- const fs = require('fs');
- const path = require('path');
- const { spawnSync } = require('child_process');
- const ROOT = path.resolve(__dirname, '..');
- const DASH = path.join(ROOT, 'mcp', 'src', 'dashboard');
- function read(file) {
- return fs.readFileSync(path.join(DASH, file), 'utf8');
- }
- function testStaticAssetsExist() {
- ['driver.min.js', 'driver.css', 'onboarding.css', 'onboarding-guide.js'].forEach((name) => {
- const stat = fs.statSync(path.join(DASH, name));
- assert(stat.size > 0, `${name} 应为非空文件`);
- });
- }
- function testIndexHtmlReferences() {
- const html = read('index.html');
- // 资源必须被引入,版本号随改动变化,只断言路径前缀
- assert.match(html, /\/dashboard\/driver\.css\?v=/);
- assert.match(html, /\/dashboard\/onboarding\.css\?v=/);
- assert.match(html, /\/dashboard\/driver\.min\.js\?v=/);
- assert.match(html, /\/dashboard\/onboarding-guide\.js\?v=/);
- // 加载顺序:driver.min.js 必须先于 onboarding-guide.js
- const driverIdx = html.indexOf('driver.min.js');
- const guideIdx = html.indexOf('onboarding-guide.js');
- assert(driverIdx > -1 && guideIdx > driverIdx, 'driver.min.js 应加载于 onboarding-guide.js 之前');
- }
- function testSidebarEntry() {
- const html = read('index.html');
- const start = html.indexOf('<a class="nav-item onboarding-nav-entry"');
- assert(start > -1, '侧边栏应包含 id="onboarding-nav-entry" 的入口');
- const end = html.indexOf('</a>', start);
- assert(end > start, '入口 <a> 应闭合');
- const entry = html.slice(start, end + 4);
- assert(entry.includes('id="onboarding-nav-entry"'), '入口应带 id');
- assert(entry.includes('onboarding-progress-badge'), '入口应包含进度徽标');
- // 入口绝不能带 data-page(避免被 renderPage 当路由 / setActiveNav 匹配)
- assert(!/data-page\s*=/.test(entry), '新手闯关入口不得包含 data-page,否则会与 hash 路由冲突');
- }
- function testGuideSourceContainsKeyMarkers() {
- const src = read('onboarding-guide.js');
- ['qiwei-onboarding-v1', '/api/status', '/api/agent/status', '/api/agent/voice/status',
- '#btn-subscribe-action', '[data-agent-action="manage-allowlist"]', '#allowlist-contact-list',
- '#allowlist-save', '#voice-profile-body', '[data-voice-save]', "ENTRY_ID = 'onboarding-nav-entry'"].forEach((marker) => {
- assert(src.includes(marker), `onboarding-guide.js 应包含关键标记:${marker}`);
- });
- // 关卡完整性:5 个设置关卡(巡览关已改为每页引导)
- ['welcome', 'subscribe', 'account', 'allowlist', 'voice'].forEach((id) => {
- assert(new RegExp(`id: '${id}'`).test(src), `应定义关卡:${id}`);
- });
- // 不再存在 tour 关卡
- assert(!new RegExp(`id: 'tour'`).test(src), '不应再有关卡 tour(已改为每页引导)');
- // 每页引导:注册表 + 引擎 + 自动触发标记
- ['PAGE_GUIDES', 'pageSeen', 'runPageGuide', 'maybeAutoPageGuide', 'openPageGuideFromOverview'].forEach((marker) => {
- assert(src.includes(marker), `onboarding-guide.js 应包含每页引导标记:${marker}`);
- });
- // 覆盖全部页面
- ['overview', 'analytics', 'agent', 'knowledge', 'skills', 'customer-ops', 'portraits', 'transfers', 'group-ops', 'groups', 'status'].forEach((pg) => {
- assert(new RegExp(`['"]?${pg}['"]?: \\{`).test(src), `PAGE_GUIDES 应覆盖页面:${pg}`);
- });
- }
- function testGuideSyntax() {
- const result = spawnSync(process.execPath, ['--check', path.join(DASH, 'onboarding-guide.js')], {
- encoding: 'utf8',
- windowsHide: true,
- });
- assert.equal(result.status, 0, result.stderr || 'onboarding-guide.js 语法错误');
- }
- function testPackageJsonScript() {
- const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
- assert.equal(typeof pkg.scripts['onboarding:smoke'], 'string', 'package.json 应有 onboarding:smoke 脚本');
- }
- function main() {
- testStaticAssetsExist();
- testIndexHtmlReferences();
- testSidebarEntry();
- testGuideSourceContainsKeyMarkers();
- testGuideSyntax();
- testPackageJsonScript();
- process.stdout.write(`${JSON.stringify({ status: 'ok', checks: 6 }, null, 2)}\n`);
- }
- try { main(); }
- catch (error) {
- process.stderr.write(`${error.message}\n`);
- process.exit(1);
- }
|