Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restart Android e2e tests #1679

Closed
wants to merge 51 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0b4fd6d
Fix mock user location import
jtklein Jun 14, 2024
6472b64
Use older runner
jtklein Jun 14, 2024
57b602a
Use JDK 8 for emulator setup and 17 for app build step
jtklein Jun 14, 2024
2f1b76c
Test bigger runner again
jtklein Jun 14, 2024
80d563f
Revert "Test bigger runner again"
jtklein Jun 14, 2024
41f670c
Increased timeouts
jtklein Jun 16, 2024
b57be15
Increase another timeout
jtklein Jun 16, 2024
4f84b23
Try emulator with higher SDK
jtklein Jun 16, 2024
a746416
Use 34 on emulator
jtklein Jun 16, 2024
402deb7
Change emu name
jtklein Jun 16, 2024
e59edd4
Testing only emulator stuff
jtklein Jun 16, 2024
414cee3
Try newer device
jtklein Jun 16, 2024
ad3660c
List avd
jtklein Jun 16, 2024
43501d0
Revert "List avd"
jtklein Jun 16, 2024
1527b42
Revert "Try newer device"
jtklein Jun 16, 2024
1c19edb
Reapply "List avd"
jtklein Jun 16, 2024
0d55775
Test on 14
jtklein Jun 16, 2024
4696be2
Testing ubuntu-latest
jtklein Jun 16, 2024
7201b2e
Remove whitespace
jtklein Jun 16, 2024
e0a90e3
Testing other steps
jtklein Jun 16, 2024
7c3e935
Add emu options
jtklein Jun 16, 2024
16ee970
Add another step back in
jtklein Jun 16, 2024
2150a18
Add pre launch script
jtklein Jun 16, 2024
7aa428e
Wrong place for this script
jtklein Jun 16, 2024
b200f49
Use the actual test run step
jtklein Jun 16, 2024
1a72feb
Do not run test for now
jtklein Jun 16, 2024
ded0204
Setup Java step
jtklein Jun 16, 2024
d7fd4d8
Remove no longer needed steps
jtklein Jun 16, 2024
7f92c95
Enable detoc store step
jtklein Jun 16, 2024
83b1932
Actually build and test
jtklein Jun 16, 2024
e6cd0f5
Do not build
jtklein Jun 16, 2024
d5ca0d2
Add gradle cache
jtklein Jun 16, 2024
a2bcb9b
AVD cache testing
jtklein Jun 16, 2024
030aec6
Update package.json
jtklein Jun 16, 2024
4e99576
Update package-lock.json
jtklein Jun 16, 2024
e86d0c5
Run build again
jtklein Jun 16, 2024
ff3b1f6
Update Podfile.lock
jtklein Jun 16, 2024
42a79a6
Test on macos-13
jtklein Jun 16, 2024
3dd8964
Test on macos-13
jtklein Jun 16, 2024
7f2d1f4
Disable KVM
jtklein Jun 16, 2024
9bf9f7e
Remove emu gpu option
jtklein Jun 16, 2024
7b5e7ad
Testing ubuntu
jtklein Jun 16, 2024
8d78df4
test
jtklein Jun 16, 2024
9fa8702
Update e2e_android.yml
jtklein Jun 16, 2024
703df8b
Update e2e_android.yml
jtklein Jun 16, 2024
4c79373
Update e2e_android.yml
jtklein Jun 16, 2024
58a073c
Update e2e_android.yml
jtklein Jun 16, 2024
9432dbf
Update e2e_android.yml
jtklein Jun 16, 2024
644843f
Update e2e_android.yml
jtklein Jun 16, 2024
503f398
Update e2e_android.yml
jtklein Jun 16, 2024
48e8412
Update e2e_android.yml
jtklein Jun 16, 2024
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
2 changes: 1 addition & 1 deletion .detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ module.exports = {
emulator: {
type: "android.emulator",
device: {
avdName: "Pixel_5_API_31_AOSP",
avdName: "Pixel_5_API_34_AOSP",
},
},
},
Expand Down
113 changes: 73 additions & 40 deletions .github/workflows/e2e_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ concurrency:
jobs:
checksecret:
name: check for oauth client
runs-on: macos-latest
runs-on: ubuntu-latest
outputs:
is_SECRETS_PRESENT_set: ${{ steps.checksecret_job.outputs.is_SECRETS_PRESENT_set }}
steps:
Expand All @@ -24,11 +24,47 @@ jobs:
test:
needs: checksecret
if: needs.checksecret.outputs.is_SECRETS_PRESENT_set == 'true'
runs-on: macos-latest
runs-on: ubuntu-latest
# Kill the task if not finished after 60 minutes
timeout-minutes: 60
timeout-minutes: 90

