133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
/**
|
|
* Guard tests for the scenario-briefing visibility gate.
|
|
*
|
|
* The right-column briefing tab is SCENARIO ONLY and must appear exactly when
|
|
* the learner has ENTERED the scenario (prep completed) and stay visible for
|
|
* the rest of the run — never for ordinary projects, never during prep. These
|
|
* lock the five states that matter so the gate can't silently regress.
|
|
*/
|
|
import { describe, expect, it } from 'vitest';
|
|
import { shouldShowScenarioBriefing } from '@/components/scene-renderers/pbl/v2/scenario-briefing-gate';
|
|
import type { PBLMilestone, PBLProjectV2, PBLScenarioConfig } from '@/lib/pbl/v2/types';
|
|
|
|
const NOW = '2026-06-17T00:00:00.000Z';
|
|
|
|
function microtask(status: PBLMilestone['microtasks'][number]['status']) {
|
|
return {
|
|
id: 'mt',
|
|
title: 't',
|
|
description: 'd',
|
|
status,
|
|
assignee: 'user' as const,
|
|
hints: [],
|
|
order: 0,
|
|
};
|
|
}
|
|
|
|
function milestone(
|
|
id: string,
|
|
order: number,
|
|
status: PBLMilestone['status'],
|
|
scenarioStage?: PBLMilestone['scenarioStage'],
|
|
): PBLMilestone {
|
|
return {
|
|
id,
|
|
title: id,
|
|
order,
|
|
status,
|
|
description: 'd',
|
|
microtasks: [microtask(status === 'completed' ? 'completed' : 'in_progress')],
|
|
documents: [],
|
|
...(scenarioStage ? { scenarioStage } : {}),
|
|
};
|
|
}
|
|
|
|
const scenarioConfig: PBLScenarioConfig = {
|
|
setting: 'A clinic',
|
|
characters: [{ id: 'c1', name: '小明', persona: 'a kid', situation: 'has a cold' }],
|
|
};
|
|
|
|
function makeProject(args: {
|
|
scenario?: PBLScenarioConfig;
|
|
milestones: PBLMilestone[];
|
|
status?: PBLProjectV2['status'];
|
|
}): PBLProjectV2 {
|
|
return {
|
|
uiPhase: 'workspace',
|
|
title: 'P',
|
|
description: 'd',
|
|
learningObjective: 'o',
|
|
proficiency: 'beginner',
|
|
language: 'zh-CN',
|
|
tags: [],
|
|
status: args.status ?? 'active',
|
|
roles: [{ id: 'role-i', type: 'instructor', name: 'Instructor' }],
|
|
milestones: args.milestones,
|
|
submissions: [],
|
|
evaluations: [],
|
|
threads: [{ agentId: 'role-i', messages: [] }],
|
|
engagementEvents: [],
|
|
createdAt: NOW,
|
|
updatedAt: NOW,
|
|
...(args.scenario ? { scenario: args.scenario } : {}),
|
|
};
|
|
}
|
|
|
|
describe('shouldShowScenarioBriefing — right-column briefing gate', () => {
|
|
it('is FALSE for ordinary (non-scenario) projects, regardless of milestone state', () => {
|
|
const project = makeProject({
|
|
milestones: [milestone('ms-1', 0, 'completed'), milestone('ms-2', 1, 'active')],
|
|
});
|
|
expect(shouldShowScenarioBriefing(project)).toBe(false);
|
|
});
|
|
|
|
it('is FALSE for a scenario project still in prep (not yet entered)', () => {
|
|
const project = makeProject({
|
|
scenario: scenarioConfig,
|
|
milestones: [
|
|
milestone('prep', 0, 'active', 'prep'),
|
|
milestone('rp', 1, 'locked', 'roleplay'),
|
|
milestone('wrap', 2, 'locked', 'wrapup'),
|
|
],
|
|
});
|
|
expect(shouldShowScenarioBriefing(project)).toBe(false);
|
|
});
|
|
|
|
it('is TRUE once prep is completed and a roleplay stage is active (entered)', () => {
|
|
const project = makeProject({
|
|
scenario: scenarioConfig,
|
|
milestones: [
|
|
milestone('prep', 0, 'completed', 'prep'),
|
|
milestone('rp', 1, 'active', 'roleplay'),
|
|
milestone('wrap', 2, 'locked', 'wrapup'),
|
|
],
|
|
});
|
|
expect(shouldShowScenarioBriefing(project)).toBe(true);
|
|
});
|
|
|
|
it('stays TRUE during a stage handover gap (no active milestone)', () => {
|
|
const project = makeProject({
|
|
scenario: scenarioConfig,
|
|
milestones: [
|
|
milestone('prep', 0, 'completed', 'prep'),
|
|
milestone('rp', 1, 'completed', 'roleplay'),
|
|
milestone('wrap', 2, 'locked', 'wrapup'),
|
|
],
|
|
});
|
|
expect(shouldShowScenarioBriefing(project)).toBe(true);
|
|
});
|
|
|
|
it('stays TRUE on a fully completed project (e.g. returning from the completion page)', () => {
|
|
const project = makeProject({
|
|
scenario: scenarioConfig,
|
|
status: 'completed',
|
|
milestones: [
|
|
milestone('prep', 0, 'completed', 'prep'),
|
|
milestone('rp', 1, 'completed', 'roleplay'),
|
|
milestone('wrap', 2, 'completed', 'wrapup'),
|
|
],
|
|
});
|
|
expect(shouldShowScenarioBriefing(project)).toBe(true);
|
|
});
|
|
});
|