这份文档用于在 Ubuntu 无图形界面(headless)环境下,稳定完成 Web 开发调试、问题复现、回归验证与发布排查。
- 不依赖“肉眼看页面”,用可量化指标替代主观判断。
- 先复现再修复,修复后必须回归。
- 每次问题都沉淀成脚本或 checklist,避免重复踩坑。
- 缓存问题默认高概率存在,版本号与资源参数必须联动更新。
安装依赖:
npm install
npx playwright install --with-deps常用启动方式:
# 方式1:直接跑 E2E(通常会自动起服务)
npm test
# 方式2:手动静态服务
npx http-server -p 8080 -s
# 或 python3 -m http.server 8080node - <<'NODE'
const { chromium } = require('playwright');
(async()=>{
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
await page.goto('http://127.0.0.1:8080', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'test-results/smoke.png', fullPage: true });
await browser.close();
})();
NODE用于排查“地图太小/偏移/遮挡”:
node - <<'NODE'
const { chromium } = require('playwright');
(async()=>{
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
await page.goto('http://127.0.0.1:8080', { waitUntil: 'networkidle' });
await page.waitForSelector('#map-container svg');
const box = await page.locator('#map-container').boundingBox();
console.log({ mapW: Math.round(box.width), mapH: Math.round(box.height) });
await browser.close();
})();
NODE用于判断按钮、面板、错误提示是否符合预期:
node - <<'NODE'
const { chromium } = require('playwright');
(async()=>{
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('http://127.0.0.1:8080', { waitUntil: 'networkidle' });
const state = await page.evaluate(()=>({
version: document.querySelector('#home-version')?.textContent?.trim(),
hasMap: !!document.querySelector('#map-container svg'),
assistHidden: document.querySelector('#assist-tools')?.classList.contains('hidden')
}));
console.log(state);
await browser.close();
})();
NODE常见 bug:拖动后点击失效、缩放被判成答题。
建议:
pointerdown记录起点与地图 transform。pointerup比较位移和 transform 变化。- 多指触控与
pointercancel必须清理状态。 - 非主指针(
isPrimary=false)不要进入答题主流程。
常见 bug:拖动后 event.target 失效导致“点了没反应”。
建议:
- 先尝试
event.target命中 feature。 - 回退到“屏幕坐标 -> 经纬度 -> geoContains(feature)”匹配。
- 两条路径都失败才判定为地图外点击。
建议统一为:
- 不计错
- 不跳题
- 给提示(例如“请点击地图轮廓内区域”)
Playwright 设备仿真:
node - <<'NODE'
const { chromium, devices } = require('playwright');
(async()=>{
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ ...devices['iPad Pro 11'] });
const page = await context.newPage();
await page.goto('http://127.0.0.1:8080', { waitUntil: 'networkidle' });
// 在这里模拟 tap/drag/pinch 场景
await browser.close();
})();
NODE移动端高频问题与处理:
- 悬停高亮残留:
- 仅在
@media (hover: hover) and (pointer: fine)下启用:hover。
- 工具栏遮挡地图:
- 默认折叠辅助操作,提供显隐开关。
- 误触拖动:
- 触屏阈值大于鼠标阈值。
node - <<'NODE'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('data/china-provinces.geojson','utf8'));
console.log('province features:', p.features.length);
NODE地图项目建议显式校验关键要素是否存在:
- 台湾
- 九段线
- 极小目标(如澳门)是否按玩法策略过滤
最低标准:
- 自动化:
npm test全绿。 - 手工脚本:
- 至少验证一次“拖动后点击仍有效”。
- 至少验证一次“地图外点击不计错”。
- 至少验证一次“移动端按钮不遮挡关键区域”。
- 发布前:版本号与资源 query 参数同步递增。
当用户反馈“我这边没更新”时,优先检查:
- 页面显示版本号是否已变化。
index.html中 CSS/JS 的?v=是否同步更新。- Pages/CDN 是否仍在旧缓存窗口。
建议固定流程:
- 改代码。
- 改
APP_VERSION。 - 改
index.html的资源版本参数。 - 回归测试。
- 提交推送。
可按下面模板记录每个问题:
问题:
- 现象:
- 设备/浏览器:
- 复现步骤:
定位:
- 关键日志:
- 根因:
修复:
- 修改文件:
- 方案:
验证:
- 自动化:
- 手工脚本:
- 截图/日志路径:# 测试
npm test
# 查看端口占用
lsof -i :8080 -sTCP:LISTEN -n -P
# 快速搜索代码
rg "pointerup|geoContains|haversine" js/
# 查看最近一次提交
git log -1 --oneline