steps:
- name: Check disk space
run: df . -h
- name: Free disk space
run: |
sudo docker rmi $(docker image ls -aq) >/dev/null 2>&1 || true
sudo rm -rf \
/usr/share/dotnet /usr/share/swift /opt/ghc /usr/local/share/boost \
/usr/local/share/powershell /usr/local/.ghcup $AGENT_TOOLSDIRECTORY || true
echo "some directories deleted"
sudo apt install aptitude -y >/dev/null 2>&1
sudo aptitude purge aria2 ansible azure-cli shellcheck rpm xorriso zsync \
esl-erlang firefox gfortran-8 gfortran-9 google-chrome-stable \
google-cloud-sdk google-cloud-cli google-chrome-stable imagemagick \
libmagickcore-dev libmagickwand-dev libmagic-dev ant ant-optional kubectl \
mercurial apt-transport-https mono-complete libmysqlclient \
unixodbc-dev chrpath libssl-dev libxft-dev \
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev \
snmp pollinate libpq-dev postgresql-client powershell \
sphinxsearch subversion mongodb-org azure-cli microsoft-edge-stable \
-y -f >/dev/null 2>&1
sudo aptitude purge google-cloud-sdk -f -y >/dev/null 2>&1
sudo aptitude purge microsoft-edge-stable -f -y >/dev/null 2>&1 || true
sudo apt purge microsoft-edge-stable -f -y >/dev/null 2>&1 || true
sudo aptitude purge '~n ^mysql' -f -y >/dev/null 2>&1
sudo aptitude purge '~n ^php' -f -y >/dev/null 2>&1
sudo aptitude purge '~n ^dotnet' -f -y >/dev/null 2>&1
sudo apt-get autoremove -y >/dev/null 2>&1
sudo apt-get autoclean -y >/dev/null 2>&1
sudo apt clean
echo "some packages purged"
- name: Check disk space
run: |
sudo dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -nr | head
df . -h
sudo du /usr/ -hx -d 4 --threshold=1G | sort -hr | head

- name: Check out Git repository
uses: actions/checkout@v4
with:
Expand All @@ -39,23 +75,27 @@ jobs:
with:
node-version: 18

# Use the Android command line tools to download an AOSP emulator image, and setup new avd
# The name of the device for testing has to be the same as on the .detoxrc.js file (even if it is not a Pixel)
# The mac-os latest runner has 3 different versions of Java pre-installed (8,11,17), and there were errors when setting to use either 11 or 17 explicitly here
# I am assuming (but haven't tested) it uses 8 and then this step works
- name: Download Android Emulator Image
# https://github.com/ReactiveCircus/android-emulator-runner?tab=readme-ov-file#running-hardware-accelerated-emulators-on-linux-runners
- name: Enable KVM
run: |
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-31;default;x86_64"
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd --force --name Pixel_5_API_31_AOSP --device "Nexus 5X" -k 'system-images;android-31;default;x86_64'
$ANDROID_HOME/emulator/emulator -list-avds
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

# Setup Java 17 (required by >RN0.73) for the build step
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'zulu'

- name: Cache node modules
uses: actions/cache@v4
id: cache
with:
path: node_modules
key: node-modules-${{ hashFiles('**/package-lock.json') }}

- name: Rebuild detox from cache
if: steps.cache.outputs.cache-hit == 'true'
# Currently, (Detox 20) those two commands are macOS only
Expand Down Expand Up @@ -95,40 +135,33 @@ jobs:
- name: Download a fake cv model and taxonomy file into the assets folder
run: npm run add-github-actions-test-model

# Macos-latest runner has 3 Java versions pre-installed, if not specified as here, the build step errors with requiring at least Java 11 or higher
# So, this step is needed for the apk build step, but somehow this is breaking emulator setup, so it is placed here
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'zulu'

