diff --git a/frontend/src/components/TemplatePickerModal.tsx b/frontend/src/components/TemplatePickerModal.tsx index 4ca9452..e8e4cfb 100644 --- a/frontend/src/components/TemplatePickerModal.tsx +++ b/frontend/src/components/TemplatePickerModal.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from 'react-i18next'; import { extractApiError } from '@/api/client'; import type { SimulationTemplate } from '@/api/types'; import { useTemplates } from '@/hooks/useTemplates'; @@ -23,6 +24,7 @@ export function TemplatePickerModal({ onSelectTemplate, isPending = false, }: TemplatePickerModalProps): JSX.Element { + const { t } = useTranslation(); const { data, isLoading, isError, error, refetch } = useTemplates(); return ( @@ -37,11 +39,11 @@ export function TemplatePickerModal({
| Name | -MITRE | -Created by | +{t('template.list.col.name')} | +{t('simulation.list.col.mitre')} | +{t('template.list.col.createdBy')} | ||||
|---|---|---|---|---|---|---|---|---|---|
| {t.name} | -{mitreCount(t)} | -{t.created_by.username} | +{template.name} | +{mitreCount(template)} | +{template.created_by.username} |
| Name | -MITRE | -Created by | -Updated | -Actions | +{t('template.list.col.name')} | +{t('template.list.col.mitre')} | +{t('template.list.col.createdBy')} | +{t('template.list.col.updated')} | +{t('engagement.list.col.actions')} |
|---|---|---|---|---|---|---|---|---|---|
| - {t.name} + {template.name} | - {mitreCount(t) === 0 ? '—' : mitreCount(t)} + {mitreCount(template) === 0 ? '—' : mitreCount(template)} | -{t.created_by.username} | -{formatDate(t.updated_at)} | +{template.created_by.username} | +{formatDate(template.updated_at)} |
-
- Edit
+
+ {t('template.list.edit')}
|
diff --git a/frontend/tests/TemplateFormPage.test.tsx b/frontend/tests/TemplateFormPage.test.tsx
index 09cb5dc..c76f19d 100644
--- a/frontend/tests/TemplateFormPage.test.tsx
+++ b/frontend/tests/TemplateFormPage.test.tsx
@@ -92,22 +92,20 @@ describe('TemplateFormPage — new mode', () => {
it('renders the form with name field in empty state', () => {
renderNew();
- expect(screen.getByLabelText(/Name/i)).toBeInTheDocument();
- expect(screen.getByLabelText(/Description/i)).toBeInTheDocument();
- expect(screen.getByLabelText(/Commands/i)).toBeInTheDocument();
- expect(screen.getByLabelText(/Prerequisites/i)).toBeInTheDocument();
- // All inputs should be empty
- expect(screen.getByLabelText(/Name/i)).toHaveValue('');
+ expect(screen.getByLabelText(/^Nom/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/^Description/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/^Commandes/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/^Prérequis/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/^Nom/i)).toHaveValue('');
});
it('shows validation error when name is empty on submit', async () => {
const user = userEvent.setup();
renderNew();
- // Name field is empty by default — click Save directly
- const saveBtn = screen.getByRole('button', { name: /Save/i });
+ const saveBtn = screen.getByRole('button', { name: /Enregistrer/i });
await user.click(saveBtn);
await waitFor(() => {
- expect(screen.getByText('Name is required')).toBeInTheDocument();
+ expect(screen.getByText('Le nom est obligatoire')).toBeInTheDocument();
});
});
@@ -115,8 +113,8 @@ describe('TemplateFormPage — new mode', () => {
mock.onPost('/templates').reply(201, { ...TEMPLATE, id: 99 });
const user = userEvent.setup();
renderNew();
- await user.type(screen.getByLabelText(/Name/i), 'My Template');
- await user.click(screen.getByRole('button', { name: /Save/i }));
+ await user.type(screen.getByLabelText(/^Nom/i), 'My Template');
+ await user.click(screen.getByRole('button', { name: /Enregistrer/i }));
await waitFor(() => {
expect(mock.history.post.length).toBe(1);
});
@@ -128,8 +126,8 @@ describe('TemplateFormPage — new mode', () => {
mock.onPost('/templates').reply(409, { error: 'template name already exists' });
const user = userEvent.setup();
renderNew();
- await user.type(screen.getByLabelText(/Name/i), 'Duplicate');
- await user.click(screen.getByRole('button', { name: /Save/i }));
+ await user.type(screen.getByLabelText(/^Nom/i), 'Duplicate');
+ await user.click(screen.getByRole('button', { name: /Enregistrer/i }));
await waitFor(() => {
expect(screen.getByText('template name already exists')).toBeInTheDocument();
});
@@ -137,7 +135,7 @@ describe('TemplateFormPage — new mode', () => {
it('does not show Delete button in new mode', () => {
renderNew();
- expect(screen.queryByText('Delete')).toBeNull();
+ expect(screen.queryByText('Supprimer')).toBeNull();
});
});
@@ -176,7 +174,7 @@ describe('TemplateFormPage — edit mode', () => {
await waitFor(() => {
expect(screen.getByDisplayValue('Mimikatz LSASS Dump')).toBeInTheDocument();
});
- expect(screen.getByText('Delete')).toBeInTheDocument();
+ expect(screen.getByText('Supprimer')).toBeInTheDocument();
});
it('submits PATCH on save', async () => {
@@ -187,7 +185,7 @@ describe('TemplateFormPage — edit mode', () => {
await waitFor(() => {
expect(screen.getByDisplayValue('Mimikatz LSASS Dump')).toBeInTheDocument();
});
- await user.click(screen.getByRole('button', { name: /Save/i }));
+ await user.click(screen.getByRole('button', { name: /Enregistrer/i }));
await waitFor(() => {
expect(mock.history.patch.length).toBe(1);
});
@@ -201,14 +199,14 @@ describe('TemplateFormPage — edit mode', () => {
const user = userEvent.setup();
renderEdit(5);
await waitFor(() => {
- expect(screen.getByText('Delete')).toBeInTheDocument();
+ expect(screen.getByText('Supprimer')).toBeInTheDocument();
});
- await user.click(screen.getByText('Delete'));
+ await user.click(screen.getByText('Supprimer'));
await waitFor(() => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
});
- // Click the Delete button inside the dialog
- const dialogDeleteBtn = screen.getAllByText('Delete').find(
+ // Click the Supprimer button inside the dialog
+ const dialogDeleteBtn = screen.getAllByText('Supprimer').find(
(el) => el.tagName === 'BUTTON' && el.closest('[role="dialog"]')
) as HTMLElement;
await user.click(dialogDeleteBtn);
diff --git a/frontend/tests/TemplatePickerModal.test.tsx b/frontend/tests/TemplatePickerModal.test.tsx
index f067200..8069d98 100644
--- a/frontend/tests/TemplatePickerModal.test.tsx
+++ b/frontend/tests/TemplatePickerModal.test.tsx
@@ -76,7 +76,7 @@ describe('TemplatePickerModal', () => {
await waitFor(() => {
expect(screen.getByTestId('empty-state')).toBeInTheDocument();
});
- expect(screen.getByText(/No templates available/i)).toBeInTheDocument();
+ expect(screen.getByText(/Aucun template disponible/i)).toBeInTheDocument();
});
it('lists templates with name and MITRE count', async () => {
@@ -128,9 +128,9 @@ describe('TemplatePickerModal', () => {
/>
);
await waitFor(() => {
- expect(screen.getByText('Cancel')).toBeInTheDocument();
+ expect(screen.getByText('Annuler')).toBeInTheDocument();
});
- await user.click(screen.getByText('Cancel'));
+ await user.click(screen.getByText('Annuler'));
expect(onClose).toHaveBeenCalledOnce();
});
diff --git a/frontend/tests/TemplatesListPage.test.tsx b/frontend/tests/TemplatesListPage.test.tsx
index e587a05..2169c91 100644
--- a/frontend/tests/TemplatesListPage.test.tsx
+++ b/frontend/tests/TemplatesListPage.test.tsx
@@ -100,7 +100,7 @@ describe('TemplatesListPage', () => {
await waitFor(() => {
expect(screen.getByText('Mimikatz LSASS Dump')).toBeInTheDocument();
});
- expect(screen.getAllByText(/New/i).length).toBeGreaterThan(0);
+ expect(screen.getAllByText(/Nouveau/i).length).toBeGreaterThan(0);
});
it('shows Edit and Delete actions', async () => {
@@ -109,8 +109,8 @@ describe('TemplatesListPage', () => {
await waitFor(() => {
expect(screen.getByText('Mimikatz LSASS Dump')).toBeInTheDocument();
});
- expect(screen.getAllByText('Edit').length).toBe(2);
- expect(screen.getAllByText('Delete').length).toBe(2);
+ expect(screen.getAllByText('Modifier').length).toBe(2);
+ expect(screen.getAllByText('Supprimer').length).toBe(2);
});
it('calls delete endpoint on confirm', async () => {
@@ -124,10 +124,10 @@ describe('TemplatesListPage', () => {
renderWithProviders(