Files
iNaturalistReactNative/tests/unit/uploaders/utils/errorHandling.test.js
Johannes Klein 9c0fcf1d60 Recovery from observations not uploading (#3015)
* Log app version on start

This is helpful for log files sent in from users, to see what version of he code they were actually running while errors happened.

* Add some types

* Import as types

* Remove unused param

* Update types

* Add string

* Update strings.ftl

* Add explanatory text to login screen if logging in again

* Add TODO

* Define new error class

* Throw a RecoverableError if trying to upload without token

* Do not make a new Error if caught. Instead re-throw the one caught.

This preserves the RecoverableError type if thrown.

* Update string

* Add to return info of function that handles errors

* Recover from an error that login again might fix

By sending the user to the login screen. In case of a multi-observation upload we stop the current queue because subsequent observations will also error out (and send one navigation event each).

* Update errorHandling.test.js

* Revert "Add TODO"

This reverts commit 57437f1ece.

* Add unit tests for recovery options
2025-07-18 07:58:42 +02:00

105 lines
3.2 KiB
JavaScript

import { handleUploadError } from "uploaders";
import { RecoverableError, RECOVERY_BY } from "uploaders/utils/errorHandling.ts";
describe( "errorHandling", ( ) => {
// Mock translation function since we're passing that into the error handler
const t = jest.fn( key => key );
describe( "handleUploadError", ( ) => {
beforeEach( ( ) => {
jest.clearAllMocks( );
} );
test( "should throw basic errors without special handling", ( ) => {
const error = new Error( "Basic error" );
expect( ( ) => {
handleUploadError( error, t );
} ).toThrow( "Basic error" );
} );
test( "should return 'Connection problem' message for network errors", ( ) => {
const networkError = new Error( "Network request failed" );
const { message } = handleUploadError( networkError, t );
expect( message ).toBe( "Connection-problem-Please-try-again-later" );
} );
test( "should return recoveryPossible=true for network errors", () => {
const networkError = new Error( "Network request failed" );
const { recoveryPossible } = handleUploadError( networkError, t );
expect( recoveryPossible ).toBe( true );
} );
test( "should return recovery options for RecoverableErrors", () => {
const recoverableError = new RecoverableError( "Recoverable error" );
recoverableError.recoveryBy = RECOVERY_BY.LOGIN_AGAIN;
const { recoveryPossible, recoveryBy } = handleUploadError(
recoverableError,
t
);
expect( recoveryPossible ).toBe( true );
expect( recoveryBy ).toBe( RECOVERY_BY.LOGIN_AGAIN );
} );
test( "should return a string from an array of iNaturalist API errors", ( ) => {
const apiError = new Error( "API Upload Error" );
apiError.json = {
errors: [
{ message: "First error" },
{ message: "Second error" }
]
};
const { message } = handleUploadError( apiError, t );
expect( message ).toBe( "First error, Second error" );
} );
test( "should return a string from a nested array of iNaturalist API errors", ( ) => {
const apiError = new Error( "API Error" );
apiError.json = {
errors: [
{ message: { errors: ["Error 1", "Error 2"] } }
]
};
const { message } = handleUploadError( apiError, t );
expect( message ).toBe( "Error 1, Error 2" );
} );
test( "should return a string from a nested string "
+ "within iNaturalist API error messages", ( ) => {
const apiError = new Error( "API Error" );
apiError.json = {
errors: [
{ message: { errors: "Nested error message string" } }
]
};
const { message } = handleUploadError( apiError, t );
expect( message ).toBe( "Nested error message string" );
} );
test( "should handle 410 errors for previously deleted observations or evidence", ( ) => {
const apiError = new Error( "API Error" );
apiError.json = {
errors: [
{ message: { error: "Resource was previously deleted" } }
]
};
const { message } = handleUploadError( apiError, t );
expect( message ).toBe( "Resource was previously deleted" );
} );
} );
} );