import { describe, it, expect } from 'vitest'; import { render, screen } from '@testing-library/react'; import { BehaviouralPrimitivesPanel, type BehaviouralObservation, type AttributionPrimitiveState, } from './AttackerDetail'; const _attr = ( primitive: string, state: AttributionPrimitiveState['state'], confidence = 0.85, observation_count = 6, ): AttributionPrimitiveState => ({ primitive, current_value: 'x', state, confidence, observation_count, last_change_ts: 1714000000, last_observation_ts: 1714000300, }); const _obs = ( primitive: string, value: unknown, confidence = 0.85, ): BehaviouralObservation => ({ primitive, value, confidence, ts: 1714521660.456, source: 'test', }); describe('BehaviouralPrimitivesPanel', () => { it('renders an empty-state placeholder when no observations', () => { render(); expect(screen.getByTestId('behaviour-empty')).toBeInTheDocument(); }); it('places day-one priority primitives at the top of their group', () => { // Mix priority + non-priority primitives in arbitrary input order. const observations: BehaviouralObservation[] = [ _obs('motor.keystroke_cadence', 'steady'), _obs('cognitive.tool_vocabulary', 'broad'), _obs('motor.input_modality', 'typed'), // priority #1 _obs('cognitive.feedback_loop_engagement', 'closed_loop'), // priority #2 _obs('cognitive.command_branch_diversity', 'adaptive_branching'), // #3 _obs('cognitive.inter_command_latency_class', 'typing_speed'), // #4 _obs('motor.error_correction', 'immediate'), ]; render(); // motor group: input_modality must precede the alphabetised rest. const motorRows = Array.from( document.querySelectorAll('[data-testid^="behaviour-row-motor."]'), ).map((el) => el.getAttribute('data-testid')!); expect(motorRows[0]).toBe('behaviour-row-motor.input_modality'); // cognitive group: the three priority primitives must be in the // documented order at the top. const cogRows = Array.from( document.querySelectorAll('[data-testid^="behaviour-row-cognitive."]'), ).map((el) => el.getAttribute('data-testid')!); expect(cogRows.slice(0, 3)).toEqual([ 'behaviour-row-cognitive.feedback_loop_engagement', 'behaviour-row-cognitive.command_branch_diversity', 'behaviour-row-cognitive.inter_command_latency_class', ]); }); it('renders the primitive value and a confidence badge', () => { const observations: BehaviouralObservation[] = [ _obs('motor.input_modality', 'pasted', 0.91), ]; render(); const row = screen.getByTestId('behaviour-row-motor.input_modality'); expect(row.textContent).toContain('input_modality'); expect(row.textContent).toContain('pasted'); expect(row.textContent).toContain('91%'); }); it('renders attribution badges only for primitives in the map', () => { const observations: BehaviouralObservation[] = [ _obs('motor.input_modality', 'pasted', 0.91), _obs('cognitive.feedback_loop_engagement', 'closed_loop', 0.88), ]; const attribution = new Map([ ['motor.input_modality', _attr('motor.input_modality', 'stable', 0.95)], // cognitive.feedback_loop_engagement intentionally absent — // the panel must not render a badge for it. ]); render( , ); const badge = screen.getByTestId('attribution-badge-motor.input_modality'); expect(badge.textContent).toBe('STABLE'); expect(badge.getAttribute('data-state')).toBe('stable'); expect( screen.queryByTestId( 'attribution-badge-cognitive.feedback_loop_engagement', ), ).toBeNull(); }); it('renders each of the five frozen states with a distinct label', () => { const cases: [AttributionPrimitiveState['state'], string][] = [ ['stable', 'STABLE'], ['drifting', 'DRIFTING'], ['conflicted', 'CONFLICTED'], ['multi_actor', 'MULTI-ACTOR'], ['unknown', 'UNKNOWN'], ]; const observations: BehaviouralObservation[] = cases.map((_pair, i) => _obs(`motor.synthetic_${i}`, 'x'), ); const attribution = new Map( cases.map(([state], i) => [ `motor.synthetic_${i}`, _attr(`motor.synthetic_${i}`, state), ]), ); render( , ); for (const [state, label] of cases) { const idx = cases.findIndex(([s]) => s === state); const badge = screen.getByTestId( `attribution-badge-motor.synthetic_${idx}`, ); expect(badge.textContent).toBe(label); expect(badge.getAttribute('data-state')).toBe(state); } }); it('does not render badges when no attribution prop is provided', () => { const observations: BehaviouralObservation[] = [ _obs('motor.input_modality', 'pasted'), ]; render(); expect( screen.queryByTestId('attribution-badge-motor.input_modality'), ).toBeNull(); }); it('groups by top-level domain in the canonical order', () => { const observations: BehaviouralObservation[] = [ _obs('emotional_valence.arousal', 'medium_engaged'), _obs('temporal.session_duration', 'medium'), _obs('motor.input_modality', 'typed'), _obs('cognitive.cognitive_load', 'medium'), ]; render(); const groups = Array.from( document.querySelectorAll('[data-testid^="behaviour-group-"]'), ).map((el) => el.getAttribute('data-testid')!); expect(groups).toEqual([ 'behaviour-group-motor', 'behaviour-group-cognitive', 'behaviour-group-temporal', 'behaviour-group-emotional_valence', ]); }); });