Files
Telegram-Panel/frontend/tests/taskCreationVisibility.test.mjs

303 lines
23 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
const tasksSource = await readFile(new URL('../src/views/Tasks.vue', import.meta.url), 'utf8')
const taskConfigFormSource = await readFile(new URL('../src/components/TaskConfigForm.vue', import.meta.url), 'utf8')
const directMessagingTaskConfigFormSource = await readFile(new URL('../src/components/DirectMessagingTaskConfigForm.vue', import.meta.url), 'utf8')
const accountsSource = await readFile(new URL('../src/views/Accounts.vue', import.meta.url), 'utf8')
const typesSource = await readFile(new URL('../src/api/types.ts', import.meta.url), 'utf8')
test('新建任务只展示后端允许创建的任务类型', () => {
assert.match(typesSource, /canCreate:\s*boolean/)
assert.match(tasksSource, /definitions\.value\.filter\(\(x\) => x\.canCreate\)/)
assert.doesNotMatch(tasksSource, /x\.canCreate \|\| x\.category !== 'system'/)
assert.match(tasksSource, /taskCenterCreateDefinitions\.value\.map/)
assert.match(tasksSource, /taskCenterCreateDefinitions\.value\s*\.filter/)
})
test('Fragment 用户名监控在任务中心使用独立配置表单', () => {
assert.match(tasksSource, /taskType === 'fragment_username_monitor'/)
assert.match(tasksSource, /if \(!definition \|\| hasTaskConfigForm\(definition\.taskType\)\) return ''/)
assert.match(tasksSource, /editRoute && !hasTaskConfigForm\(def\.taskType\)/)
assert.match(taskConfigFormSource, /taskType === 'fragment_username_monitor'/)
assert.match(taskConfigFormSource, /Usernames:/)
assert.match(taskConfigFormSource, /TargetGroupIds:/)
assert.match(taskConfigFormSource, /CheckIntervalSeconds:/)
assert.match(taskConfigFormSource, /AssignedUsernames: \[\]/)
})
test('独立模块任务编辑时携带任务 ID 返回模块页面', () => {
assert.match(tasksSource, /resolveCreateTarget\(def\)/)
assert.match(tasksSource, /taskId=\$\{encodeURIComponent\(String\(task\.id\)\)\}&mode=edit/)
assert.match(tasksSource, /withModulePageMode\(routeWithTaskId, false\)/)
})
test('私信任务使用宿主原生配置表单', () => {
assert.match(tasksSource, /import DirectMessagingTaskConfigForm from '@\/components\/DirectMessagingTaskConfigForm\.vue'/)
assert.match(tasksSource, /function isDirectMessagingTask\(taskType: string\) \{\s*return taskType === 'direct_message\.live' \|\| taskType === 'direct_message\.batch'/)
assert.match(tasksSource, /<DirectMessagingTaskConfigForm\s+v-if="isDirectMessagingTask\(createDialog\.form\.taskType\)"/)
assert.match(tasksSource, /<DirectMessagingTaskConfigForm\s+v-if="isDirectMessagingTask\(editTaskDialog\.form\.taskType\)"/)
assert.doesNotMatch(tasksSource, /<DirectMessagingTaskConfigForm\s+v-if="isDirectMessagingTask\(editScheduledDialog\.form\.taskType\)"/)
assert.match(tasksSource, /direct-messaging-task-dialog': isDirectMessagingTask\(editTaskDialog\.form\.taskType\)/)
assert.match(tasksSource, /direct-messaging-task-dialog': isDirectMessagingTask\(editScheduledDialog\.form\.taskType\)/)
assert.match(tasksSource, /私信任务暂不支持 Cron 计划编辑/)
assert.match(tasksSource, /if \(isDirectMessagingTask\(dialog\.form\.taskType\)\)/)
assert.doesNotMatch(tasksSource, /moduleInlineVisible|module-inline-frame|module-inline-panel/)
assert.match(tasksSource, /if \(definition && isDirectMessagingTask\(definition\.taskType\)\) return ''/)
assert.match(tasksSource, /isDirectMessagingTask\(form\.taskType\) && !createDraft\.value\.canSubmit/)
assert.match(tasksSource, /isDirectMessagingTask\(dialog\.form\.taskType\) && !editDraft\.value\.canSubmit/)
assert.match(tasksSource, /if \(isDirectMessagingTask\(dialog\.form\.taskType\)\) \{\s*ElMessage\.warning\('私信任务暂不支持 Cron 计划编辑/)
assert.match(directMessagingTaskConfigFormSource, /props\.taskType === 'direct_message\.live'/)
assert.match(directMessagingTaskConfigFormSource, /props\.taskType === 'direct_message\.batch'/)
assert.match(directMessagingTaskConfigFormSource, /api\.get<unknown>\('\/extensions\/direct-messaging\/options'\)/)
assert.match(directMessagingTaskConfigFormSource, /\/extensions\/direct-messaging\/accounts\/\$\{encodeURIComponent\(String\(accountId\)\)\}\/groups/)
assert.match(directMessagingTaskConfigFormSource, /api\.post<unknown>\('\/extensions\/direct-messaging\/assets', formData\)/)
assert.match(directMessagingTaskConfigFormSource, /formData\.append\('file', raw\)/)
assert.match(directMessagingTaskConfigFormSource, /form\.messageRules\.length < 50/)
assert.match(directMessagingTaskConfigFormSource, /type SenderSource = 'Category' \| 'AccountIds'/)
assert.match(directMessagingTaskConfigFormSource, /type SenderMode = 'Queue' \| 'Random'/)
assert.match(directMessagingTaskConfigFormSource, /type DedupeScope = 'Global' \| 'Task'/)
assert.match(directMessagingTaskConfigFormSource, /type ContentAction = 'MessageRules' \| 'Forward' \| 'Todo'/)
assert.match(directMessagingTaskConfigFormSource, /config\.listenerAccountId = Math\.trunc\(form\.listenerAccountId\)/)
assert.match(directMessagingTaskConfigFormSource, /config\.chats = chats/)
assert.match(directMessagingTaskConfigFormSource, /config\.manualUsernames = usernames/)
assert.match(directMessagingTaskConfigFormSource, /config\.dictionaryKey = form\.dictionaryKey \|\| null/)
assert.match(directMessagingTaskConfigFormSource, /senderSource: form\.senderSource/)
assert.match(directMessagingTaskConfigFormSource, /senderCategory: form\.senderSource === 'Category' \? senderCategory : null/)
assert.match(directMessagingTaskConfigFormSource, /senderAccountIds: form\.senderSource === 'AccountIds' \? senderAccountIds : \[\]/)
assert.match(directMessagingTaskConfigFormSource, /senderMode: form\.senderMode/)
assert.match(directMessagingTaskConfigFormSource, /ruleMode: form\.ruleMode/)
assert.match(directMessagingTaskConfigFormSource, /form\.ruleMode = normalizeSenderMode\(content \? readValue\(content, 'ruleMode'\)/)
assert.match(directMessagingTaskConfigFormSource, /dedupeDays,/)
assert.match(directMessagingTaskConfigFormSource, /dedupeScope: form\.dedupeScope/)
assert.match(directMessagingTaskConfigFormSource, /cooldownSeconds,/)
assert.match(directMessagingTaskConfigFormSource, /rolling24h,/)
assert.match(directMessagingTaskConfigFormSource, /const content = \{\s*\.\.\.rawContent,\s*action: form\.contentAction,/)
assert.match(directMessagingTaskConfigFormSource, /messageRules: form\.contentAction === 'MessageRules' \? messageRules : \[\]/)
assert.match(directMessagingTaskConfigFormSource, /forward: form\.contentAction === 'Forward' \? buildForwardConfig\(/)
assert.match(directMessagingTaskConfigFormSource, /todo: form\.contentAction === 'Todo' \? buildTodoConfig\(/)
assert.match(directMessagingTaskConfigFormSource, /content,/)
assert.match(directMessagingTaskConfigFormSource, /dedupDays: 3/)
assert.match(directMessagingTaskConfigFormSource, /dedupeScope: 'Task'/)
assert.match(directMessagingTaskConfigFormSource, /dedupeScope: 'Task'[\s\S]{0,120}cooldownSeconds: 60/)
assert.match(directMessagingTaskConfigFormSource, /cooldownSeconds: 60/)
assert.match(directMessagingTaskConfigFormSource, /rolling24h: 20/)
assert.match(directMessagingTaskConfigFormSource, /v-model="form\.dedupeDisabled"/)
assert.match(directMessagingTaskConfigFormSource, /@change="onDedupeDisabledChanged"/)
assert.match(directMessagingTaskConfigFormSource, /:min="form\.dedupeDisabled \? 0 : 1"/)
assert.match(directMessagingTaskConfigFormSource, /:disabled="form\.dedupeDisabled"/)
assert.match(directMessagingTaskConfigFormSource, /const lastPositiveDedupeDays = ref\(3\)/)
assert.match(directMessagingTaskConfigFormSource, /form\.dedupeDisabled = dedupeDays === 0/)
assert.match(directMessagingTaskConfigFormSource, /if \(dedupeDays > 0\) lastPositiveDedupeDays\.value = dedupeDays/)
assert.match(directMessagingTaskConfigFormSource, /const dedupeDays = form\.dedupeDisabled\s*\? 0\s*:\s*clampInteger\(form\.dedupDays, lastPositiveDedupeDays\.value \|\| 3, 1, 3650\)/)
assert.match(directMessagingTaskConfigFormSource, /function onDedupeDisabledChanged\(disabled: boolean \| string \| number\)/)
assert.doesNotMatch(directMessagingTaskConfigFormSource, /dedupeDisabled:\s*form\.dedupeDisabled/)
assert.match(directMessagingTaskConfigFormSource, /v-model="form\.rolling24h" :min="1" :max="1000"/)
assert.match(directMessagingTaskConfigFormSource, /const telegramUsernamePattern = \/\^\[A-Za-z\]\[A-Za-z0-9_\]\{4,31\}\$\//)
assert.match(directMessagingTaskConfigFormSource, /usernames\.length > 10000/)
assert.match(directMessagingTaskConfigFormSource, /const assetId = textValue/)
assert.match(directMessagingTaskConfigFormSource, /rule\.assetId = assetId/)
assert.match(directMessagingTaskConfigFormSource, /imageAssetId: rule\.assetId\.trim\(\) \|\| null/)
assert.match(directMessagingTaskConfigFormSource, /forwardSourceDictionaryKey/)
assert.match(directMessagingTaskConfigFormSource, /todoTitle/)
assert.match(directMessagingTaskConfigFormSource, /isNumericCategoryValue/)
assert.match(directMessagingTaskConfigFormSource, /senderCategoryFormatError/)
assert.match(directMessagingTaskConfigFormSource, /normalizeAccountOptions/)
assert.match(directMessagingTaskConfigFormSource, /formatAccountOptionLabel/)
assert.match(directMessagingTaskConfigFormSource, /panelApi\.operationAccounts\(\)/)
assert.match(directMessagingTaskConfigFormSource, /用户名词典/)
assert.match(directMessagingTaskConfigFormSource, /每条规则只能发送文本或图片;最多 50 条。/)
assert.doesNotMatch(directMessagingTaskConfigFormSource, /两者组合/)
assert.match(directMessagingTaskConfigFormSource, /<el-form-item v-if="form\.contentAction === 'Todo'" label="原生清单">/)
assert.doesNotMatch(directMessagingTaskConfigFormSource, /<el-form-item v-else label="原生清单">/)
assert.match(directMessagingTaskConfigFormSource, /const rawInitialConfig = ref<Record<string, unknown>>\(\{\}\)/)
assert.match(directMessagingTaskConfigFormSource, /rawInitialConfig\.value = cloneRecord\(config\)/)
assert.match(directMessagingTaskConfigFormSource, /const rawConfig = cloneRecord\(rawInitialConfig\.value\)/)
assert.match(directMessagingTaskConfigFormSource, /removeManagedTopLevelFields\(rawConfig\)/)
assert.match(directMessagingTaskConfigFormSource, /passthrough: extractRulePassthrough\(record\)/)
assert.match(directMessagingTaskConfigFormSource, /const passthrough = cloneRecord\(rule\.passthrough\)/)
assert.match(directMessagingTaskConfigFormSource, /for \(const key of \['caption', 'imageCaption', 'caption_text'\]\) delete passthrough\[key\]/)
assert.match(directMessagingTaskConfigFormSource, /if \(!taskTypeChanged\) applyInitialConfig\(\)/)
assert.match(directMessagingTaskConfigFormSource, /function clampInteger\(value: unknown, fallback: number, min: number, max: number\): number \{\s*return clamp\(Math\.trunc\(positiveNumber\(value, fallback\)\), min, max\)/)
assert.match(directMessagingTaskConfigFormSource, /const cooldownSeconds = clampInteger\(form\.cooldownSeconds, 60, 0, 86400\)/)
assert.match(directMessagingTaskConfigFormSource, /const rolling24h = clampInteger\(form\.rolling24h, 20, 1, 1000\)/)
assert.match(directMessagingTaskConfigFormSource, /return validDraft\(isBatchTask\.value \? usernames\.length \+ dictionaryCount : 0, config\)/)
assert.match(directMessagingTaskConfigFormSource, /emit\('draft-changed', \{ \.\.\.next \}\)/)
assert.match(directMessagingTaskConfigFormSource, /if \(apiFailure\.value\) return invalidDraft\(apiFailure\.value\)/)
assert.match(directMessagingTaskConfigFormSource, /watch\(form, pushDraft, \{ deep: true, flush: 'post' \}\)/)
assert.match(directMessagingTaskConfigFormSource, /if \(sameDraft\(draft, next\)\) return/)
assert.match(directMessagingTaskConfigFormSource, /function sameDraft\(current: TaskConfigDraft, next: TaskConfigDraft\)/)
assert.match(tasksSource, /direct-messaging-task-dialog/)
assert.match(tasksSource, /height: min\(820px, calc\(100vh - 24px\)\);/)
assert.match(tasksSource, /scrollbar-gutter: stable;/)
assert.match(tasksSource, /if \(createDialog\.value\.form\.total !== draft\.total\) createDialog\.value\.form\.total = draft\.total/)
})
test('任务复制对模块任务走通用 JSON 新建表单', () => {
assert.match(tasksSource, /CopyDocument/)
assert.match(tasksSource, /<el-dropdown[^>]*trigger="click"[\s\S]*?<el-dropdown-menu>/)
assert.match(tasksSource, /<el-dropdown-item v-if="canCopyTask\(row\)"[\s\S]*?@click="copyTask\(row\)"[^>]*>复制<\/el-dropdown-item>/)
assert.match(tasksSource, /<el-dropdown-item v-if="!isDirectMessagingTask\(row\.taskType\) && canCopyScheduled\(row\)"[\s\S]*?@click="copyScheduledTask\(row\)"[^>]*>复制<\/el-dropdown-item>/)
assert.match(tasksSource, /function canCopyDefinition\(taskType: string\) \{\s*return !!taskType\.trim\(\) && !!taskDefinition\(taskType\)\s*\}/)
assert.match(tasksSource, /if \(createDialog\.value\.sourceTaskId > 0\) return ''/)
assert.match(tasksSource, /sourceDescription: `执行任务 #\$\{fullTask\.id\}`/)
assert.match(tasksSource, /sourceDescription: `计划任务 #\$\{fullTask\.id\}`/)
assert.match(tasksSource, /:model-value="createDialog\.sourceTaskId > 0 \? \['json'\] : \[\]"/)
assert.match(tasksSource, /:initial-config-json="createDialog\.form\.config"/)
})
test('宿主任务页面和通用表单不植入独立举报模块', () => {
assert.doesNotMatch(tasksSource, /user_message_report/)
assert.doesNotMatch(taskConfigFormSource, /user_message_report/)
for (const source of [tasksSource, taskConfigFormSource]) {
assert.doesNotMatch(source, /messageReport|BuildMessageReport|reportPresetName/)
}
})
test('账号编号输入说明和解析支持逗号与顿号', () => {
assert.match(taskConfigFormSource, /accountNumbersPlaceholder = '可选:每行一个,或用英文逗号、中文逗号、顿号分隔;如 #1,#2、#3'/)
assert.match(taskConfigFormSource, /:placeholder="accountNumbersPlaceholder"/)
assert.match(taskConfigFormSource, /\.split\(\/\[\\s,,、;\]\+\//)
})
test('自动更改登录邮箱支持多域名池和原域名避让说明', () => {
assert.match(taskConfigFormSource, /label="邮箱域名池"/)
assert.match(taskConfigFormSource, /domains,/)
assert.match(taskConfigFormSource, /domain: domains\[0\] \|\| null/)
assert.match(taskConfigFormSource, /function normalizeEmailDomains\(value: unknown\)/)
assert.match(taskConfigFormSource, /多个域名会优先避开账号当前登录邮箱掩码中的原域名/)
assert.match(tasksSource, /邮箱域名池: \$\{domainText\}/)
assert.match(tasksSource, /previous_login_email_domain/)
})
test('账号详情展示登录邮箱状态', () => {
assert.match(accountsSource, /<el-descriptions-item label="登录邮箱">/)
assert.match(accountsSource, /panelApi\.loginEmailStatus\(row\.id\)/)
assert.match(accountsSource, /function formatLoginEmailStatus\(status: LoginEmailStatus \| null\)/)
})
test('待执行或执行中任务编辑前必须经过暂停屏障', () => {
assert.match(
tasksSource,
/if \(status === 'paused'\) return true[\s\S]*?return \(status === 'pending' \|\| status === 'running'\) && def\.autoPauseBeforeEdit/,
)
const editStart = tasksSource.indexOf(" if (status === 'pending' || status === 'running') {")
const editEnd = tasksSource.indexOf(' const editRoute = resolveCreateTarget(def)', editStart)
assert.ok(editStart >= 0 && editEnd > editStart, '找不到 pending/running 编辑前的暂停屏障代码块')
const editBlock = tasksSource.slice(editStart, editEnd)
assert.match(editBlock, /if \(!def\.autoPauseBeforeEdit\)/)
assert.match(editBlock, /await ElMessageBox\.confirm\(/)
assert.match(editBlock, /await panelApi\.pauseTask\(task\.id\)/)
assert.match(editBlock, /await load\(\)/)
const confirmIndex = editBlock.indexOf('await ElMessageBox.confirm')
const pauseIndex = editBlock.indexOf('await panelApi.pauseTask(task.id)')
const reloadIndex = editBlock.indexOf('await load()')
assert.ok(confirmIndex < pauseIndex && pauseIndex < reloadIndex, '暂停屏障调用顺序必须为确认、暂停、刷新')
})
test('常驻任务使用后端执行通道、停止中状态和服务端重跑合同', () => {
assert.match(typesSource, /executionKind:\s*'batch' \| 'persistent' \| string/)
assert.match(typesSource, /requiresAttention:\s*boolean/)
assert.match(typesSource, /nextEligibleAtUtc\?:\s*string \| null/)
assert.match(typesSource, /canSchedule:\s*boolean/)
assert.match(tasksSource, /return task\.executionKind === 'persistent'/)
assert.match(tasksSource, /status === 'pausing'/)
assert.match(tasksSource, /status === 'initializing'\) return '正在初始化'/)
assert.match(tasksSource, /status === 'updating'\) return '正在提交配置'/)
assert.match(tasksSource, /下次领取时间/)
assert.match(tasksSource, /row\.requiresAttention/)
assert.match(tasksSource, /row\.runtimeMessage/)
assert.match(tasksSource, /await panelApi\.rerunTask\(task\.id\)/)
assert.match(tasksSource, /:disabled="!currentCreateDefinition\?\.canSchedule \|\| isDirectMessagingTask\(createDialog\.form\.taskType\)"/)
assert.doesNotMatch(tasksSource, /async function rerunTask[\s\S]*?await panelApi\.createTask\(/)
})
test('账号持续活跃支持回复消息与转发来源配置', () => {
assert.match(taskConfigFormSource, /messageActionMode:\s*'send_generated_text'/)
assert.match(taskConfigFormSource, /reply_to_message_url:/)
assert.doesNotMatch(taskConfigFormSource, /reply_to_message_id|replyToMessageId|回复消息 ID/)
assert.match(taskConfigFormSource, /forward_source_urls:/)
assert.match(taskConfigFormSource, /forward_mode:/)
assert.match(taskConfigFormSource, /skip_if_last_message_from_self:/)
assert.match(taskConfigFormSource, /skipIfLastMessageFromSelf:\s*false/)
assert.match(taskConfigFormSource, /去重发送/)
assert.match(taskConfigFormSource, /转发来源消息链接/)
assert.match(taskConfigFormSource, /<el-form-item label="转发来源消息链接" label-width="128px">/)
assert.match(tasksSource, /发送动作: \$\{isForwardMode \? '转发消息链接' : '发送消息规则'\}/)
assert.match(tasksSource, /去重发送: \$\{skipIfLastMessageFromSelf \? '启用/)
assert.match(taskConfigFormSource, /v-if="forms\.userChatActive\.messageActionMode === 'send_generated_text'" :span="8"/)
assert.match(taskConfigFormSource, /message_mode: effectiveMessageMode/)
assert.match(tasksSource, /\.\.\.\(isForwardMode \? \[\] : \[`内容模式:/)
assert.match(taskConfigFormSource, /也可单独填写一个文本字典变量,例如 \{forward_sources\}/)
assert.match(taskConfigFormSource, /validateUserChatActiveForwardSourceDictionaries\(forwardSourceUrls\)/)
assert.match(taskConfigFormSource, /account_queue_cursor: Math\.max\(0, form\.accountQueueCursor\)/)
})
test('账号持续活跃目标字段说明并校验文本字典变量', () => {
assert.match(taskConfigFormSource, /目标支持固定群组\/频道\/Bot 用户名\/链接,也支持单个文本字典变量/)
assert.match(taskConfigFormSource, /placeholder="每行一个固定目标,或单独填写一个文本字典变量,例如 \{groups\}"/)
assert.match(taskConfigFormSource, /支持文本字典变量:\{\{ targetVariableHint \}\}/)
assert.match(taskConfigFormSource, /function validateUserChatActiveTargetDictionaries\(targets: string\[\]\)/)
assert.match(taskConfigFormSource, /目标字典不能使用内置时间变量 \{time\}/)
assert.match(taskConfigFormSource, /不是已启用且有内容的文本字典/)
assert.match(taskConfigFormSource, /validateUserChatActiveTargetDictionaries\(targets\)/)
})
test('任务弹窗在手机端收窄并纵向排版', () => {
assert.match(tasksSource, /width="min\(760px, calc\(100vw - 24px\)\)"/)
assert.match(tasksSource, /width="min\(720px, calc\(100vw - 24px\)\)"/)
assert.match(tasksSource, /:label-position="isTaskDialogCompact \? 'top' : 'right'"/)
assert.match(tasksSource, /:label-width="isTaskDialogCompact \? 'auto' : '96px'"/)
assert.match(tasksSource, /v-model="editScheduledDialog\.visible"[\s\S]*?width="min\(760px, calc\(100vw - 24px\)\)"/)
assert.match(tasksSource, /v-model="editScheduledDialog\.visible"[\s\S]*?:label-position="isTaskDialogCompact \? 'top' : 'right'"/)
assert.match(tasksSource, /:class="\['task-dialog', \{ 'direct-messaging-task-dialog':/)
assert.match(tasksSource, /:global\(\.task-dialog\)/)
assert.match(tasksSource, /max-height: calc\(100vh - 24px\);/)
assert.match(tasksSource, /overflow-y: auto;/)
assert.match(tasksSource, /:global\(\.task-dialog \.el-dialog__footer\)/)
assert.match(tasksSource, /:xs="24" :sm="12"/)
assert.match(taskConfigFormSource, /@media \(max-width: 640px\)/)
assert.match(taskConfigFormSource, /grid-template-columns: 1fr;/)
assert.match(taskConfigFormSource, /:deep\(\.el-form-item__content\)/)
})
test('新建和编辑即时任务支持自定义任务名称', () => {
assert.match(typesSource, /name\?:\s*string \| null/)
assert.match(tasksSource, /<el-form-item v-if="!currentCreateTarget" label="任务名称">/)
assert.match(tasksSource, /placeholder="可选,留空则显示任务类型和 ID"/)
assert.match(tasksSource, /@change="onCreateModeChanged"/)
assert.match(tasksSource, /function onCreateModeChanged\(mode: string \| number \| boolean \| undefined\)/)
assert.match(tasksSource, /const taskDisplayName = form\.name\.trim\(\)/)
assert.match(tasksSource, /name:\s*taskDisplayName \|\| null/)
assert.match(tasksSource, /name:\s*fullTask\.name\?\.trim\(\) \|\| ''/)
assert.match(tasksSource, /name:\s*dialog\.form\.name\.trim\(\)/)
assert.match(tasksSource, /const created = await panelApi\.rerunTask\(task\.id\)/)
})
test('任务账号来源只能在分类和编号之间二选一', () => {
assert.match(taskConfigFormSource, /账号来源/)
assert.match(taskConfigFormSource, /账号分类选择/)
assert.match(taskConfigFormSource, /账号编号填写/)
assert.match(taskConfigFormSource, /accountSourceMode === 'category'/)
assert.match(taskConfigFormSource, /activeAccountSource\(form\)/)
assert.doesNotMatch(taskConfigFormSource, /与账号分类合并执行/)
assert.doesNotMatch(taskConfigFormSource, /请至少选择账号分类或填写账号编号/)
})
test('任务操作支持复制到新建表单而不是立即重跑', () => {
assert.match(tasksSource, /CopyDocument/)
assert.match(tasksSource, /<el-dropdown-item[^>]*@click="copyTask\(row\)"[^>]*>复制<\/el-dropdown-item>/)
assert.match(tasksSource, /<el-dropdown-item v-if="!isDirectMessagingTask\(row\.taskType\) && canCopyScheduled\(row\)"[^>]*@click="copyScheduledTask\(row\)"[^>]*>复制<\/el-dropdown-item>/)
assert.match(tasksSource, /initial-config-json="createDialog\.form\.config"/)
assert.match(tasksSource, /已复制任务 #\$\{createDialog\.sourceTaskId\} 的配置/)
assert.match(tasksSource, /function copyTask\(task: BatchTask\)/)
assert.match(tasksSource, /function copyScheduledTask\(task: ScheduledTask\)/)
assert.match(tasksSource, /function openCopiedCreateDialog/)
assert.match(tasksSource, /stripRuntimeFields\(fullTask\.taskType, fullTask\.config\)/)
})