# This is by far the longest step in this job, currently we are building the apk everytime, maybe there should be a more specific trigger for the entire job
- name: Build for detox
run: npm run e2e:build:android

# Starts the avd previously set-up by name
- name: Android Emulator
timeout-minutes: 10
continue-on-error: true
run: |
echo "Starting emulator"
nohup $ANDROID_HOME/emulator/emulator -avd Pixel_5_API_31_AOSP -no-audio -no-snapshot -no-window -no-boot-anim -timezone America/Los_Angeles &
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
$ANDROID_HOME/platform-tools/adb devices
$ANDROID_HOME/platform-tools/adb emu geo fix -121.45356 46.51119 4392 12
echo "Emulator started"

- name: Ensure servers are running
run: |
# is rails running?
curl -I --fail "https://staging.inaturalist.org/ping"
# is node running & is ES working?
curl -I --fail "https://stagingapi.inaturalist.org/v2/taxa"

# Start the Android e2e tests with extensive logging and screen captures for failing tests
- name: Android Detox
run: npm run e2e:test:android -- --take-screenshots failing --record-videos failing -l debug
- name: Check disk space
run: df . -h
# This is by far the longest step in this job, currently we are building the apk everytime, maybe there should be a more specific trigger for the entire job
- name: Build for Detox
run: npm run e2e:build:android

- name: Start Android Emulator and run Detox tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
target: aosp_atd
arch: x86_64
profile: Nexus 5X
avd-name: Pixel_5_API_34_AOSP
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -no-audio -timezone America/Los_Angeles
script: |
$ANDROID_HOME/emulator/emulator -list-avds
$ANDROID_HOME/platform-tools/adb devices
$ANDROID_HOME/platform-tools/adb emu geo fix -121.45356 46.51119 4392 12
npm run e2e:test:android -- --take-screenshots failing --record-videos failing -l debug

