Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions next/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Improved VTL editor. Errors are better handled.
- Some variables names have been reverted to legacy ones in French (e.g. "Libellé").
- Some visual improvements to Select component.

Expand Down
8 changes: 4 additions & 4 deletions next/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pogues",
"private": true,
"version": "2.3.0-rc.6",
"version": "2.3.0-rc.vtl-editor",
"type": "module",
"scripts": {
"generate-routes": "tsr generate",
Expand All @@ -19,9 +19,9 @@
"@base-ui-components/react": "^1.0.0-rc.0",
"@fontsource-variable/open-sans": "^5.1.1",
"@hookform/resolvers": "^5.1.1",
"@making-sense/antlr-editor": "2.0.0",
"@making-sense/vtl-2-0-antlr-tools-ts": "^2.0.1",
"@making-sense/vtl-2-0-monaco-tools-ts": "^0.1.0",
"@making-sense/antlr-editor": "2.5.0",
"@making-sense/vtl-2-1-antlr-tools-ts": "^1.0.5",
"@making-sense/vtl-2-1-monaco-tools-ts": "^1.0.1",
"@tanstack/react-query": "^5.64.1",
"@tanstack/react-router": "^1.121.34",
"axios": "^1.11.0",
Expand Down
50 changes: 26 additions & 24 deletions next/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

111 changes: 47 additions & 64 deletions next/src/components/articulation/form/ArticulationForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,141 +18,124 @@ vi.mock('@tanstack/react-router', async () => {
});

describe('ArticulationForm', () => {
const mockSubmit = vi.fn();
it('should enable validate button only when all articulation items fields are filled', async () => {
// Given an empty form
const foo = vi.fn();
await renderWithRouter(
<ArticulationForm questionnaireId="q-id" onSubmit={mockSubmit} />,
<ArticulationForm questionnaireId="q-id" onSubmit={foo} />,
);

// no data for any articulation item
// Then the form is invalid and cannot be submitted
await waitFor(() => {
expect(screen.getByRole('button', { name: /Validate/i })).toBeDisabled();
});

// fill first name field
fireEvent.input(screen.getByTestId('items.0.value'), {
target: {
value: 'first name formula',
},
// When we fill all the inputs
fireEvent.input(screen.getByRole('textbox', { name: 'First Name *' }), {
target: { value: 'first name formula' },
});

await waitFor(() => {
expect(screen.getByRole('button', { name: /Validate/i })).toBeDisabled();
});

// fill gender field
fireEvent.input(screen.getByTestId('items.1.value'), {
target: {
value: 'gender formula',
},
fireEvent.input(screen.getByRole('textbox', { name: 'Gender *' }), {
target: { value: 'gender formula' },
});

await waitFor(() => {
expect(screen.getByRole('button', { name: /Validate/i })).toBeDisabled();
});

// fill age field
fireEvent.input(screen.getByTestId('items.2.value'), {
target: {
value: 'age formula',
},
fireEvent.input(screen.getByRole('textbox', { name: 'Age *' }), {
target: { value: 'gender formula' },
});

// all articulation item fields have been filled
// Then the form becomes valid and can be submitted
await waitFor(() => {
expect(screen.getByRole('button', { name: /Validate/i })).toBeEnabled();
});

fireEvent.submit(screen.getByRole('button', { name: /Validate/i }));
await waitFor(() => {
expect(mockSubmit).toBeCalled();
expect(foo).toHaveBeenCalledOnce();
});
});

it('should display error when first name field is empty', async () => {
// Given a form with a filled first name input
await renderWithRouter(
<ArticulationForm questionnaireId="q-id" onSubmit={mockSubmit} />,
<ArticulationForm questionnaireId="q-id" onSubmit={vi.fn()} />,
);

fireEvent.input(screen.getByTestId('items.0.value'), {
target: {
value: 'first name formula',
},
fireEvent.input(screen.getByRole('textbox', { name: 'First Name *' }), {
target: { value: 'age formula' },
});
fireEvent.input(screen.getByTestId('items.0.value'), {
target: {
value: '',
},

// When the first name input becomes empty
fireEvent.input(screen.getByRole('textbox', { name: 'First Name *' }), {
target: { value: '' },
});

// Then an error is displayed
expect(await screen.findAllByRole('alert')).toHaveLength(1);
expect(
screen.getByText('You must provide a formula for first name'),
).toBeDefined();
});

it('should display error when gender field is empty', async () => {
// Given a form with a filled gender input
await renderWithRouter(
<ArticulationForm questionnaireId="q-id" onSubmit={mockSubmit} />,
<ArticulationForm questionnaireId="q-id" onSubmit={vi.fn()} />,
);

fireEvent.input(screen.getByTestId('items.1.value'), {
target: {
value: 'gender formula',
},
fireEvent.input(screen.getByRole('textbox', { name: 'Gender *' }), {
target: { value: 'age formula' },
});
fireEvent.input(screen.getByTestId('items.1.value'), {
target: {
value: '',
},

// When the gender input becomes empty
fireEvent.input(screen.getByRole('textbox', { name: 'Gender *' }), {
target: { value: '' },
});

// Then an error is displayed
expect(await screen.findAllByRole('alert')).toHaveLength(1);
expect(
screen.getByText('You must provide a formula for gender'),
).toBeDefined();
});

it('should display error when age field is empty', async () => {
// Given a form with a filled age input
await renderWithRouter(
<ArticulationForm questionnaireId="q-id" onSubmit={mockSubmit} />,
<ArticulationForm questionnaireId="q-id" onSubmit={vi.fn()} />,
);

fireEvent.input(screen.getByTestId('items.2.value'), {
target: {
value: 'age formula',
},
fireEvent.input(screen.getByRole('textbox', { name: 'Age *' }), {
target: { value: 'age formula' },
});
fireEvent.input(screen.getByTestId('items.2.value'), {
target: {
value: '',
},

// When the age input becomes empty
fireEvent.input(screen.getByRole('textbox', { name: 'Age *' }), {
target: { value: '' },
});

// Then an error is displayed
expect(await screen.findAllByRole('alert')).toHaveLength(1);
expect(
screen.getByText('You must provide a formula for age'),
).toBeDefined();
});

it("allows to go back to articulation overview page by clicking 'cancel' button", async () => {
// Given a form in dirty state
await renderWithRouter(
<ArticulationForm questionnaireId="q-id" onSubmit={mockSubmit} />,
<ArticulationForm questionnaireId="q-id" onSubmit={vi.fn()} />,
);

// edit form for being in dirty state
fireEvent.input(screen.getByTestId('items.0.value'), {
target: {
value: 'first name formula',
},
fireEvent.input(screen.getByRole('textbox', { name: 'First Name *' }), {
target: { value: 'first name formula' },
});

const cancelButton = screen.getByRole('button', { name: /cancel/i });

expect(cancelButton).toBeEnabled();
fireEvent.click(cancelButton);
// When we click on "cancel" button
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));

// navigate to articulation page, ignoring the dirty state
// Then we navigate to articulation page
expect(mockNavigate).toHaveBeenCalledWith({
to: '/questionnaire/$questionnaireId/articulation',
params: { questionnaireId: 'q-id' },
Expand Down
Loading
Loading