470 lines
20 KiB
JavaScript
470 lines
20 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import test, { after } from 'node:test';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { createServer } from 'vite';
|
|
|
|
const vite = await createServer({
|
|
root: fileURLToPath(new URL('..', import.meta.url)),
|
|
configFile: false,
|
|
optimizeDeps: { noDiscovery: true },
|
|
server: { middlewareMode: true, hmr: false },
|
|
appType: 'custom'
|
|
});
|
|
|
|
after(async () => {
|
|
await vite.close();
|
|
});
|
|
|
|
const supervisorView = await vite.ssrLoadModule('/src/services/supervisor-view.ts');
|
|
const {
|
|
actionablePracticeAlerts,
|
|
buildSupervisorMetrics,
|
|
buildTeamProfiles,
|
|
buildTrainingTrend,
|
|
createAsyncGenerationGuard,
|
|
filterReviewRecords,
|
|
filterTeamProfiles,
|
|
inferSupervisorScenarioId,
|
|
isReviewEditable,
|
|
isSupervisorAccessDenied,
|
|
paginateTeamProfiles,
|
|
reviewCompletionAction,
|
|
supervisorScenarioOptions
|
|
} = supervisorView;
|
|
const { maskPhone } = await vite.ssrLoadModule('/src/services/auth.ts');
|
|
|
|
const assignPageSource = await readFile(
|
|
new URL('../src/pages/supervisor/assign/index.vue', import.meta.url),
|
|
'utf8'
|
|
);
|
|
const reviewPageSource = await readFile(
|
|
new URL('../src/pages/supervisor/review/index.vue', import.meta.url),
|
|
'utf8'
|
|
);
|
|
const dashboardPageSource = await readFile(
|
|
new URL('../src/pages/supervisor/index/index.vue', import.meta.url),
|
|
'utf8'
|
|
);
|
|
const teamPageSource = await readFile(
|
|
new URL('../src/pages/supervisor/team/index.vue', import.meta.url),
|
|
'utf8'
|
|
);
|
|
const reviewsPageSource = await readFile(
|
|
new URL('../src/pages/supervisor/reviews/index.vue', import.meta.url),
|
|
'utf8'
|
|
);
|
|
const practiceServiceSource = await readFile(
|
|
new URL('../src/services/practice.ts', import.meta.url),
|
|
'utf8'
|
|
);
|
|
|
|
const review = (overrides = {}) => ({
|
|
id: 1,
|
|
extPartyId: '13800138000',
|
|
time: '2026-07-10 09:00:00',
|
|
trainee: '张三',
|
|
scene: '投诉接待',
|
|
score: 86,
|
|
status: '待复盘',
|
|
summary: '训练摘要',
|
|
...overrides
|
|
});
|
|
|
|
const assignment = (overrides = {}) => ({
|
|
id: 1,
|
|
extPartyId: '13800138000',
|
|
traineeName: '张三',
|
|
scenarioId: 'complaint-water',
|
|
scenarioName: '投诉接待',
|
|
source: 'manual',
|
|
reason: '主管指派',
|
|
status: '待训练',
|
|
createTime: '2026-07-10 09:00:00',
|
|
...overrides
|
|
});
|
|
|
|
const alert = (overrides = {}) => ({
|
|
type: 'low_score',
|
|
title: '低分训练预警',
|
|
desc: '0 条训练低于 80 分',
|
|
severity: 'high',
|
|
...overrides
|
|
});
|
|
|
|
test('daily 任务不计入主管待训练指标,也不创建团队成员', () => {
|
|
const assignments = [
|
|
assignment({ id: 1, extPartyId: 'daily-user', traineeName: '每日任务员工', source: 'daily' }),
|
|
assignment({ id: 2, extPartyId: 'manual-user', traineeName: '专项员工' }),
|
|
assignment({ id: 3, extPartyId: 'done-user', traineeName: '已完成员工', status: '已完成' })
|
|
];
|
|
|
|
const metrics = buildSupervisorMetrics([], assignments, []);
|
|
const profiles = buildTeamProfiles([], assignments);
|
|
|
|
assert.equal(metrics.find((item) => item.key === 'pendingAssignments')?.value, 1);
|
|
assert.deepEqual(profiles.map((item) => item.extPartyId).sort(), ['done-user', 'manual-user']);
|
|
});
|
|
|
|
test('低分指标只取 alerts 的后端聚合值', () => {
|
|
const metrics = buildSupervisorMetrics(
|
|
[review({ score: 32 })],
|
|
[],
|
|
[alert({ desc: '7 条训练低于 80 分,建议派发定向再练' })]
|
|
);
|
|
|
|
assert.equal(metrics.find((item) => item.key === 'lowScores')?.value, 7);
|
|
assert.equal(buildSupervisorMetrics([review({ score: 32 })], [], [])
|
|
.find((item) => item.key === 'lowScores')?.value, 0);
|
|
});
|
|
|
|
test('按 extPartyId 聚合复盘和专项,并区分同名成员', () => {
|
|
const profiles = buildTeamProfiles(
|
|
[
|
|
review({ id: 1, extPartyId: '13800138000', trainee: '张三', score: 76 }),
|
|
review({ id: 2, extPartyId: '13800138000', trainee: '张三', score: 91, status: '已复盘' })
|
|
],
|
|
[
|
|
assignment({ id: 1, extPartyId: '13800138000', traineeName: '张三' }),
|
|
assignment({ id: 2, extPartyId: '13900139000', traineeName: '张三', status: '已完成' })
|
|
]
|
|
);
|
|
|
|
assert.equal(profiles.length, 2);
|
|
assert.deepEqual(
|
|
profiles.map((item) => item.displayName).sort(),
|
|
['张三 · 尾号8000', '张三 · 尾号9000'].sort()
|
|
);
|
|
const first = profiles.find((item) => item.extPartyId === '13800138000');
|
|
assert.equal(first?.pendingReview, 1);
|
|
assert.equal(first?.pendingAssignment, 1);
|
|
assert.equal(first?.latestScore, 76);
|
|
assert.equal(first?.latestScene, '投诉接待');
|
|
});
|
|
|
|
test('低于 80 分为高风险,有待复盘为中风险,其余为低风险', () => {
|
|
const profiles = buildTeamProfiles(
|
|
[
|
|
review({ id: 1, extPartyId: 'high', trainee: '高风险', score: 79 }),
|
|
review({ id: 2, extPartyId: 'medium', trainee: '中风险', score: 88 }),
|
|
review({ id: 3, extPartyId: 'low', trainee: '低风险', score: 95, status: '已复盘' })
|
|
],
|
|
[]
|
|
);
|
|
|
|
assert.equal(profiles.find((item) => item.extPartyId === 'high')?.risk, 'high');
|
|
assert.equal(profiles.find((item) => item.extPartyId === 'medium')?.risk, 'medium');
|
|
assert.equal(profiles.find((item) => item.extPartyId === 'low')?.risk, 'low');
|
|
});
|
|
|
|
test('待复盘列表支持全部、低分和待处理筛选', () => {
|
|
const rows = [
|
|
review({ id: 1, score: 68, status: '待复盘' }),
|
|
review({ id: 2, score: 88, status: '待复盘' }),
|
|
review({ id: 3, score: 92, status: '已复盘' })
|
|
];
|
|
|
|
assert.equal(filterReviewRecords(rows, 'all').length, 3);
|
|
assert.deepEqual(filterReviewRecords(rows, 'low').map((item) => item.id), [1]);
|
|
assert.deepEqual(filterReviewRecords(rows, 'pending').map((item) => item.id), [1, 2]);
|
|
assert.match(reviewsPageSource, /reviewFilters/);
|
|
assert.match(reviewsPageSource, /visibleReviews/);
|
|
});
|
|
|
|
test('主管页面共享三个业务场景及文本推荐规则', () => {
|
|
assert.deepEqual(supervisorScenarioOptions.map((item) => item.id), [
|
|
'fee-parking',
|
|
'complaint-water',
|
|
'repair-elevator-delay'
|
|
]);
|
|
assert.match(practiceServiceSource, /getPracticeScenarios/);
|
|
assert.match(assignPageSource, /v-for="item in filteredScenarioOptions"/);
|
|
assert.match(assignPageSource, /scenarioSearch/);
|
|
assert.match(assignPageSource, /暂无可派发的已启用场景/);
|
|
assert.match(reviewPageSource, /scenarioOptions/);
|
|
assert.match(reviewPageSource, /scenarioOptions\.value = \[\]/);
|
|
assert.equal(inferSupervisorScenarioId('电梯故障沟通低分'), 'repair-elevator-delay');
|
|
assert.equal(inferSupervisorScenarioId('投诉接待需要补练'), 'complaint-water');
|
|
assert.equal(inferSupervisorScenarioId('催缴物业费'), 'fee-parking');
|
|
});
|
|
|
|
test('stable 提示不计入团队预警指标', () => {
|
|
const alerts = [
|
|
alert({ type: 'stable', title: '团队状态稳定', desc: '当前无新增风险', severity: 'low' }),
|
|
alert({ type: 'pending_review', title: '待复盘积压', desc: '2 条对练等待主管复盘', severity: 'medium' })
|
|
];
|
|
const metrics = buildSupervisorMetrics([], [], alerts);
|
|
const alertMetric = metrics.find((item) => item.key === 'alerts');
|
|
|
|
assert.equal(alertMetric?.value, 1);
|
|
assert.equal(alertMetric?.desc, '请及时跟进风险项');
|
|
assert.deepEqual(actionablePracticeAlerts(alerts).map((item) => item.type), ['pending_review']);
|
|
assert.deepEqual(alerts.map((item) => item.type), ['stable', 'pending_review']);
|
|
assert.equal(buildSupervisorMetrics([], [], [
|
|
alert({ type: 'stable', title: '团队状态稳定', desc: '当前无新增风险', severity: 'low' })
|
|
]).find((item) => item.key === 'alerts')?.value, 0);
|
|
});
|
|
|
|
test('主管工作台展示真实训练趋势并压缩预警首屏密度', () => {
|
|
const trend = buildTrainingTrend([
|
|
review({ id: 1, time: '2026-07-15 09:00:00', score: 70 }),
|
|
review({ id: 2, time: '2026-07-16 09:00:00', score: 82 }),
|
|
review({ id: 3, time: '2026-07-17 09:00:00', score: 91 })
|
|
]);
|
|
|
|
assert.deepEqual(trend.map((item) => item.value), [70, 82, 91]);
|
|
assert.match(dashboardPageSource, /class="panel training-trend-panel"/);
|
|
assert.match(dashboardPageSource, /trainingTrend/);
|
|
assert.match(dashboardPageSource, /visibleAlerts/);
|
|
assert.doesNotMatch(dashboardPageSource, /<text class="metric-desc"/);
|
|
});
|
|
|
|
test('画像按时间显示最新训练,但任一未复盘低分仍保持高风险', () => {
|
|
const profiles = buildTeamProfiles([
|
|
review({
|
|
id: 1,
|
|
time: '2026-07-08 09:00:00',
|
|
scene: '旧低分场景',
|
|
score: 62,
|
|
status: '待复盘'
|
|
}),
|
|
review({
|
|
id: 2,
|
|
time: '2026-07-10 10:30:00',
|
|
scene: '新高分场景',
|
|
score: 93,
|
|
status: '已复盘'
|
|
})
|
|
], []);
|
|
const profile = profiles[0];
|
|
|
|
assert.equal(profile.latestScore, 93);
|
|
assert.equal(profile.latestScoreText, '93分');
|
|
assert.equal(profile.latestScene, '新高分场景');
|
|
assert.equal(profile.lastPracticeAt, '2026-07-10 10:30:00');
|
|
assert.equal(profile.risk, 'high');
|
|
assert.match(profile.reason, /未复盘低分/);
|
|
});
|
|
|
|
test('组织快照中的稳定成员即使没有训练记录也保留在画像中', () => {
|
|
const profiles = buildTeamProfiles([], [], [
|
|
{ extPartyId: 'stable-1', name: '稳定员工' }
|
|
]);
|
|
|
|
assert.equal(profiles.length, 1);
|
|
assert.equal(profiles[0].extPartyId, 'stable-1');
|
|
assert.equal(profiles[0].latestScoreText, '暂无评分');
|
|
assert.equal(profiles[0].risk, 'low');
|
|
});
|
|
|
|
test('团队画像搜索和分页不把 2316 名成员一次性渲染', () => {
|
|
const profiles = Array.from({ length: 2316 }, (_, index) => ({
|
|
key: `member-${index}`,
|
|
extPartyId: `employee-${index}`,
|
|
displayName: `员工 ${index}`
|
|
}));
|
|
|
|
assert.equal(paginateTeamProfiles(profiles, 1, 20).items.length, 20);
|
|
assert.equal(paginateTeamProfiles(profiles, 1, 20).pageCount, 116);
|
|
assert.deepEqual(
|
|
filterTeamProfiles(profiles, '员工 2315').map((item) => item.key),
|
|
['member-2315']
|
|
);
|
|
assert.match(teamPageSource, /v-for="member in visibleTeamProfiles"/);
|
|
assert.match(teamPageSource, /v-if="selectedMember" class="panel member-detail-panel"/);
|
|
assert.match(teamPageSource, /<template v-else>[\s\S]*class="panel team-panel"/);
|
|
});
|
|
|
|
test('员工和主管个人页只展示脱敏手机号', async () => {
|
|
const [employeeProfile, supervisorProfile] = await Promise.all([
|
|
readFile(new URL('../src/pages/user/profile/index.vue', import.meta.url), 'utf8'),
|
|
readFile(new URL('../src/pages/supervisor/profile/index.vue', import.meta.url), 'utf8')
|
|
]);
|
|
|
|
assert.equal(maskPhone('181368856126'), '181****6126');
|
|
assert.equal(maskPhone('invalid'), 'invalid');
|
|
assert.match(employeeProfile, /displayPhone/);
|
|
assert.match(supervisorProfile, /displayPhone/);
|
|
assert.doesNotMatch(employeeProfile, /\{\{ phone \|\|/);
|
|
assert.doesNotMatch(supervisorProfile, /\{\{ phone \|\|/);
|
|
});
|
|
|
|
test('主管个人工作台展示真实指标和三项业务入口', async () => {
|
|
const source = await readFile(new URL('../src/pages/supervisor/profile/index.vue', import.meta.url), 'utf8');
|
|
|
|
assert.match(source, /\{\{ loading \? '--' : teamCount \}\}/);
|
|
assert.match(source, /\{\{ loading \? '--' : pendingReviewCount \}\}/);
|
|
assert.match(source, /\{\{ loading \? '--' : alertCount \}\}/);
|
|
assert.match(source, /主管业务入口/);
|
|
assert.match(source, /\/pages\/supervisor\/team\/index/);
|
|
assert.match(source, /\/pages\/supervisor\/reviews\/index/);
|
|
assert.match(source, /\/pages\/supervisor\/assign\/index/);
|
|
assert.match(source, /getPracticeTeamSnapshot/);
|
|
});
|
|
|
|
test('组织快照姓名不可读时用稳定身份标识展示', () => {
|
|
const profiles = buildTeamProfiles([], [], [
|
|
{ extPartyId: '13900001111', name: '???????' },
|
|
{ extPartyId: 'employee-project-A-000123', name: '????????A_000123' }
|
|
]);
|
|
|
|
assert.deepEqual(
|
|
profiles.map((item) => item.displayName).sort(),
|
|
['员工 尾号1111', '员工 末尾000123'].sort()
|
|
);
|
|
assert.equal(profiles.every((item) => !item.name.includes('?')), true);
|
|
assert.equal(profiles.every((item) => !item.reason.includes('?')), true);
|
|
});
|
|
|
|
test('只有待复盘可编辑,成功返回仅在上一页为 reviews 时后退', () => {
|
|
assert.equal(isReviewEditable('待复盘'), true);
|
|
assert.equal(isReviewEditable('已复盘'), false);
|
|
assert.equal(reviewCompletionAction('pages/supervisor/reviews/index'), 'back');
|
|
assert.equal(reviewCompletionAction('/pages/supervisor/reviews/index'), 'back');
|
|
assert.equal(reviewCompletionAction('pages/supervisor/index/index'), 'replace');
|
|
assert.equal(reviewCompletionAction(undefined), 'replace');
|
|
});
|
|
|
|
test('异步代次守卫拒绝刷新后的旧结果和组件销毁后的结果', () => {
|
|
const guard = createAsyncGenerationGuard();
|
|
const audioGeneration = guard.capture();
|
|
|
|
assert.equal(guard.isCurrent(audioGeneration), true);
|
|
guard.invalidate();
|
|
assert.equal(guard.isCurrent(audioGeneration), false);
|
|
|
|
const historyGeneration = guard.begin();
|
|
assert.equal(guard.isCurrent(historyGeneration), true);
|
|
guard.dispose();
|
|
assert.equal(guard.isCurrent(historyGeneration), false);
|
|
});
|
|
|
|
test('只把主管权限错误识别为访问拒绝', () => {
|
|
assert.equal(isSupervisorAccessDenied(new Error('无主管权限,无法访问主管功能')), true);
|
|
assert.equal(isSupervisorAccessDenied('当前账号无主管权限'), true);
|
|
assert.equal(isSupervisorAccessDenied(new Error('登录已过期,请重新登录')), false);
|
|
assert.equal(isSupervisorAccessDenied(new Error('网络请求失败')), false);
|
|
assert.equal(isSupervisorAccessDenied({ message: '无主管权限' }), false);
|
|
assert.equal(isSupervisorAccessDenied(null), false);
|
|
});
|
|
|
|
test('复盘详情只允许待复盘提交并保留未保存草稿', () => {
|
|
assert.match(reviewPageSource, /const editable = computed\(\(\) => isReviewEditable\(detail\.value\?\.status\)\)/);
|
|
assert.match(reviewPageSource, /v-if="editable" class="panel review-form"/);
|
|
assert.match(reviewPageSource, /v-else class="panel review-complete"/);
|
|
assert.match(reviewPageSource, /if \(!editable\.value\)[\s\S]*该记录已完成复盘/);
|
|
assert.match(reviewPageSource, /onShow\(\(\) => \{[\s\S]*refreshAuthState\(\);[\s\S]*if \(!loggedIn\.value\) \{[\s\S]*accessDenied\.value = false;[\s\S]*clearDetail\(\);\s*return;\s*\}[\s\S]*if \(!detail\.value && !loading\.value\)[\s\S]*loadDetail\(\)/);
|
|
assert.match(reviewPageSource, /训练摘要/);
|
|
assert.match(reviewPageSource, /录音与逐句复盘/);
|
|
assert.match(reviewPageSource, /SOP 依据与改进建议/);
|
|
assert.match(reviewPageSource, /下一步行动/);
|
|
});
|
|
|
|
test('复盘详情登录和完成导航不堆叠页面', () => {
|
|
assert.match(reviewPageSource, /rememberLoginRedirect\(currentPath\(\)\);\s*uni\.redirectTo/);
|
|
assert.match(reviewPageSource, /reviewCompletionAction\(previousRoute\)/);
|
|
assert.match(reviewPageSource, /uni\.navigateBack\(\{ delta: 1, fail: goToReviews \}\)/);
|
|
assert.match(reviewPageSource, /const goToReviews = \(\) => \{\s*uni\.redirectTo/);
|
|
});
|
|
|
|
test('复盘音频晚到结果被释放且不会写入页面状态', () => {
|
|
assert.match(reviewPageSource, /const audioRequests = createAsyncGenerationGuard\(\)/);
|
|
assert.match(reviewPageSource, /const generation = audioRequests\.capture\(\)/);
|
|
assert.match(reviewPageSource, /if \(!audioRequests\.isCurrent\(generation\)\) \{\s*releaseAuthenticatedFileUrl\(source\);\s*return;/);
|
|
assert.match(reviewPageSource, /audioRequests\.dispose\(\)/);
|
|
});
|
|
|
|
test('指派页登录失效清空私密状态且登录跳转不携带草稿', () => {
|
|
assert.match(assignPageSource, /const clearPrivateState = \(\) => \{[\s\S]*resetDraft\(\);[\s\S]*assignments\.value = \[\]/);
|
|
assert.match(assignPageSource, /if \(!loggedIn\.value\) return '选择员工并配置训练任务'/);
|
|
assert.match(assignPageSource, /rememberLoginRedirect\(basePath\);\s*uni\.redirectTo/);
|
|
assert.doesNotMatch(assignPageSource, /const loginRedirect =/);
|
|
});
|
|
|
|
test('指派中锁定返回和刷新,写入成功后强制启动新历史读取', () => {
|
|
assert.match(assignPageSource, /class="subpage-back-button"[\s\S]*:disabled="assigning"/);
|
|
assert.match(assignPageSource, /:disabled="historyLoading \|\| assigning"\s*@click="loadAssignments\(\)"/);
|
|
assert.match(assignPageSource, /const loadAssignments = async \(force = false\)/);
|
|
assert.match(assignPageSource, /const generation = historyRequests\.begin\(\)/);
|
|
assert.match(assignPageSource, /await loadAssignments\(true\)/);
|
|
assert.match(assignPageSource, /const goBack = \(\) => \{\s*if \(assigning\.value\) return;/);
|
|
});
|
|
|
|
test('指派页销毁时废止历史请求,晚到结果不能写回', () => {
|
|
assert.match(assignPageSource, /import \{ computed, onBeforeUnmount, ref \} from 'vue'/);
|
|
assert.match(assignPageSource, /onBeforeUnmount\(\(\) => \{\s*historyRequests\.dispose\(\);\s*\}\)/);
|
|
});
|
|
|
|
test('五个主管页面优先显示统一无权限状态并清理私密数据', () => {
|
|
const pages = [
|
|
{ source: dashboardPageSource, clear: 'clearData' },
|
|
{ source: teamPageSource, clear: 'clearData' },
|
|
{ source: assignPageSource, clear: 'clearPrivateState' },
|
|
{ source: reviewsPageSource, clear: 'clearData' },
|
|
{ source: reviewPageSource, clear: 'clearDetail' }
|
|
];
|
|
|
|
pages.forEach(({ source, clear }) => {
|
|
assert.match(source, /const accessDenied = ref\(false\)/);
|
|
assert.match(source, /<view v-if="accessDenied" class="panel supervisor-access-panel">[\s\S]*无主管权限[\s\S]*当前账号不是主管或项目经理,无法查看团队训练数据。[\s\S]*@click="goToEmployee"[\s\S]*返回员工端[\s\S]*@click="switchAccount"[\s\S]*切换账号[\s\S]*<\/view>\s*<view v-else-if="!loggedIn"/);
|
|
assert.match(source, new RegExp(`if \\(isSupervisorAccessDenied\\(error\\)\\) \\{[\\s\\S]*${clear}\\(\\);[\\s\\S]*accessDenied\\.value = true;[\\s\\S]*return;`));
|
|
assert.match(source, /if \(!loggedIn\.value\) \{[\s\S]*accessDenied\.value = false;/);
|
|
assert.match(source, /accessDenied\.value = false;[\s\S]*(?:snapshot|nextReviews|detail\.value|nextAssignments)/);
|
|
assert.match(source, /const goToEmployee = \(\) => \{\s*uni\.switchTab\(\{ url: '\/pages\/user\/today\/index' \}\);\s*\};/);
|
|
assert.match(source, /const switchAccount = \(\) => \{\s*clearAuth\(\);\s*rememberLoginRedirect\([\s\S]*?\);\s*uni\.redirectTo\(\{ url: '\/pages\/auth\/login\/index' \}\);\s*\};/);
|
|
});
|
|
});
|
|
|
|
test('指派页通过团队快照在进入时校验主管权限', () => {
|
|
assert.match(assignPageSource, /getPracticeTeamSnapshot/);
|
|
assert.match(assignPageSource, /getPracticeTeamSnapshot\(\)/);
|
|
assert.match(assignPageSource, /assignments\.value = snapshot\.assignments/);
|
|
assert.doesNotMatch(assignPageSource, /getPracticeAssignments/);
|
|
});
|
|
|
|
test('指派表单和派发进度使用同页分段视图避免堆成长页', () => {
|
|
assert.match(assignPageSource, /assignView/);
|
|
assert.match(assignPageSource, /v-if="assignView === 'create'" class="panel assignment-form"/);
|
|
assert.match(assignPageSource, /v-if="assignView === 'progress'" class="panel history-panel"/);
|
|
assert.match(assignPageSource, /class="assign-tabs"/);
|
|
assert.match(assignPageSource, /memberSuggestions/);
|
|
assert.match(assignPageSource, /selectTarget/);
|
|
assert.match(assignPageSource, /class="target-suggestions"/);
|
|
assert.match(assignPageSource, /assignmentDueTime/);
|
|
assert.match(assignPageSource, /assignmentGoal/);
|
|
assert.match(assignPageSource, /assignmentRequirements/);
|
|
assert.match(assignPageSource, /item\.dueTime/);
|
|
});
|
|
|
|
test('指派页保留 H5 查询参数二次解码', () => {
|
|
assert.match(assignPageSource, /decodeURIComponent\(value\)\.trim\(\)/);
|
|
});
|
|
|
|
test('主管 dashboard 和 team 使用团队快照,reviews 继续使用待复盘接口', () => {
|
|
assert.match(practiceServiceSource, /export const getPracticeTeamSnapshot = \(\) =>/);
|
|
assert.match(practiceServiceSource, /url: '\/api\/aihr\/mobile\/practice\/team'/);
|
|
assert.match(dashboardPageSource, /getPracticeTeamSnapshot/);
|
|
assert.match(dashboardPageSource, /buildTeamProfiles\(records\.value, assignments\.value, members\.value\)/);
|
|
assert.match(teamPageSource, /getPracticeTeamSnapshot/);
|
|
assert.match(teamPageSource, /buildTeamProfiles\(records\.value, assignments\.value, members\.value\)/);
|
|
assert.match(reviewsPageSource, /getPracticeReviews/);
|
|
assert.doesNotMatch(reviewsPageSource, /getPracticeTeamSnapshot/);
|
|
});
|
|
|
|
test('五个主管页面登录都使用 redirectTo 避免返回栈重复', () => {
|
|
const pages = [
|
|
dashboardPageSource,
|
|
teamPageSource,
|
|
reviewsPageSource,
|
|
assignPageSource,
|
|
reviewPageSource
|
|
];
|
|
|
|
pages.forEach((source) => {
|
|
const goLogin = source.match(/const goLogin = \(\) => \{\n[\s\S]*?\n\};/)?.[0] || '';
|
|
assert.match(goLogin, /rememberLoginRedirect\(/);
|
|
assert.match(goLogin, /uni\.redirectTo\(\{ url: '\/pages\/auth\/login\/index' \}\)/);
|
|
assert.doesNotMatch(goLogin, /uni\.navigateTo\(/);
|
|
});
|
|
});
|