# The artifacts for the failing tests are available for download on github.com on the page of the individual actions run
- name: Store Detox artifacts on test failure
Expand All @@ -142,7 +175,7 @@ jobs:
name: Notify Slack
needs: test
if: ${{ success() || failure() }}
runs-on: macos-latest
runs-on: ubuntu-latest
steps:
- uses: iRoachie/slack-github-actions@v2.3.0
if: env.SLACK_WEBHOOK_URL != null
Expand Down
38 changes: 19 additions & 19 deletions e2e/signedIn.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe( "Signed in user", () => {

async function createAndUploadObservation( options = { upload: false } ) {
const addObsButton = element( by.id( "add-obs-button" ) );
await waitFor( addObsButton ).toBeVisible().withTimeout( 10000 );
await waitFor( addObsButton ).toBeVisible().withTimeout( 20000 );
await addObsButton.tap();
await expect( element( by.id( "identify-text" ) ) ).toBeVisible();
// Observe without evidence
Expand All @@ -23,7 +23,7 @@ describe( "Signed in user", () => {
// Check that the new observation screen is visible
await waitFor( element( by.id( "new-observation-text" ) ) )
.toBeVisible()
.withTimeout( 10000 );
.withTimeout( 20000 );
if ( options.upload ) {
// Press Upload now button
const uploadNowButton = element( by.id( "ObsEdit.uploadButton" ) );
Expand Down Expand Up @@ -52,7 +52,7 @@ describe( "Signed in user", () => {
by.id( "ObsStatus.commentsCount" )
.withAncestor( by.id( `MyObservations.obsListItem.${uuid}` ) )
);
await waitFor( commentCount ).toBeVisible().withTimeout( 10000 );
await waitFor( commentCount ).toBeVisible().withTimeout( 20000 );
}

return uuid;
Expand All @@ -63,29 +63,29 @@ describe( "Signed in user", () => {
await obsListItem.tap();
if ( options.uploaded ) {
const editButton = element( by.id( "ObsDetail.editButton" ) );
await waitFor( editButton ).toBeVisible().withTimeout( 10000 );
await waitFor( editButton ).toBeVisible().withTimeout( 20000 );
// Navigate to the edit screen
await editButton.tap();
}
// Check that the edit screen is visible
await waitFor( element( by.text( "EVIDENCE" ) ) )
.toBeVisible()
.withTimeout( 10000 );
.withTimeout( 20000 );
// Press header kebab menu
const headerKebabMenu = element( by.id( "KebabMenu.Button" ) );
await expect( headerKebabMenu ).toBeVisible();
await headerKebabMenu.tap();
// Press delete observation
const deleteObservation = element( by.id( "Header.delete-observation" ) );
await waitFor( deleteObservation ).toBeVisible().withTimeout( 10000 );
await waitFor( deleteObservation ).toBeVisible().withTimeout( 20000 );
await deleteObservation.tap();
// Check that the delete button is visible
const deleteObservationButton = element( by.text( "DELETE" ) );
await waitFor( deleteObservationButton ).toBeVisible().withTimeout( 10000 );
await waitFor( deleteObservationButton ).toBeVisible().withTimeout( 20000 );
// Press delete observation
await deleteObservationButton.tap();
// Make sure we're back on MyObservations
await waitFor( username ).toBeVisible().withTimeout( 10000 );
await waitFor( username ).toBeVisible().withTimeout( 20000 );
}

it( "should create an observation, add a comment, and delete the observation", async () => {
Expand All @@ -95,11 +95,11 @@ describe( "Signed in user", () => {
const loginText = element( by.id( "log-in-to-iNaturalist-button.text" ) );
// 10000 timeout is for github actions, which was failing with a
// shorter timeout period
await waitFor( loginText ).toBeVisible().withTimeout( 10000 );
await waitFor( loginText ).toBeVisible().withTimeout( 20000 );
await expect( loginText ).toBeVisible();
await element( by.id( "log-in-to-iNaturalist-button.text" ) ).tap();
const usernameInput = element( by.id( "Login.email" ) );
await waitFor( usernameInput ).toBeVisible().withTimeout( 10000 );
await waitFor( usernameInput ).toBeVisible().withTimeout( 20000 );
await expect( usernameInput ).toBeVisible();
await element( by.id( "Login.email" ) ).tap();
await element( by.id( "Login.email" ) ).typeText( Config.E2E_TEST_USERNAME );
Expand All @@ -113,22 +113,22 @@ describe( "Signed in user", () => {
const username = element( by.text( `@${Config.E2E_TEST_USERNAME}` ) ).atIndex(
1
);
await waitFor( username ).toBeVisible().withTimeout( 10000 );
await waitFor( username ).toBeVisible().withTimeout( 20000 );
await expect( username ).toBeVisible();

/*
/ 2. Switch UI to power user mode
*/
const drawerButton = element( by.id( "OPEN_DRAWER" ) );
await waitFor( drawerButton ).toBeVisible().withTimeout( 10000 );
await waitFor( drawerButton ).toBeVisible().withTimeout( 20000 );
await drawerButton.tap();
// Tap the settings drawer menu item
const settingsDrawerMenuItem = element( by.id( "settings" ) );
await waitFor( settingsDrawerMenuItem ).toBeVisible().withTimeout( 10000 );
await waitFor( settingsDrawerMenuItem ).toBeVisible().withTimeout( 20000 );
await settingsDrawerMenuItem.tap();
// Tap the settings radio button for power user mode
const powerUserRadioButton = element( by.id( "all-observation-option" ) );
await waitFor( powerUserRadioButton ).toBeVisible().withTimeout( 10000 );
await waitFor( powerUserRadioButton ).toBeVisible().withTimeout( 20000 );
await powerUserRadioButton.tap();

/*
Expand All @@ -146,11 +146,11 @@ describe( "Signed in user", () => {
const obsListItem = element( by.id( `MyObservations.obsListItem.${uuid}` ) );
await obsListItem.tap();
const commentButton = element( by.id( "ObsDetail.commentButton" ) );
await waitFor( commentButton ).toBeVisible().withTimeout( 10000 );
await waitFor( commentButton ).toBeVisible().withTimeout( 20000 );
await commentButton.tap();
// Check that the comment modal is visible
const commentModalInput = element( by.id( "ObsEdit.notes" ) );
await waitFor( commentModalInput ).toBeVisible().withTimeout( 10000 );
await waitFor( commentModalInput ).toBeVisible().withTimeout( 20000 );
// Add a comment
await commentModalInput.tap();
await commentModalInput.typeText( "This is a comment" );
Expand All @@ -163,9 +163,9 @@ describe( "Signed in user", () => {
// Check that the comment is visible
await element( by.id( `ObsDetails.${uuid}` ) ).scrollTo( "bottom" );
const comment = element( by.text( "This is a comment" ) );
await waitFor( comment ).toBeVisible().withTimeout( 10000 );
await waitFor( comment ).toBeVisible().withTimeout( 20000 );
await element( by.label( "Go back" ) ).tap( );
await waitFor( username ).toBeVisible( ).withTimeout( 10000 );
await waitFor( username ).toBeVisible( ).withTimeout( 20000 );

/*
/ 5. Delete the observations
Expand All @@ -175,6 +175,6 @@ describe( "Signed in user", () => {
// Testing for the empty list UI isn't adequate because other test runs
// happening in parallel might cause other observations to be there
const deletedObservationText = element( by.text( /1 observation deleted/ ) );
await waitFor( deletedObservationText ).toBeVisible().withTimeout( 10000 );
await waitFor( deletedObservationText ).toBeVisible().withTimeout( 20000 );
} );
} );
2 changes: 1 addition & 1 deletion e2e/signedOut.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe( "Signed out user", () => {

it( "should start at My Observations with log in text", async () => {
const loginText = element( by.id( "log-in-to-iNaturalist-button.text" ) );
await waitFor( loginText ).toBeVisible( ).withTimeout( 10000 );
await waitFor( loginText ).toBeVisible( ).withTimeout( 20000 );
await expect( loginText ).toBeVisible( );
} );

Expand Down
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ PODS:
- React-Core
- react-native-restart (0.0.27):
- React-Core
- react-native-safe-area-context (4.9.0):
- react-native-safe-area-context (4.10.4):
- React-Core
- react-native-sensitive-info (6.0.0-alpha.9):
- React-Core
Expand Down Expand Up @@ -1523,7 +1523,7 @@ SPEC CHECKSUMS:
react-native-orientation-locker: 851f6510d8046ea2f14aa169b1e01fcd309a94ba
react-native-render-html: 984dfe2294163d04bf5fe25d7c9f122e60e05ebe
react-native-restart: 7595693413fe3ca15893702f2c8306c62a708162
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
react-native-safe-area-context: 399a5859f6acbdf67f671c69b53113f535f3b5b0
react-native-sensitive-info: d44e909d065f9c0e15734245e5dd6a24b82e3dcd
react-native-slider: 09e5a8b7e766d3b5ae24ec15c5c4ec2679ca0f8c
react-native-webview: 9395e82c917d81407deb9b1fe53158dd6c8880ff
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"react-native-reanimated-carousel": "^3.5.1",
"react-native-render-html": "^6.3.4",
"react-native-restart": "^0.0.27",
"react-native-safe-area-context": "^4.9.0",
"react-native-safe-area-context": "^4.10.4",
"react-native-screens": "^3.30.1",
"react-native-sensitive-info": "^6.0.0-alpha.9",
"react-native-share-menu": "github:visoft/react-native-share-menu",
Expand Down
4 changes: 3 additions & 1 deletion src/providers/ExploreContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import { t } from "i18next";
import * as React from "react";
import { LatLng } from "react-native-maps";
import fetchUserLocation from "sharedHelpers/fetchUserLocation.ts";

// Importing this as "sharedHelpers/fetchUserLocation.ts" breaks e2e mock import
import fetchUserLocation from "../sharedHelpers/fetchUserLocation";

export enum EXPLORE_ACTION {
CHANGE_SORT_BY = "CHANGE_SORT_BY",
Expand Down
4 changes: 3 additions & 1 deletion src/sharedHooks/useCurrentObservationLocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
useState
} from "react";
import { checkMultiple, RESULTS } from "react-native-permissions";
import fetchUserLocation from "sharedHelpers/fetchUserLocation.ts";

// Importing this as "sharedHelpers/fetchUserLocation.ts" breaks e2e mock import
import fetchUserLocation from "../sharedHelpers/fetchUserLocation";

const INITIAL_POSITIONAL_ACCURACY = 99999;
const TARGET_POSITIONAL_ACCURACY = 10;
Expand Down
Loading