Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Separate validationResult and constraintValidationResult
  • Loading branch information
grzesiek2010 committed Jan 21, 2026
commit 6c7de7fc25002ec03ecdff2f6355a051e2ebd8cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public class FormEntryViewModel extends ViewModel implements SelectChoiceLoader
private final MutableLiveData<Triple<FormIndex, FormIndex, FailedValidationResult>> currentIndex = new MutableLiveData<>(null);
private final MutableLiveData<Consumable<ValidationResult>>
validationResult = new MutableLiveData<>(new Consumable<>(null));
private final MutableLiveData<Consumable<ValidationResult>>
constraintValidationResult = new MutableLiveData<>(new Consumable<>(null));
@NonNull
private final FormSessionRepository formSessionRepository;
private final String sessionId;
Expand Down Expand Up @@ -129,6 +131,10 @@ public LiveData<Consumable<ValidationResult>> getValidationResult() {
return validationResult;
}

public LiveData<Consumable<ValidationResult>> getConstraintValidationResult() {
return constraintValidationResult;
}

public NonNullLiveData<Boolean> isLoading() {
return worker.isWorking();
}
Expand Down Expand Up @@ -394,11 +400,7 @@ public void exit() {
public void validateAnswerConstraint(FormIndex index, IAnswerData answer) {
worker.immediate(() -> {
ValidationResult result = formController.validateAnswerConstraint(index, answer);
if (result instanceof FailedValidationResult) {
validationResult.postValue(new Consumable<>(result));
} else {
validationResult.postValue(new Consumable<>(null));
}
constraintValidationResult.postValue(new Consumable<>(result));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import org.javarosa.core.model.Constants
import org.javarosa.core.model.data.GeoShapeData
import org.javarosa.core.model.data.GeoTraceData
import org.javarosa.core.model.data.IAnswerData
import org.javarosa.form.api.FormEntryController
import org.javarosa.form.api.FormEntryPrompt
import org.odk.collect.android.javarosawrapper.FailedValidationResult
import org.odk.collect.android.utilities.FormEntryPromptUtils
Expand Down Expand Up @@ -65,12 +64,9 @@ class GeoPolyDialogFragment(viewModelFactory: ViewModelProvider.Factory) :
prompt.isReadOnly,
retainMockAccuracy,
inputPolygon,
validationResult.map {
constraintValidationResult.map {
val validationResult = it.value
if (validationResult is FailedValidationResult &&
validationResult.index == prompt.index &&
validationResult.status == FormEntryController.ANSWER_CONSTRAINT_VIOLATED
) {
if (validationResult is FailedValidationResult && validationResult.index == prompt.index) {
validationResult.customErrorMessage ?: getString(validationResult.defaultErrorMessage)
} else {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ abstract class WidgetAnswerDialogFragment<T : Fragment>(
private val prompt: FormEntryPrompt by lazy {
formEntryViewModel.getQuestionPrompt(requireArguments().getSerializable(ARG_FORM_INDEX) as FormIndex)
}
protected val validationResult by lazy {
formEntryViewModel.validationResult
protected val constraintValidationResult by lazy {
formEntryViewModel.constraintValidationResult
}

abstract fun onCreateFragment(prompt: FormEntryPrompt): T
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ public void answerQuestion_savesAnswerToFormController() {
}

@Test
public void validateAnswerConstraint_updatesValidationResult_ifIsIsFailedValidationResult() {
public void validateAnswerConstraint_updatesConstraintValidationResult_ifIsIsFailedValidationResult() {
FormDef formDef = mock();
when(formDef.evaluateConstraint(any(), any())).thenReturn(false);
formController.setFormDef(formDef);
Expand All @@ -473,24 +473,7 @@ public void validateAnswerConstraint_updatesValidationResult_ifIsIsFailedValidat

viewModel.validateAnswerConstraint(formIndex, new StringData("answer"));
scheduler.flush(true);
assertThat(viewModel.getValidationResult().getValue().getValue(), equalTo(failedValidationResult));
}

@Test
public void validateAnswerConstraint_clearsResult_ifItIsSuccessValidationResult() {
FormDef formDef = mock();
when(formDef.evaluateConstraint(any(), any())).thenReturn(false);
formController.setFormDef(formDef);

TreeReference reference = new TreeReference();
reference.add("blah", TreeReference.INDEX_UNBOUND);
FormIndex formIndex = new FormIndex(null, 1, 1, reference);
FormEntryPrompt prompt = new MockFormEntryPromptBuilder().build();
formController.setPrompt(formIndex, prompt);

viewModel.validateAnswerConstraint(formIndex, new StringData("answer"));
scheduler.flush(true);
assertThat(viewModel.getValidationResult().getValue().getValue(), equalTo(null));
assertThat(viewModel.getConstraintValidationResult().getValue().getValue(), equalTo(failedValidationResult));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ import org.odk.collect.testshared.getOrAwaitValue
class GeoPolyDialogFragmentTest {

private var prompt = MockFormEntryPromptBuilder().build()
private val validationResult = MutableLiveData<Consumable<ValidationResult>>(
private val constraintValidationResult = MutableLiveData<Consumable<ValidationResult>>(
Consumable(SuccessValidationResult)
)
private val formEntryViewModel = mock<FormEntryViewModel> {
on { getQuestionPrompt(prompt.index) } doReturn prompt
on { validationResult } doReturn validationResult
on { constraintValidationResult } doReturn constraintValidationResult
}

private val viewModelFactory = object : ViewModelProvider.Factory {
Expand Down Expand Up @@ -475,22 +475,7 @@ class GeoPolyDialogFragmentTest {
0,
TreeReference()
)
validationResult.value = Consumable(FailedValidationResult(anotherQuestionFormIndex, FormEntryController.ANSWER_CONSTRAINT_VIOLATED, "blah", 0))
launcherRule.launchAndAssertOnChild<GeoPolyFragment>(
GeoPolyDialogFragment::class,
bundleOf(ARG_FORM_INDEX to prompt.index)
) {
assertThat(it.invalidMessage.getOrAwaitValue(), equalTo(null))
}
}

@Test
fun `ignores the validation message if it was triggered by a required but empty answer`() {
prompt = MockFormEntryPromptBuilder(prompt)
.withDataType(Constants.DATATYPE_GEOTRACE)
.build()

validationResult.value = Consumable(FailedValidationResult(prompt.index, FormEntryController.ANSWER_REQUIRED_BUT_EMPTY, "blah", 0))
constraintValidationResult.value = Consumable(FailedValidationResult(anotherQuestionFormIndex, FormEntryController.ANSWER_CONSTRAINT_VIOLATED, "blah", 0))
launcherRule.launchAndAssertOnChild<GeoPolyFragment>(
GeoPolyDialogFragment::class,
bundleOf(ARG_FORM_INDEX to prompt.index)
Expand All @@ -512,7 +497,7 @@ class GeoPolyDialogFragmentTest {
assertThat(it.invalidMessage.getOrAwaitValue(), equalTo(null))
}

validationResult.value = Consumable(FailedValidationResult(prompt.index, FormEntryController.ANSWER_CONSTRAINT_VIOLATED, "blah", 0))
constraintValidationResult.value = Consumable(FailedValidationResult(prompt.index, FormEntryController.ANSWER_CONSTRAINT_VIOLATED, "blah", 0))
launcherRule.launchAndAssertOnChild<GeoPolyFragment>(
GeoPolyDialogFragment::class,
bundleOf(ARG_FORM_INDEX to prompt.index)
Expand All @@ -534,7 +519,7 @@ class GeoPolyDialogFragmentTest {
assertThat(it.invalidMessage.getOrAwaitValue(), equalTo(null))
}

validationResult.value =
constraintValidationResult.value =
Consumable(FailedValidationResult(prompt.index, FormEntryController.ANSWER_CONSTRAINT_VIOLATED, null, R.string.cancel))
launcherRule.launchAndAssertOnChild<GeoPolyFragment>(
GeoPolyDialogFragment::class,
Expand Down