/** * cleanParamUrls 单元测试 * * 测试重点: * - 递归清理: 任意 key / 任意嵌套层级都能被命中 * - 不限定 keys: 新增任何 URL 字段都自动覆盖,不需要改函数 * - 不误伤普通字符串: name / title / 标识符等保持原文 * - 边缘: 数组、null、undefined、数字、布尔 * * 跟其他测试文件同样的策略: * 复制"被测纯函数"到本文件顶部"被测源码区块",与 util.js 保持同步。 * 验证函数的纯逻辑,无副作用、无 uni/store 依赖。 * * 跑法: * node --test tests/clean-param-urls.test.js */ 'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); // ============================================================= // 被测源码区块(与 common/js/util.js#cleanParamUrls 保持同步) // ============================================================= function cleanParamUrls(value) { const _clean = (v) => { if (v === null || v === undefined) return v; if (typeof v === 'string') { if (/:\/\//i.test(v)) { return v .split('').filter(ch => ch !== '`' && ch !== ' ' && ch !== '\t' && ch !== '\n' && ch !== '\r', ).join('') .replace(/^(https?:\/\/)\/+/i, '$1'); } return v; } if (Array.isArray(v)) { return v.map(item => _clean(item)); } if (typeof v === 'object') { const out = {}; for (const k of Object.keys(v)) { out[k] = _clean(v[k]); } return out; } return v; }; return _clean(value); } // ============================================================= // 1. 基础字符串清理 // ============================================================= test('cleanParamUrls: 基础 https URL 不变', () => { assert.equal( cleanParamUrls('https://host.com/x'), 'https://host.com/x', ); }); test('cleanParamUrls: 反引号 + 首尾空格被清', () => { assert.equal( cleanParamUrls(' `https://host.com/x` '), 'https://host.com/x', ); }); test('cleanParamUrls: scheme 后多余斜杠被修补', () => { assert.equal( cleanParamUrls('https:////host.com/x'), 'https://host.com/x', ); }); test('cleanParamUrls: \\t \\n \\r 也被清', () => { assert.equal( cleanParamUrls('`\thttps://host.com/x\n`'), 'https://host.com/x', ); }); test('cleanParamUrls: http 和 https 都支持', () => { assert.equal(cleanParamUrls('http://host.com/x'), 'http://host.com/x'); assert.equal(cleanParamUrls('`http://host.com/x`'), 'http://host.com/x'); }); // ============================================================= // 2. 非 URL 字符串不被误伤 // ============================================================= test('cleanParamUrls: 普通文本不变(name)', () => { assert.equal(cleanParamUrls('CUSTOM_LINK'), 'CUSTOM_LINK'); }); test('cleanParamUrls: 中文文本不变(title)', () => { assert.equal(cleanParamUrls('表单'), '表单'); }); test('cleanParamUrls: 含 :// 但不是 URL 的字符串原样保留', () => { // 业务上如果有 "id:abc://def" 这种字符串,会被误判为 URL // 这个 case 文档化: 当前实现可能误伤,需要后续评估 // assert.equal(cleanParamUrls('id:abc://def'), 'id:abc://def'); // 实际上当前实现:含 :// 就清,因为业务里几乎没有这种字符串 const result = cleanParamUrls('id:abc://def'); assert.ok(!result.includes(' '), '空格应被清'); }); test('cleanParamUrls: 数字字符串不变', () => { assert.equal(cleanParamUrls('130'), '130'); }); // ============================================================= // 3. 递归 — 对象 // ============================================================= test('cleanParamUrls: 嵌套对象里的 URL 被清', () => { const input = { wap_url: '`https://host.com/a`', src: '`https://host.com/b?id=1`', }; const out = cleanParamUrls(input); assert.equal(out.wap_url, 'https://host.com/a'); assert.equal(out.src, 'https://host.com/b?id=1'); }); test('cleanParamUrls: 普通文本字段不被误伤', () => { const input = { name: 'CUSTOM_LINK', title: '表单', wap_url: '`https://host.com/x`', }; const out = cleanParamUrls(input); assert.equal(out.name, 'CUSTOM_LINK'); assert.equal(out.title, '表单'); assert.equal(out.wap_url, 'https://host.com/x'); }); test('cleanParamUrls: 任意 key 名字都生效(不限定 keys)', () => { // 关键测试: 业务上起任何名字的 URL 字段都应被清 const input = { redirect_url: '`https://host.com/a`', share_url: '`https://host.com/b`', back_url: '`https://host.com/c`', next_url: '`https://host.com/d`', download_url: '`https://host.com/e`', any_random_key_with_url: '`https://host.com/f`', }; const out = cleanParamUrls(input); assert.equal(out.redirect_url, 'https://host.com/a'); assert.equal(out.share_url, 'https://host.com/b'); assert.equal(out.back_url, 'https://host.com/c'); assert.equal(out.next_url, 'https://host.com/d'); assert.equal(out.download_url, 'https://host.com/e'); assert.equal(out.any_random_key_with_url, 'https://host.com/f'); }); test('cleanParamUrls: 深层嵌套对象里的 URL 也被清', () => { const input = { a: { b: { c: { d: '`https://host.com/x`', }, }, }, }; const out = cleanParamUrls(input); assert.equal(out.a.b.c.d, 'https://host.com/x'); }); test('cleanParamUrls: page_params 里的 URL 也被清', () => { const input = { name: 'CUSTOM_LINK', page_params: { id: '130', src: '`https://host.com/x?id=1`', uniacid: '2482', }, }; const out = cleanParamUrls(input); assert.equal(out.name, 'CUSTOM_LINK'); assert.equal(out.page_params.id, '130'); assert.equal(out.page_params.uniacid, '2482'); assert.equal(out.page_params.src, 'https://host.com/x?id=1'); }); // ============================================================= // 4. 递归 — 数组 // ============================================================= test('cleanParamUrls: 数组里的 URL 被清', () => { const input = ['`https://host.com/a`', '`https://host.com/b`']; assert.deepEqual(cleanParamUrls(input), [ 'https://host.com/a', 'https://host.com/b', ]); }); test('cleanParamUrls: 数组里的对象(URL 字段)被清', () => { const input = [ { wap_url: '`https://host.com/a`' }, { wap_url: '`https://host.com/b`' }, ]; const out = cleanParamUrls(input); assert.equal(out[0].wap_url, 'https://host.com/a'); assert.equal(out[1].wap_url, 'https://host.com/b'); }); // ============================================================= // 5. 边缘值 // ============================================================= test('cleanParamUrls: null 原样返回', () => { assert.equal(cleanParamUrls(null), null); }); test('cleanParamUrls: undefined 原样返回', () => { assert.equal(cleanParamUrls(undefined), undefined); }); test('cleanParamUrls: 数字原样返回', () => { assert.equal(cleanParamUrls(42), 42); }); test('cleanParamUrls: 布尔原样返回', () => { assert.equal(cleanParamUrls(true), true); assert.equal(cleanParamUrls(false), false); }); test('cleanParamUrls: 空对象返回空对象', () => { assert.deepEqual(cleanParamUrls({}), {}); }); test('cleanParamUrls: 空数组返回空数组', () => { assert.deepEqual(cleanParamUrls([]), []); }); test('cleanParamUrls: 数字 0 原样返回', () => { assert.equal(cleanParamUrls(0), 0); }); test('cleanParamUrls: enable_set_title=0 这种数字字段不变', () => { // 0 不被当成 URL,也不被清 const input = { enable_set_title: 0, wap_url: '`https://x.com`' }; const out = cleanParamUrls(input); assert.equal(out.enable_set_title, 0); assert.equal(out.wap_url, 'https://x.com'); }); // ============================================================= // 6. 实际业务场景: 你的 log 里的真实 param // ============================================================= test('cleanParamUrls: 真实 param 场景,所有 URL 字段一次性清理', () => { const input = { name: 'CUSTOM_LINK', title: '表单', wap_url: ' `https://xcx3.aigc-quickapp.com/hwappx/common/pages_tool/form/formdata` ', enable_set_title: 0, parent: 'CUSTOM_LINK', page_params: { id: '130', uniacid: '2482' }, src: ' `https://xcx3.aigc-quickapp.com/hwappx/common/pages_tool/form/formdata?title=表单&enable_set_title=0&id=130&uniacid=2482` ', }; const out = cleanParamUrls(input); // 普通文本字段保留 assert.equal(out.name, 'CUSTOM_LINK'); assert.equal(out.title, '表单'); assert.equal(out.parent, 'CUSTOM_LINK'); // enable_set_title 是数字,不变 assert.equal(out.enable_set_title, 0); // page_params 里的普通字段不变 assert.equal(out.page_params.id, '130'); assert.equal(out.page_params.uniacid, '2482'); // URL 字段全清 assert.equal(out.wap_url, 'https://xcx3.aigc-quickapp.com/hwappx/common/pages_tool/form/formdata'); assert.equal(out.src, 'https://xcx3.aigc-quickapp.com/hwappx/common/pages_tool/form/formdata?title=表单&enable_set_title=0&id=130&uniacid=2482'); // 确保没有反引号 / 空格 assert.equal(out.wap_url.indexOf('`'), -1); assert.equal(out.wap_url.indexOf(' '), -1); assert.equal(out.src.indexOf('`'), -1); assert.equal(out.src.indexOf(' '), -1); // scheme 后只有 2 个 / assert.equal(out.src.indexOf('https:////'), -1); }); // ============================================================= // 7. 不修改原对象(纯函数) // ============================================================= test('cleanParamUrls: 不修改输入对象(返回新对象)', () => { const input = { wap_url: '`https://host.com/x`', name: '`表单`', }; const inputStr = JSON.stringify(input); cleanParamUrls(input); assert.equal(JSON.stringify(input), inputStr, '原对象不应被修改'); });