| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- import fs from 'node:fs/promises';
- import path from 'node:path';
- import assert from 'node:assert/strict';
- import { spawn } from 'node:child_process';
- import { pathToFileURL } from 'node:url';
- import { WORKSHOP_OUTPUTS, workshopReview } from '../src/core/workshop-outputs.js';
- import { buildModel } from '../src/core/kfs/build.js';
- const out = path.resolve('docs/workshop-report-coverage-r13-20261110');
- const raw = JSON.parse(await fs.readFile(path.join(out, 'local-public-projection.json')));
- const groups = JSON.parse(await fs.readFile(path.join(out, 'local-shared-groups.json')));
- const review = workshopReview(buildModel(raw), groups, { mode: 'sample' });
- const { chromium } = await import(pathToFileURL(path.resolve('work/workshop-manual-tools/node_modules/playwright/index.mjs')).href);
- const server = spawn(process.execPath, ['src/dev/serve.mjs', '--port', '4186'], { cwd: process.cwd(), windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
- let log = '';
- server.stdout.on('data', b => { log += b; });server.stderr.on('data', b => { log += b; });
- const origin = 'http://127.0.0.1:4186';
- const browser = await chromium.launch({ headless: true, executablePath: 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe' });
- const checks = [], errors = [], requests = [];
- try {
- for (let attempt = 0; attempt < 50; attempt++) {
- if (server.exitCode !== null) throw Error('Local test server failed: ' + log);
- try { const r = await fetch(origin); if (r.ok) break; } catch {}
- if (attempt === 49) throw Error('Local test server not ready: ' + log);
- await new Promise(resolve => setTimeout(resolve, 100));
- }
- const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
- // Synthetic data is only supplied inside this browser. Nothing reaches production.
- await context.route('**/*', route => {
- const request = route.request(), url = new URL(request.url());
- if (url.pathname === '/api/functions' || url.pathname === '/__dev/member-groups') {
- const body = request.postDataJSON(); requests.push(body?.path);
- const response = body?.path === '/ksfGetPublicBoard' ? raw : body?.path === '/ksfGetPublicMemberGroups' ? groups : null;
- if (!response) { errors.push('Unexpected action: ' + body?.path); return route.abort(); }
- return route.fulfill({ contentType: 'application/json', body: JSON.stringify({ result: { ok: true, data: response } }) });
- }
- if (url.origin === origin && request.method() === 'GET') return route.continue();
- errors.push('Unexpected network request: ' + url.origin + url.pathname);return route.abort();
- });
- const page = await context.newPage();page.on('pageerror', error => errors.push(error.message));
- const ready = async () => {
- await page.locator('view-workshop-outputs .filters').waitFor();
- await page.waitForFunction(() => document.querySelector('kf-app')?.shadowRoot.querySelector('view-workshop-outputs')?._loading === false);
- };
- await page.goto(origin + '/workshop');await ready();
- assert.equal(await page.locator('[data-workshop-artifact]').count(), 0);
- checks.push('样例不进入默认真实资料列表');
- await page.getByLabel('资料范围', { exact: true }).selectOption('sample');
- await page.waitForFunction(() => document.querySelector('kf-app')?.shadowRoot.querySelector('view-workshop-outputs')?.renderRoot.querySelectorAll('[data-workshop-artifact]').length === 16);
- assert.equal(await page.locator('[data-workshop-artifact]').count(), 16);
- assert.equal(await page.locator('.matrix thead th').count(), 9);
- checks.push('16份本地草稿显示,含3份独立机会卡报告;七类矩阵存在');
- for (const output of WORKSHOP_OUTPUTS) {
- await page.getByLabel('成果类别', { exact: true }).selectOption(output.key);
- const expected = review.rows.filter(row => row.refs.some(ref => ref.key === output.key)).length;
- await page.waitForFunction(n => document.querySelector('kf-app')?.shadowRoot.querySelector('view-workshop-outputs')?.renderRoot.querySelectorAll('[data-workshop-artifact]').length === n, expected);
- const row = review.rows.find(row => row.refs.some(ref => ref.key === output.key && ref.chapterId));
- assert.ok(row);
- const ref = row.refs.find(ref => ref.key === output.key);
- await page.locator(`[data-workshop-artifact="${row.id}"] .refs a`).filter({ hasText: output.name }).click();
- await page.locator(`view-report [data-chapter="${ref.chapterId}"]`).waitFor();
- const chapter = row.artifact.report.chapters.find(ch => ch.id === ref.chapterId);
- const text = await page.locator(`view-report [data-chapter="${ref.chapterId}"]`).textContent();
- const normalize = s => s.replace(/\s/g, '');
- assert.ok(normalize(text).includes(normalize(chapter.sections[0].text).slice(0, 90)));
- assert.equal(new URL(page.url()).searchParams.get('ch'), ref.chapterId);
- checks.push(output.name + ':筛选数量、正文、章节链接通过(不代表内容已完整)');
- await page.goBack();await ready();
- }
- await page.getByLabel('成果类别', { exact: true }).selectOption('');
- for (const width of [1280, 390]) {
- await page.setViewportSize({ width, height: 900 });
- const size = await page.evaluate(() => ({ width: document.documentElement.clientWidth, scroll: document.documentElement.scrollWidth }));
- assert.ok(size.scroll <= size.width, JSON.stringify(size));
- await page.screenshot({ path: path.join(out, 'local-board-' + width + '.png'), fullPage: true });
- checks.push(width + 'px页面无横向溢出');
- }
- await context.close();
- } finally { await browser.close(); server.kill(); }
- assert.deepEqual(errors, []);
- await fs.writeFile(path.join(out, 'browser-check.json'), JSON.stringify({ checkedAt: new Date().toISOString(),
- method: 'Actual local board components with in-memory API projection intercepted in browser only',
- productionWrites: 0, deployed: false, checks, errors, requests: [...new Set(requests)] }, null, 2) + '\n');
- console.log(JSON.stringify({ checks: checks.length, errors, productionWrites: 0 }));
|