Files
mimic/frontend/tests/MitreTechniqueTag.test.tsx
Knacky 771483f3b0 feat(frontend): sprint 3 — multi-technique MITRE selection + matrix modal
- types: replace mitre_technique_id/name scalars with techniques:MitreTechnique[]
  on Simulation; add MitreTactic/MitreMatrixTechnique/MitreMatrixSubtechnique;
  SimulationPatchInput now uses technique_ids:string[]
- api/mitre.ts: add getMitreMatrix() → GET /api/mitre/matrix
- hooks/useMitre: add useMitreMatrix(enabled) with staleTime:Infinity
- MitreTechniquePicker: clean rewrite — onSelect(technique) one-shot, resets
  input after selection, no incoming value props
- MitreTechniqueTag: chip component with id+name and × remove button
- MitreMatrixModal: tactic columns (220px fixed), expand/collapse subtechniques,
  search filter (auto-expands parent on sub match), selection state, focus trap
  (Tab wrap, Escape, search autofocus), backdrop click cancel, Apply N techniques
- MitreTechniquesField: orchestrates tags+picker+matrix with auto-save PATCH on
  every add/remove/Apply, dedup guard, disabled read-only mode for SOC
- SimulationFormPage: swap MitreTechniquePicker for MitreTechniquesField; remove
  technique state from RT form (techniques have independent auto-save cycle)
- SimulationList: MITRE column → T1059 +2 counter format, — when empty
- Tests: 84 passing (13 test files); new suites for Tag, Field, Modal;
  MitreTechniquePicker + SimulationFormPage + SimulationList adapted to new API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 04:04:23 +02:00

42 lines
1.5 KiB
TypeScript
Raw 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 { describe, expect, it, vi } from 'vitest';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MitreTechniqueTag } from '@/components/MitreTechniqueTag';
import { renderWithProviders } from './utils';
const TECHNIQUE = { id: 'T1059', name: 'Command and Scripting Interpreter', tactics: ['execution'] };
describe('MitreTechniqueTag', () => {
it('renders id and name', () => {
renderWithProviders(
<MitreTechniqueTag technique={TECHNIQUE} onRemove={vi.fn()} />,
);
expect(screen.getByText('T1059')).toBeInTheDocument();
expect(screen.getByText(/Command and Scripting Interpreter/)).toBeInTheDocument();
});
it('shows remove button when not disabled', () => {
renderWithProviders(
<MitreTechniqueTag technique={TECHNIQUE} onRemove={vi.fn()} />,
);
expect(screen.getByRole('button', { name: /Remove T1059/i })).toBeInTheDocument();
});
it('clicking × calls onRemove', async () => {
const onRemove = vi.fn();
const user = userEvent.setup();
renderWithProviders(
<MitreTechniqueTag technique={TECHNIQUE} onRemove={onRemove} />,
);
await user.click(screen.getByRole('button', { name: /Remove T1059/i }));
expect(onRemove).toHaveBeenCalledOnce();
});
it('hides remove button when disabled', () => {
renderWithProviders(
<MitreTechniqueTag technique={TECHNIQUE} onRemove={vi.fn()} disabled />,
);
expect(screen.queryByRole('button', { name: /Remove/i })).toBeNull();
});
});