debug-admin-spa.spec.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * 调试脚本:诊断远程 cfc-web SPA 为何不渲染侧边栏
  3. * 运行:npx playwright test debug-admin-spa.spec.js --config=playwright.remote.config.js
  4. */
  5. const { test, expect } = require('@playwright/test');
  6. var BASE_URL = 'http://cfc.iwintrue.com';
  7. test('debug SPA render - capture console + network failures', async ({ page }) => {
  8. // 1. 捕获页面 console 与错误
  9. var consoleMessages = [];
  10. var pageErrors = [];
  11. var failedRequests = [];
  12. var responses = [];
  13. page.on('console', function (msg) {
  14. consoleMessages.push('[' + msg.type() + '] ' + msg.text());
  15. });
  16. page.on('pageerror', function (err) {
  17. pageErrors.push(String(err) + '\nSTACK: ' + (err.stack || 'no-stack'));
  18. });
  19. page.on('requestfailed', function (req) {
  20. failedRequests.push(req.method() + ' ' + req.url() + ' :: ' + (req.failure() && req.failure().errorText));
  21. });
  22. page.on('response', function (res) {
  23. if (res.status() >= 400) {
  24. responses.push(res.status() + ' ' + res.url());
  25. }
  26. });
  27. // 2. 登录拿 token(与 spec 相同)
  28. var loginRes = await page.request.post(BASE_URL + '/api/admin-auth/login-by-password', {
  29. data: { phone: '13800138000', password: 'admin123' },
  30. headers: { 'Content-Type': 'application/json' }
  31. });
  32. console.log('LOGIN_STATUS=' + loginRes.status());
  33. var body = await loginRes.json();
  34. console.log('LOGIN_BODY=' + JSON.stringify(body).substring(0, 300));
  35. if (body.code === 200 && body.data && body.data.token) {
  36. // 注意:addInitScript 只接受一个 arg,必须打包成单个对象传参
  37. await page.addInitScript(function (auth) {
  38. var token = auth.token, role = auth.role, roles = auth.roles, adminId = auth.adminId;
  39. localStorage.setItem('token', token);
  40. localStorage.setItem('access_token', token);
  41. localStorage.setItem('role', role);
  42. localStorage.setItem('roles', JSON.stringify(roles));
  43. localStorage.setItem('adminId', String(adminId));
  44. // Monkey-patch JSON.parse 捕获崩溃点
  45. var origParse = JSON.parse;
  46. window.__parseLog = [];
  47. JSON.parse = function (input) {
  48. if (input === undefined || input === null || (typeof input === 'string' && input.indexOf('undefined') >= 0)) {
  49. window.__parseLog.push({
  50. input: String(input),
  51. stack: new Error('captured').stack,
  52. rolesRaw: localStorage.getItem('roles'),
  53. roleRaw: localStorage.getItem('role'),
  54. tokenRaw: localStorage.getItem('token')
  55. });
  56. }
  57. return origParse.apply(this, arguments);
  58. };
  59. }, { token: body.data.token, role: body.data.role, roles: body.data.roles || [body.data.role], adminId: body.data.adminId });
  60. }
  61. // 捕获 localStorage 所有写入(在 app 脚本前注入)
  62. await page.addInitScript(function () {
  63. var origSet = Storage.prototype.setItem;
  64. window.__storageLog = [];
  65. Storage.prototype.setItem = function (k, v) {
  66. window.__storageLog.push(k + '=' + String(v).substring(0, 60));
  67. return origSet.call(this, k, v);
  68. };
  69. });
  70. // 3. 访问 dashboard
  71. await page.goto(BASE_URL + '/admin/dashboard', { waitUntil: 'domcontentloaded', timeout: 60000 });
  72. await page.waitForTimeout(8000);
  73. console.log('FINAL_URL=' + page.url());
  74. console.log('TITLE=' + (await page.title()));
  75. var appHtml = await page.evaluate(function () {
  76. var app = document.getElementById('app');
  77. return app ? app.innerHTML.substring(0, 500) : 'NO #app';
  78. });
  79. console.log('APP_HTML=' + appHtml);
  80. var bodyText = await page.evaluate(function () {
  81. return document.body.innerText.substring(0, 300);
  82. });
  83. console.log('BODY_TEXT=' + bodyText);
  84. var asideCount = await page.locator('.el-aside').count();
  85. var menuCount = await page.locator('.el-menu').count();
  86. var loginCount = await page.locator('.login-container, #login, .login-page').count();
  87. console.log('ASIDE_COUNT=' + asideCount + ' MENU_COUNT=' + menuCount + ' LOGIN_COUNT=' + loginCount);
  88. console.log('--- PAGE ERRORS (' + pageErrors.length + ') ---');
  89. pageErrors.forEach(function (e) { console.log('ERR: ' + e.substring(0, 500)); });
  90. console.log('--- FAILED REQUESTS (' + failedRequests.length + ') ---');
  91. failedRequests.forEach(function (r) { console.log('FAIL: ' + r); });
  92. console.log('--- HTTP>=400 RESPONSES (' + responses.length + ') ---');
  93. responses.forEach(function (r) { console.log('RESP: ' + r); });
  94. console.log('--- CONSOLE (' + consoleMessages.length + ') ---');
  95. consoleMessages.forEach(function (m) { console.log('CON: ' + m.substring(0, 300)); });
  96. await page.screenshot({ path: 'test-artifacts/debug-spa.png', fullPage: true });
  97. // 输出 monkey-patch 捕获
  98. var parseLog = await page.evaluate(function () { return window.__parseLog || []; });
  99. console.log('--- JSON.PARSE CAPTURES (' + parseLog.length + ') ---');
  100. parseLog.forEach(function (entry) {
  101. console.log('INPUT=' + entry.input);
  102. console.log('ROLES_RAW=' + entry.rolesRaw + ' ROLE_RAW=' + entry.roleRaw + ' TOKEN_RAW=' + (entry.tokenRaw || '').substring(0, 20));
  103. console.log('STACK=' + (entry.stack || 'no-stack'));
  104. });
  105. var storageLog = await page.evaluate(function () { return window.__storageLog || []; });
  106. console.log('--- STORAGE WRITES (' + storageLog.length + ') ---');
  107. storageLog.forEach(function (s) { console.log('SET: ' + s); });
  108. expect(true).toBeTruthy();
  109. });