P0-1/P0-2/P1-1: dimensions form + E2E tests + PDF export
P0-1 Backend: dimensions column on template entity + validation P0-1 Frontend: dimensions edit UI in TemplateManager P0-2: routeAfterGrading unit tests (10 cases), service spec fix + certificate tests, jest-e2e.json P1-1: proper PDF generation with embedded CJK font via pdf-lib low-level API
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { routeAfterGrading } from './builder';
|
||||
|
||||
describe('routeAfterGrading', () => {
|
||||
it('should route to interviewer when shouldFollowUp is true (overrides all other logic)', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: true,
|
||||
currentQuestionIndex: 0,
|
||||
questionCount: 5,
|
||||
questions: [],
|
||||
} as any);
|
||||
expect(result).toBe('interviewer');
|
||||
});
|
||||
|
||||
it('should route to generator when currentIndex >= questionsLen and currentIndex < targetCount', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 3,
|
||||
questionCount: 5,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }],
|
||||
} as any);
|
||||
expect(result).toBe('generator');
|
||||
});
|
||||
|
||||
it('should route to interviewer when currentIndex < questionsLen and currentIndex < targetCount', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 2,
|
||||
questionCount: 5,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }, { text: 'q4' }, { text: 'q5' }],
|
||||
} as any);
|
||||
expect(result).toBe('interviewer');
|
||||
});
|
||||
|
||||
it('should route to analyzer when currentIndex >= targetCount', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 5,
|
||||
questionCount: 5,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }, { text: 'q4' }, { text: 'q5' }],
|
||||
} as any);
|
||||
expect(result).toBe('analyzer');
|
||||
});
|
||||
|
||||
it('should use default targetCount of 5 when questionCount is undefined', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 4,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }, { text: 'q4' }],
|
||||
} as any);
|
||||
expect(result).toBe('generator');
|
||||
});
|
||||
|
||||
it('should use default targetCount of 5 when questionCount is undefined and index 5 routes to analyzer', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 5,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }, { text: 'q4' }, { text: 'q5' }],
|
||||
} as any);
|
||||
expect(result).toBe('analyzer');
|
||||
});
|
||||
|
||||
it('should handle undefined questions gracefully (defaults to empty array)', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 0,
|
||||
questionCount: 5,
|
||||
} as any);
|
||||
expect(result).toBe('generator');
|
||||
});
|
||||
|
||||
it('should prevent negative currentQuestionIndex via Math.max(0)', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: -1,
|
||||
questionCount: 5,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }, { text: 'q4' }, { text: 'q5' }],
|
||||
} as any);
|
||||
expect(result).toBe('interviewer');
|
||||
});
|
||||
|
||||
it('should handle completely empty state (no fields provided)', () => {
|
||||
const result = routeAfterGrading({} as any);
|
||||
expect(result).toBe('generator');
|
||||
});
|
||||
|
||||
it('should route to interviewer at last index before targetCount boundary', () => {
|
||||
const result = routeAfterGrading({
|
||||
shouldFollowUp: false,
|
||||
currentQuestionIndex: 4,
|
||||
questionCount: 5,
|
||||
questions: [{ text: 'q1' }, { text: 'q2' }, { text: 'q3' }, { text: 'q4' }, { text: 'q5' }],
|
||||
} as any);
|
||||
expect(result).toBe('interviewer');
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import { reportAnalyzerNode } from './nodes/analyzer.node';
|
||||
/**
|
||||
* Conditional routing logic for the Grader node.
|
||||
*/
|
||||
const routeAfterGrading = (state: typeof EvaluationAnnotation.State) => {
|
||||
export const routeAfterGrading = (state: typeof EvaluationAnnotation.State) => {
|
||||
const targetCount = state.questionCount || 5;
|
||||
const questionsLen = state.questions?.length || 0;
|
||||
const currentIndex = Math.max(0, state.currentQuestionIndex || 0);
|
||||
|
||||
Reference in New Issue
Block a user