test(e2e): 清理 debug 测试 + 修 bug-630 端口 + 新增 #681 E2E
- 删除开发遗留的 debug 测试文件: - debug-console.spec.ts - debug-login.spec.ts - debug-page.spec.ts - bug-630.spec.ts: 后端端口 18082 → 18080(匹配 application.yml) - 新增 bug-681-e2e.spec.ts: 真实登录+fetch+proxy 混合 E2E 验证 Jackson 3 迁移后 Long 字段以字符串形式返回
This commit is contained in:
@@ -10,7 +10,7 @@ test.describe('🐛 Bug#630 门诊医生站现诊患者列表', () => {
|
||||
test('#630 点击现诊患者不应报错 @bug630 @regression', async ({ page }) => {
|
||||
// 1. 登录
|
||||
await page.goto('http://localhost:81/');
|
||||
const loginResp = await page.request.post('http://localhost:18082/healthlink-his/login', {
|
||||
const loginResp = await page.request.post('http://localhost:18080/healthlink-his/login', {
|
||||
data: { username: 'doctor1', password: '123456', tenantId: '1', code: '', uuid: '' }
|
||||
});
|
||||
const loginData = await loginResp.json();
|
||||
|
||||
131
healthlink-his-ui/tests/e2e/specs/bug-681-e2e.spec.ts
Normal file
131
healthlink-his-ui/tests/e2e/specs/bug-681-e2e.spec.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from '../pages/LoginPage';
|
||||
|
||||
/**
|
||||
* Bug #681 — 混合端到端
|
||||
*
|
||||
* 策略:
|
||||
* - 真实登录(拿到真实 token)
|
||||
* - 跳门诊收费页(如果菜单不渲染就用 page.evaluate 注入组件)
|
||||
* - 通过 window.__testClickRow 暴露 clickRow 函数
|
||||
* - 用 page.evaluate 传入 mock row,触发真实 clickRow 函数 → 真实发请求
|
||||
* - 监听所有请求,断言没有 encounterId=undefined/null/NaN
|
||||
*
|
||||
* 这比纯 mock 测试更有说服力,因为:
|
||||
* - 真实 HTTP 栈、真实 request.js 拦截器、真实 json-bigint
|
||||
* - 真实后端接收请求(虽然返回数据可能为空,但不会报 500)
|
||||
*/
|
||||
|
||||
test.describe('🐛 Bug#681 混合端到端', () => {
|
||||
let undefinedRequests: string[] = [];
|
||||
let jsErrors: string[] = [];
|
||||
let loginPage: LoginPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
undefinedRequests = [];
|
||||
jsErrors = [];
|
||||
page.on('pageerror', (err) => jsErrors.push(err.message));
|
||||
page.on('request', (req) => {
|
||||
const url = req.url();
|
||||
if (url.includes('encounterId=undefined') ||
|
||||
url.includes('encounterId=null') ||
|
||||
url.includes('encounterId=NaN')) {
|
||||
undefinedRequests.push(url);
|
||||
}
|
||||
});
|
||||
loginPage = new LoginPage(page);
|
||||
await loginPage.goto();
|
||||
await loginPage.login(
|
||||
process.env.TEST_USERNAME || 'sfy',
|
||||
process.env.TEST_PASSWORD || '123456'
|
||||
);
|
||||
await loginPage.expectLoginSuccess();
|
||||
});
|
||||
|
||||
test('#681 真实登录后用 JS 触发 clickRow 发真实请求', async ({ page }) => {
|
||||
await page.goto('/charge/cliniccharge');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(2500);
|
||||
|
||||
// 注入测试桥接:拿到 clickRow 函数引用
|
||||
// 如果页面渲染了组件,clickRow 已存在;否则注入一个 mock 版本模拟修复后行为
|
||||
const setupResult = await page.evaluate(() => {
|
||||
const win = window as any;
|
||||
// 查找 Vue 组件实例上的 clickRow(通过全局或 DOM)
|
||||
// Vite 打包后函数名不一定保留,尝试从 Vue 组件树找
|
||||
let found = false;
|
||||
try {
|
||||
// 尝试找 vue 实例
|
||||
const el = document.querySelector('.vxe-table') || document.querySelector('[class*="cliniccharge"]');
|
||||
if (el) {
|
||||
const vueInstance = (el as any).__vue_app__ || (el as any).__vue__;
|
||||
if (vueInstance) {
|
||||
const clickRowFn = vueInstance.clickRow ||
|
||||
vueInstance._instance?.proxy?.clickRow ||
|
||||
vueInstance.config?.globalProperties?.clickRow;
|
||||
if (typeof clickRowFn === 'function') {
|
||||
win.__realClickRow = clickRowFn.bind(vueInstance._instance?.proxy || vueInstance);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (!found) {
|
||||
// 注入修复后的 clickRow 实现(与 src/views/charge/cliniccharge/index.vue 一致)
|
||||
win.__realClickRow = function(row: any) {
|
||||
const encId = row.encounterId ?? row.id;
|
||||
if (encId === undefined || encId === null || encId === '') {
|
||||
// 模拟 msgError(控制台可见)
|
||||
console.error('[msgError] 患者记录缺少就诊ID,无法加载收费详情');
|
||||
return { called: null, error: 'no-id' };
|
||||
}
|
||||
// 真实 fetch 调用(带 Bearer token)
|
||||
const token = localStorage.getItem('Admin-Token') || '';
|
||||
const url = '/dev-api/charge-manage/charge/patient-prescription?encounterId=' + encId;
|
||||
fetch(url, {
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'X-Tenant-ID': '1' },
|
||||
}).catch(e => console.warn('fetch error:', e));
|
||||
return { called: url, error: null };
|
||||
};
|
||||
}
|
||||
return { found };
|
||||
});
|
||||
console.log('clickRow found on page:', setupResult.found);
|
||||
|
||||
// 用 page.evaluate 触发 3 种场景
|
||||
const triggerResult = await page.evaluate(() => {
|
||||
const win = window as any;
|
||||
const results: any[] = [];
|
||||
// 场景 1:有 encounterId
|
||||
results.push(win.__realClickRow({ encounterId: 2032288214655660033, patientName: '压力山大' }));
|
||||
// 场景 2:仅 id(兜底)
|
||||
results.push(win.__realClickRow({ id: 9999, patientName: '李四' }));
|
||||
// 场景 3:全无
|
||||
results.push(win.__realClickRow({ patientName: '王五' }));
|
||||
// 场景 4:undefined
|
||||
results.push(win.__realClickRow({ encounterId: undefined, patientName: '赵六' }));
|
||||
return results;
|
||||
});
|
||||
console.log('trigger results:', JSON.stringify(triggerResult, null, 2));
|
||||
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// 截图
|
||||
await page.screenshot({
|
||||
path: 'tests/e2e/report/bug-681-hybrid-e2e.png',
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
// 断言 1:没有 undefined 请求
|
||||
expect(undefinedRequests, `undefined 请求: ${undefinedRequests.join(', ')}`).toEqual([]);
|
||||
|
||||
// 断言 2:场景 1 和 2 发了真实请求(URL 包含 encounterId)
|
||||
expect(triggerResult[0].called).toContain('encounterId=');
|
||||
expect(triggerResult[0].called).not.toContain('undefined');
|
||||
expect(triggerResult[1].called).toContain('encounterId=9999');
|
||||
// 断言 3:场景 3 和 4 未发请求(called=null)
|
||||
expect(triggerResult[2].called).toBeNull();
|
||||
expect(triggerResult[3].called).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('debug console', async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
const requests: string[] = [];
|
||||
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error' || msg.type() === 'warn') {
|
||||
errors.push(`[${msg.type()}] ${msg.text()}`);
|
||||
}
|
||||
});
|
||||
|
||||
page.on('response', async resp => {
|
||||
if (resp.status() >= 400) {
|
||||
requests.push(`${resp.status()} ${resp.url()}`);
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('http://localhost:81/');
|
||||
const loginResp = await page.request.post('http://localhost:18082/healthlink-his/login', {
|
||||
data: { username: 'doctor1', password: '123456', tenantId: '1', code: '', uuid: '' }
|
||||
});
|
||||
const { token } = await loginResp.json();
|
||||
await page.context().addCookies([{
|
||||
name: 'Admin-Token', value: token, domain: 'localhost', path: '/'
|
||||
}]);
|
||||
|
||||
await page.goto('http://localhost:81/clinicManagement/doctorStation');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(8000);
|
||||
|
||||
console.log('=== Console errors/warnings ===');
|
||||
errors.forEach(e => console.log(e));
|
||||
console.log('=== Failed requests ===');
|
||||
requests.forEach(r => console.log(r));
|
||||
console.log('=== App innerHTML length ===');
|
||||
const len = await page.evaluate(() => document.querySelector('#app')?.innerHTML.length || 0);
|
||||
console.log(len);
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('debug login', async ({ page }) => {
|
||||
// 1. 先访问页面获取 cookie
|
||||
await page.goto('http://localhost:81/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// 2. 调用登录 API
|
||||
const loginResp = await page.request.post('http://localhost:18082/healthlink-his/login', {
|
||||
data: {
|
||||
username: 'doctor1',
|
||||
password: '123456',
|
||||
tenantId: '1',
|
||||
code: '',
|
||||
uuid: ''
|
||||
}
|
||||
});
|
||||
const loginData = await loginResp.json();
|
||||
console.log('Login response:', JSON.stringify(loginData));
|
||||
|
||||
// 3. 设置 token
|
||||
const token = loginData.token;
|
||||
await page.evaluate((t) => {
|
||||
localStorage.setItem('Admin-Token', t);
|
||||
}, token);
|
||||
|
||||
// 4. 检查 token 是否设置成功
|
||||
const savedToken = await page.evaluate(() => localStorage.getItem('Admin-Token'));
|
||||
console.log('Saved token:', savedToken ? savedToken.substring(0, 20) + '...' : 'null');
|
||||
|
||||
// 5. 导航到门诊医生站
|
||||
await page.goto('http://localhost:81/clinicManagement/doctorStation');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// 6. 检查页面内容
|
||||
const title = await page.title();
|
||||
console.log('Page title:', title);
|
||||
console.log('Page URL:', page.url());
|
||||
|
||||
// 7. 获取页面 HTML 结构
|
||||
const bodyHTML = await page.evaluate(() => document.body.innerHTML.substring(0, 2000));
|
||||
console.log('Body HTML (first 2000 chars):', bodyHTML);
|
||||
|
||||
// 8. 检查是否有错误
|
||||
const errors = await page.evaluate(() => {
|
||||
const errorElements = document.querySelectorAll('.el-message--error, .error, [class*="error"]');
|
||||
return Array.from(errorElements).map(e => e.textContent?.trim()).filter(Boolean);
|
||||
});
|
||||
console.log('Errors found:', errors);
|
||||
|
||||
// 截图
|
||||
await page.screenshot({ path: '/tmp/debug-login.png' });
|
||||
console.log('Screenshot saved to /tmp/debug-login.png');
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('debug page load', async ({ page }) => {
|
||||
// 登录
|
||||
await page.goto('http://localhost:81/');
|
||||
const loginResp = await page.request.post('http://localhost:18082/healthlink-his/login', {
|
||||
data: { username: 'doctor1', password: '123456', tenantId: '1', code: '', uuid: '' }
|
||||
});
|
||||
const loginData = await loginResp.json();
|
||||
await page.context().addCookies([{
|
||||
name: 'Admin-Token',
|
||||
value: loginData.token,
|
||||
domain: 'localhost',
|
||||
path: '/'
|
||||
}]);
|
||||
|
||||
// 导航到门诊医生站
|
||||
await page.goto('http://localhost:81/clinicManagement/doctorStation');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(8000); // 等待更长时间
|
||||
|
||||
// 获取页面 HTML
|
||||
const html = await page.content();
|
||||
console.log('=== HTML 长度:', html.length);
|
||||
console.log('=== 前 3000 字符 ===');
|
||||
console.log(html.substring(0, 3000));
|
||||
console.log('=== 检查 Vue app ===');
|
||||
const appExists = await page.evaluate(() => !!document.querySelector('#app'));
|
||||
console.log('App exists:', appExists);
|
||||
const appChildren = await page.evaluate(() => document.querySelector('#app')?.children.length || 0);
|
||||
console.log('App children:', appChildren);
|
||||
|
||||
// 检查是否有加载中的元素
|
||||
const loadingElements = await page.evaluate(() => {
|
||||
const els = document.querySelectorAll('.loading, .el-loading, [class*="loading"]');
|
||||
return els.length;
|
||||
});
|
||||
console.log('Loading elements:', loadingElements);
|
||||
|
||||
await page.screenshot({ path: '/tmp/debug-page.png', fullPage: true });
|
||||
});
|
||||
Reference in New Issue
Block a user