Skip to content

Commit 7ae3100

Browse files
committed
refactor: add tests for specific cases
1 parent 57a6e01 commit 7ae3100

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

js/modules/Forms/RendererController.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,15 @@ export class GlpiFormRendererController
146146
dataType: 'json',
147147
});
148148

149-
// Remove previous error messages
149+
// Remove previous error messages and aria attributes
150150
$(this.#target)
151151
.find(".invalid-tooltip")
152152
.remove();
153153
$(this.#target)
154154
.find(".is-invalid")
155-
.removeClass("is-invalid");
155+
.removeClass("is-invalid")
156+
.removeAttr("aria-invalid")
157+
.removeAttr("aria-errormessage");
156158

157159
if (response.success === false) {
158160
Object.values(response.errors).forEach(error => {
@@ -161,13 +163,13 @@ export class GlpiFormRendererController
161163
if (!question.length) {
162164
return;
163165
}
164-
166+
165167
// Find the input field within the question
166168
const inputField = question.find('input:not([data-uploader-name]), select, textarea');
167169
if (!inputField.length) {
168170
return;
169171
}
170-
172+
171173
// Generate a unique ID for the error message
172174
const errorId = `error-${error.question_id}`;
173175

phpunit/functional/Glpi/Form/AnswersHandler/AnswersHandlerTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
use Glpi\Form\Condition\LogicOperator;
4343
use Glpi\Form\Condition\Type;
4444
use Glpi\Form\Condition\ValueOperator;
45+
use Glpi\Form\Condition\VisibilityStrategy;
4546
use Glpi\Form\Destination\FormDestinationChange;
4647
use Glpi\Form\Destination\FormDestinationProblem;
4748
use Glpi\Form\Destination\FormDestinationTicket;
@@ -223,6 +224,58 @@ public function testValidateAnswers(): void
223224
$this->assertCount(1, $result->getErrors(), "There should be one error when a mandatory field contains an empty string");
224225
}
225226

227+
public function testValidateAnswersWithConditions(): void
228+
{
229+
self::login();
230+
231+
// Create a form with conditional questions
232+
$builder = new FormBuilder("Conditional Validation Test Form");
233+
$builder
234+
->addQuestion("Main Question", QuestionTypeShortText::class, is_mandatory: true)
235+
->addQuestion("Conditional Question", QuestionTypeShortText::class, is_mandatory: true)
236+
->setQuestionVisibility(
237+
"Conditional Question",
238+
VisibilityStrategy::VISIBLE_IF,
239+
[
240+
[
241+
'logic_operator' => LogicOperator::AND,
242+
'item_name' => "Main Question",
243+
'item_type' => Type::QUESTION,
244+
'value_operator' => ValueOperator::EQUALS,
245+
'value' => "Show Conditional",
246+
],
247+
]
248+
)
249+
;
250+
$form = self::createForm($builder);
251+
252+
// Get handler instance
253+
$handler = AnswersHandler::getInstance();
254+
255+
// Test 1: Conditional question is shown - should be valid
256+
$conditional_answers = [
257+
self::getQuestionId($form, "Main Question") => "Show Conditional",
258+
self::getQuestionId($form, "Conditional Question") => "This is a conditional answer",
259+
];
260+
$result = $handler->validateAnswers($form, $conditional_answers);
261+
$this->assertTrue($result->isValid(), "Validation should pass when the conditional question is shown and filled");
262+
263+
// Test 2: Conditional question is not shown - should be valid
264+
$non_conditional_answers = [
265+
self::getQuestionId($form, "Main Question") => "Do not show"
266+
];
267+
$result = $handler->validateAnswers($form, $non_conditional_answers);
268+
$this->assertTrue($result->isValid(), "Validation should pass when the conditional question is not shown");
269+
270+
// Test 3: Conditional question is shown but not filled - should be invalid
271+
$missing_conditional_answers = [
272+
self::getQuestionId($form, "Main Question") => "Show Conditional",
273+
self::getQuestionId($form, "Conditional Question") => "",
274+
];
275+
$result = $handler->validateAnswers($form, $missing_conditional_answers);
276+
$this->assertFalse($result->isValid(), "Validation should fail when the conditional question is shown but not filled");
277+
}
278+
226279
private function validateAnswers(
227280
Form $form,
228281
array $answers,

tests/cypress/e2e/form/form_rendering.cy.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,71 @@ describe('Form rendering', () => {
156156
cy.findByText('Form submitted').should('be.visible');
157157
});
158158
});
159+
160+
it.only('Mandatory question alert is correctly removed when value is set and go to next section', () => {
161+
// Set up a form with two sections, each with a mandatory question
162+
cy.createWithAPI('Glpi\\Form\\Form', {
163+
'name': 'Test mandatory questions',
164+
}).as('form_id');
165+
cy.get('@form_id').then((form_id) => {
166+
// Add mandatory question to default section
167+
cy.addQuestionToDefaultSectionWithAPI(
168+
form_id,
169+
'First question',
170+
'Glpi\\Form\\QuestionType\\QuestionTypeShortText',
171+
0,
172+
null,
173+
null,
174+
null,
175+
true // Mandatory
176+
);
177+
178+
// Add second section
179+
cy.createWithAPI('Glpi\\Form\\Section', {
180+
'name': 'Second section',
181+
'rank': 1,
182+
'forms_forms_id': form_id,
183+
}).as('second_section_id');
184+
185+
cy.get('@second_section_id').then((second_section_id) => {
186+
// Add mandatory question to second section
187+
cy.createWithAPI('Glpi\\Form\\Question', {
188+
'name': 'Second question',
189+
'type': 'Glpi\\Form\\QuestionType\\QuestionTypeShortText',
190+
'vertical_rank': 1,
191+
'forms_sections_id': second_section_id,
192+
'is_mandatory' : true,
193+
}).as('second_section_id');
194+
});
195+
196+
// Preview form
197+
cy.login();
198+
cy.visit(`/Form/Render/${form_id}`);
199+
200+
// Try to submit first section, should fail since we didn't answer
201+
// the mandatory question
202+
cy.findByRole('button', {name: 'Continue'}).click();
203+
checkMandatoryQuestion('First question');
204+
cy.findByRole('heading', {name: 'First section'}).should('be.visible');
205+
cy.findByRole('heading', {name: 'Second section'}).should('not.exist');
206+
207+
// Submit again with a value
208+
cy.findByRole('textbox', {name: 'First question'}).type("test");
209+
cy.findByRole('button', {name: 'Continue'}).click();
210+
cy.findByRole('heading', {name: 'Second section'}).should('be.visible');
211+
cy.findByRole('heading', {name: 'First section'}).should('not.exist');
212+
213+
// Go back to first section
214+
cy.findByRole('button', {name: 'Back'}).click();
215+
cy.findByRole('heading', {name: 'First section'}).should('be.visible');
216+
217+
// Check that the error message isn't displayed anymore
218+
getAriaErrorMessageElement(cy.findByRole('textbox', {name: 'First question'}))
219+
.should('not.exist');
220+
cy.findByRole('textbox', {name: 'First question'}).should('not.have.attr', 'aria-invalid');
221+
cy.findByRole('textbox', {name: 'First question'}).should('not.have.attr', 'aria-errormessage');
222+
});
223+
});
159224
});
160225

161226
function addQuestionAndGetUuuid(name, type = null, subtype = null) {

0 commit comments

Comments
 (0)