Files
iNaturalistReactNative/tests/unit/helpers/validateProjectFieldsForObservation.test.js

251 lines
7.6 KiB
JavaScript

import validateProjectFieldsForObservation, {
INVALID_NUMERIC,
MISSING_REQUIRED,
validateProjectFieldValue,
} from "sharedHelpers/validateProjectFieldsForObservation";
import factory from "tests/factory";
describe( "validateProjectFieldValue", () => {
describe( "required fields", () => {
test.each( [
[undefined],
[null],
[""],
[" "],
] )( "should return MISSING_REQUIRED for empty value %p", value => {
const mockPOF = factory( "LocalProjectObservationField", {
required: true,
obsField: factory( "LocalObservationField", {
allowedValues: [],
} ),
} );
expect( validateProjectFieldValue( mockPOF, value ) ).toBe( MISSING_REQUIRED );
} );
it( "should return null for a non-empty value", () => {
const mockPOF = factory( "LocalProjectObservationField", {
required: true,
obsField: factory( "LocalObservationField", {
allowedValues: [],
} ),
} );
expect( validateProjectFieldValue( mockPOF, "x" ) ).toBeNull( );
} );
it( "should return null for a value that is non-empty after trim", () => {
const mockPOF = factory( "LocalProjectObservationField", {
required: true,
obsField: factory( "LocalObservationField", {
allowedValues: [],
} ),
} );
expect( validateProjectFieldValue( mockPOF, " x " ) ).toBeNull( );
} );
} );
describe( "numeric fields", () => {
it( "should return INVALID_NUMERIC for a non-numeric value on an optional field", () => {
const mockPOF = factory( "LocalProjectObservationField", {
required: false,
obsField: factory( "LocalObservationField", {
allowedValues: [],
datatype: "numeric",
} ),
} );
expect( validateProjectFieldValue( mockPOF, "abc" ) ).toBe( INVALID_NUMERIC );
} );
test.each( [
[undefined],
[""],
] )( "should return null for empty value %p on an optional numeric field", value => {
const mockPOF = factory( "LocalProjectObservationField", {
required: false,
obsField: factory( "LocalObservationField", {
allowedValues: [],
datatype: "numeric",
} ),
} );
expect( validateProjectFieldValue( mockPOF, value ) ).toBeNull( );
} );
} );
describe( "optional text fields", () => {
test.each( [
[undefined],
[""],
["anything"],
] )( "should return null for %p", value => {
const mockPOF = factory( "LocalProjectObservationField", {
required: false,
obsField: factory( "LocalObservationField", {
allowedValues: [],
} ),
} );
expect( validateProjectFieldValue( mockPOF, value ) ).toBeNull( );
} );
} );
} );
describe( "validateProjectFieldsForObservation", () => {
describe( "required fields", () => {
it( "should be valid when a required field has a non-empty OFV", () => {
const mockProject = {
projectObservationFields: [{
required: true,
obsField: {
allowedValues: [],
id: 10,
},
}],
};
const mockObservation = {
observationFieldValues: [{ obsFieldId: 10, value: "shrubland" }],
};
const result = validateProjectFieldsForObservation( mockObservation, [mockProject] );
expect( result.valid ).toBe( true );
expect( result.errors ).toEqual( [] );
} );
it( "should return MISSING_REQUIRED when a required field has no OFV", () => {
const mockProject = {
title: "Mushrooms of Bavaria",
projectObservationFields: [{
required: true,
obsField: {
allowedValues: [],
id: 10,
name: "Habitat",
},
}],
};
const mockObservation = { observationFieldValues: [] };
const result = validateProjectFieldsForObservation( mockObservation, [mockProject] );
expect( result.valid ).toBe( false );
expect( result.errors ).toEqual( [{
projectId: mockProject.id,
projectTitle: "Mushrooms of Bavaria",
obsFieldId: 10,
fieldName: "Habitat",
reason: MISSING_REQUIRED,
}] );
} );
test.each( [
[""],
[" "],
] )( "should return MISSING_REQUIRED when a required field's OFV value is %p", value => {
const mockProject = {
projectObservationFields: [{
required: true,
obsField: {
allowedValues: [],
id: 10,
},
}],
};
const mockObservation = {
observationFieldValues: [{ id: 10, value }],
};
const result = validateProjectFieldsForObservation( mockObservation, [mockProject] );
expect( result.valid ).toBe( false );
expect( result.errors[0].reason ).toBe( MISSING_REQUIRED );
} );
it( "should be valid when a required field's OFV is non-empty after trim", () => {
const mockProject = {
projectObservationFields: [{
required: true,
obsField: {
allowedValues: [],
id: 10,
},
}],
};
const mockObservation = {
observationFieldValues: [{
obsFieldId: 10,
value: "something",
}],
};
expect(
validateProjectFieldsForObservation( mockObservation, [mockProject] ).valid,
).toBe( true );
} );
it( "should be valid when an optional field has no OFV", () => {
const mockProject = {
projectObservationFields: [{
required: false,
obsField: {
allowedValues: [],
id: 10,
},
}],
};
const mockObservation = { observationFieldValues: [] };
expect(
validateProjectFieldsForObservation( mockObservation, [mockProject] ).valid,
).toBe( true );
} );
it( "should report only the unfilled field when one of two required fields is filled", () => {
const mockProject = {
projectObservationFields: [
{
required: true,
obsField: {
allowedValues: [],
id: 10,
name: "Habitat",
},
},
{
required: true,
obsField: {
allowedValues: [],
id: 20,
name: "Substrate",
},
},
],
};
const mockObservation = {
observationFieldValues: [{ obsFieldId: 10, value: "shrubland" }],
};
const result = validateProjectFieldsForObservation( mockObservation, [mockProject] );
expect( result.valid ).toBe( false );
expect( result.errors ).toHaveLength( 1 );
expect( result.errors[0].fieldName ).toBe( "Substrate" );
expect( result.errors[0].reason ).toBe( MISSING_REQUIRED );
} );
it( "should report both fields in POF order when two required fields are unfilled", () => {
const mockProject = {
projectObservationFields: [
{
required: true,
obsField: {
allowedValues: [],
id: 10,
name: "Habitat",
},
},
{
required: true,
obsField: {
allowedValues: [],
id: 20,
name: "Substrate",
},
},
],
};
const mockObservation = { observationFieldValues: [] };
const result = validateProjectFieldsForObservation( mockObservation, [mockProject] );
expect( result.valid ).toBe( false );
expect( result.errors.map( e => e.fieldName ) ).toEqual( ["Habitat", "Substrate"] );
} );
} );
} );