feat(frontend): i18n template pages (list, form, picker modal)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-06-21 23:36:20 +02:00
parent 5b93f880a3
commit 284494cee8
6 changed files with 124 additions and 124 deletions

View File

@@ -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);

View File

@@ -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();
});

View File

@@ -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(<TemplatesListPage />);
await waitFor(() => {
expect(screen.getAllByText('Delete')[0]).toBeInTheDocument();
expect(screen.getAllByText('Supprimer')[0]).toBeInTheDocument();
});
const deleteButtons = screen.getAllByText('Delete');
const deleteButtons = screen.getAllByText('Supprimer');
await user.click(deleteButtons[0]);
await waitFor(() => {