const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const protocolSource = fs.readFileSync(path.join(__dirname, 'channels-protocols.js'), 'utf8'); const modalsSource = fs.readFileSync(path.join(__dirname, 'channels-modals.js'), 'utf8'); const html = fs.readFileSync(path.join(__dirname, '..', '..', 'channels.html'), 'utf8'); const zhLocaleSource = fs.readFileSync(path.join(__dirname, '..', 'locales', 'zh-CN.js'), 'utf8'); const enLocaleSource = fs.readFileSync(path.join(__dirname, '..', 'locales', 'en.js'), 'utf8'); function createModalElement() { const classSet = new Set(); return { textContent: '', disabled: false, classList: { add(name) { classSet.add(name); }, remove(name) { classSet.delete(name); }, contains(name) { return classSet.has(name); } } }; } function createBatchDeleteHarness() { const elements = new Map([ ['deleteModal', createModalElement()], ['deleteModalMessage', createModalElement()] ]); const fetchCalls = []; const successMessages = []; const warningMessages = []; const errorMessages = []; const loadChannelsCalls = []; let clearCacheCalls = 0; const translations = { 'channels.batchNoSelection': '请先选择至少一个渠道', 'channels.confirmBatchDeleteMsg': '将删除 {count} 个渠道,确认继续吗?', 'channels.batchDeleteSummary': '批量删除完成:删除 {deleted},不存在 {notFound}', 'channels.batchOperationFailed': '批量操作失败: {error}', 'common.failed': '失败' }; const sandbox = { console, window: { t(key, params = {}) { const template = translations[key] || key; return template.replace(/\{(\w+)\}/g, (_, name) => String(params[name] ?? '')); }, showSuccess(message) { successMessages.push(message); }, showWarning(message) { warningMessages.push(message); }, showError(message) { errorMessages.push(message); } }, document: { getElementById(id) { return elements.get(id) || null; } }, fetchAPIWithAuth: async (url, options = {}) => { fetchCalls.push({ url, options }); return { success: true, data: { deleted: 2, not_found_count: 1 } }; }, loadChannels: async (type) => { loadChannelsCalls.push(type); }, reloadChannelsList: async (type) => { loadChannelsCalls.push(type ?? 'all'); }, clearChannelsCache() { clearCacheCalls += 1; }, filters: { channelType: 'all' }, deletingChannelRequest: null, selectedChannelIds: new Set(), normalizeSelectedChannelID(value) { return String(value); } }; vm.createContext(sandbox); vm.runInContext(`${protocolSource}\n${modalsSource}`, sandbox); return { sandbox, elements, fetchCalls, successMessages, warningMessages, errorMessages, loadChannelsCalls, getClearCacheCalls() { return clearCacheCalls; } }; } test('channels.html 提供批量删除按钮和动态删除提示容器', () => { assert.match(html, /id="batchDeleteChannelsBtn"[\s\S]*?data-action="batch-delete-channels"/); assert.match(html, /id="deleteModalMessage"/); }); test('batchDeleteSelectedChannels 复用删除弹窗显示批量删除提示', () => { const { sandbox, elements, warningMessages } = createBatchDeleteHarness(); sandbox.selectedChannelIds = new Set(['1', '2']); sandbox.batchDeleteSelectedChannels(); assert.equal(warningMessages.length, 0); assert.equal(elements.get('deleteModal').classList.contains('show'), true); assert.equal(elements.get('deleteModalMessage').textContent, '将删除 2 个渠道,确认继续吗?'); }); test('confirmDelete 在批量模式下调用批量删除接口并显示摘要', async () => { const { sandbox, fetchCalls, successMessages, loadChannelsCalls, getClearCacheCalls } = createBatchDeleteHarness(); sandbox.selectedChannelIds = new Set(['1', '2']); sandbox.batchDeleteSelectedChannels(); await sandbox.confirmDelete(); assert.equal(fetchCalls.length, 1); assert.equal(fetchCalls[0].url, '/admin/channels/batch-delete'); assert.equal(fetchCalls[0].options.method, 'POST'); assert.equal(fetchCalls[0].options.body, JSON.stringify({ channel_ids: [1, 2] })); assert.deepEqual(Array.from(sandbox.selectedChannelIds), []); assert.equal(getClearCacheCalls(), 1); assert.deepEqual(loadChannelsCalls, ['all']); assert.deepEqual(successMessages, ['批量删除完成:删除 2,不存在 1']); }); test('批量删除文案已添加到中英文语言包', () => { assert.match(zhLocaleSource, /'channels\.batchDeleteChannels': '批量删除'/); assert.match(zhLocaleSource, /'channels\.confirmBatchDeleteMsg': '将删除 \{count\} 个渠道,确认继续吗?'/); assert.match(zhLocaleSource, /'channels\.batchDeleteSummary': '批量删除完成:删除 \{deleted\},不存在 \{notFound\}'/); assert.match(enLocaleSource, /'channels\.batchDeleteChannels': 'Batch Delete'/); assert.match(enLocaleSource, /'channels\.confirmBatchDeleteMsg': 'Delete \{count\} selected channels\?'/); assert.match(enLocaleSource, /'channels\.batchDeleteSummary': 'Batch delete completed: deleted \{deleted\}, not found \{notFound\}'/); });