import { create } from "apisauce"; import inatjs from "inaturalistjs"; import Config from "react-native-config-node"; import { CHUCKS_PAD as sampleObservation } from "../../src/appConstants/e2e"; const apiHost = Config.OAUTH_API_URL; const testUsernameAllowlist = [ "inaturalist-test" ]; const userAgent = "iNaturalistRN/e2e"; inatjs.setConfig( { apiURL: Config.API_URL, writeApiURL: Config.API_URL, userAgent } ); // programatically dismisses announcements for user and resets to a lone sample observation // in order to set up consistent testing conditions and remove need to wait for announcements export default async function resetUserForTesting() { console.log( "Test user reset: dismissing announcements and resetting observations..." ); if ( !testUsernameAllowlist.includes( Config.E2E_TEST_USERNAME ) ) { const message = "This e2e test deletes observations of the user under test. " + "Add this username to the `testUsernameAllowlist` if that's really what you want. " + "Aborting."; throw new Error( message ); } const apiClient = create( { baseURL: apiHost, headers: { "User-Agent": userAgent } } ); await apiClient.get( "/logout" ); const formData = { format: "json", grant_type: "password", client_id: Config.OAUTH_CLIENT_ID, client_secret: Config.OAUTH_CLIENT_SECRET, username: Config.E2E_TEST_USERNAME, password: Config.E2E_TEST_PASSWORD, locale: "en" }; const tokenResponse = await apiClient.post( "/oauth/token", formData ); const accessToken = tokenResponse.data.access_token; apiClient.setHeader( "Authorization", `Bearer ${accessToken}` ); const jwtResponse = await apiClient.get( "/users/api_token.json" ); const opts = { api_token: jwtResponse.data.api_token }; const announcementSearchParams = { placement: "mobile", locale: "en-US", per_page: 20, fields: { id: true, dismissible: true } }; const announcementResponse = await inatjs.announcements.search( announcementSearchParams, opts ); const announcementIdsToDismiss = announcementResponse .results .filter( a => a.dismissible ) .map( a => a.id ); console.log( `Dismissing ${announcementIdsToDismiss.length} announcements` ); await Promise.all( announcementIdsToDismiss.map( async id => { try { await inatjs.announcements.dismiss( { id }, opts ); } catch ( _error ) { console.log( `Could not delete announcement: ${id}. Moving on...` ); } } ) ); const usersEditResponse = await apiClient.get( "/users/edit.json", {}, { headers: { Authorization: `Bearer ${accessToken}`, "User-Agent": userAgent } } ); const userId = usersEditResponse.data.id; const observationResponse = await inatjs.observations.search( { user_id: userId }, opts ); if ( typeof observationResponse?.total_results !== "number" ) { const message = "Unexpected issue getting test user's observations. Aborting."; throw new Error( message ); } // spot check to smell if this is _really_ a test user, we don't want to accidentally delete // real observations const suspiciousObservationThreshold = 10; if ( observationResponse.total_results > suspiciousObservationThreshold ) { const message = `More than ${suspiciousObservationThreshold} observations found for test user. Aborting.`; throw new Error( message ); } const observationIdsToDelete = observationResponse.results.map( a => a.uuid ); console.log( `Deleting ${observationIdsToDelete.length} observations` ); await Promise.all( observationIdsToDelete.map( async uuid => { try { await inatjs.observations.delete( { uuid }, opts ); } catch ( _error ) { console.log( `Could not delete observation: ${uuid}. Moving on...` ); } } ) ); console.log( "Creating sample observation" ); const sampleObservationParams = { observation: { latitude: sampleObservation.latitude, longitude: sampleObservation.longitude } }; await inatjs.observations.create( sampleObservationParams, opts ); await apiClient.get( "/logout" ); console.log( "Test user reset: announcements dismissed and observations reset" ); }