diff --git a/.github/workflows/androidTests.yml b/.github/workflows/androidTests.yml new file mode 100644 index 000000000..0ca4a0fcc --- /dev/null +++ b/.github/workflows/androidTests.yml @@ -0,0 +1,75 @@ +name: Android Tests +on: + push: + branches: + - main + pull_request: + +env: + GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GPR_USER: ${{ secrets.GITHUB_ACTOR }} + +defaults: + run: + working-directory: ./example + +jobs: + android-tests: + name: Android Tests + runs-on: macos-latest + steps: + - name: Checkout project sources + uses: actions/checkout@v3 + + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '11' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Setup Node.js + uses: actions/setup-node@v3 + + - name: Install dependencies + run: npm i --force + + - name: Install React Native CLI + run: npm i -g react-native-cli --force + + - name: Install docker + run: | + mkdir -p ~/.docker/machine/cache + curl -Lo ~/.docker/machine/cache/boot2docker.iso https://github.com/boot2docker/boot2docker/releases/download/v19.03.12/boot2docker.iso + brew install docker docker-compose + + - name: Start colima + run: | + # Docker engine is no longer available because of licensing + # Alternative Colima is part of the github macOS runner + colima start + # Ensure colima is configured for later user + echo "DOCKER_HOST=unix://$HOME/.colima/default/docker.sock" >> $GITHUB_ENV + # Verify Docker + docker ps + + - name: Start local test server + run: docker-compose -p xmtp -f dev/local/docker-compose.yml up -d + + - name: Gradle Run Integration Tests + uses: reactivecircus/android-emulator-runner@v2 + with: + working-directory: ./example + api-level: 29 + script: | + react-native run-android + sleep 15 + cd android && ./gradlew connectedCheck + + - name: Stop local test server + run: docker-compose -p xmtp -f dev/local/docker-compose.yml down diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 9febfbbb3..47332fa9a 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -124,10 +124,15 @@ android { versionName "1.0.0" // For Espresso testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - testInstrumentationRunnerArguments useTestStorageService: 'true' + testInstrumentationRunnerArgument('useTestStorageService', 'true') + // The following argument makes the Android Test Orchestrator run its + // "pm clear" command after each test invocation. This command ensures + // that the app's state is completely cleared between tests. + testInstrumentationRunnerArgument('clearPackageData', 'true') } testOptions { + execution 'ANDROIDX_TEST_ORCHESTRATOR' managedDevices { devices { // run with ../gradlew nexusOneApi30DebugAndroidTest @@ -264,6 +269,7 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.5.0' androidTestImplementation "androidx.test.ext:junit-ktx:1.1.5" androidTestUtil "androidx.test.services:test-services:1.4.2" + androidTestUtil 'androidx.test:orchestrator:1.4.2' } apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle"); diff --git a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt b/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt index 7d319ccaf..c90219837 100644 --- a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt +++ b/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt @@ -16,7 +16,7 @@ import java.util.concurrent.TimeoutException object EspressoViewFinder { private const val CHECK_INTERVAL = 50L - private const val TIMEOUT_MS = 10 * 1000L + private const val TIMEOUT_MS = 15 * 1000L /** * Waits for the view referenced in [viewMatcher] to become visible, with a timeout of [timeOut]. If it diff --git a/example/dev/local/docker-compose.yml b/example/dev/local/docker-compose.yml new file mode 100644 index 000000000..4d1e7fadd --- /dev/null +++ b/example/dev/local/docker-compose.yml @@ -0,0 +1,39 @@ +version: "3.8" +services: + wakunode: + image: xmtp/node-go + platform: linux/amd64 + environment: + - GOWAKU-NODEKEY=8a30dcb604b0b53627a5adc054dbf434b446628d4bd1eccc681d223f0550ce67 + command: + - --ws + - --store + - --message-db-connection-string=postgres://postgres:xmtp@db:5432/postgres?sslmode=disable + - --message-db-reader-connection-string=postgres://postgres:xmtp@db:5432/postgres?sslmode=disable + - --lightpush + - --filter + - --ws-port=9001 + - --wait-for-db=30s + - --api.authn.enable + ports: + - 9001:9001 + - 5555:5555 # http message API + - 5556:5556 # grpc message API + depends_on: + - db + healthcheck: + test: ["CMD", "lsof", "-i", ":5556"] + interval: 3s + timeout: 10s + retries: 5 + db: + image: postgres:13 + environment: + POSTGRES_PASSWORD: xmtp + js: + restart: always + depends_on: + wakunode: + condition: service_healthy + build: ./test + \ No newline at end of file diff --git a/example/dev/local/test/Dockerfile b/example/dev/local/test/Dockerfile new file mode 100644 index 000000000..27716fe2c --- /dev/null +++ b/example/dev/local/test/Dockerfile @@ -0,0 +1,7 @@ +FROM node:19-alpine + +WORKDIR /code +ADD script.js script.js +RUN apk update && apk add git +RUN npm install @xmtp/xmtp-js ethers +CMD ["node", "script.js"] diff --git a/example/dev/local/test/script.js b/example/dev/local/test/script.js new file mode 100644 index 000000000..8dd5c1c1d --- /dev/null +++ b/example/dev/local/test/script.js @@ -0,0 +1,35 @@ +let Client = require("@xmtp/xmtp-js").Client; +let Wallet = require("ethers").Wallet; + +console.log("NODE VERSION", process.version); + +// 0xf4BF19Ed562651837bc11ff975472ABd239D35B5 +const keyBytes = [ + 80, 7, 53, 52, 122, 163, 75, 130, 199, 86, 216, 14, 29, 2, 255, 71, 121, 51, + 165, 3, 208, 178, 193, 207, 223, 217, 75, 247, 84, 78, 204, 3, +]; + +async function checkAll() { + const wallet = new Wallet(keyBytes); + const client = await Client.create(wallet, { + apiUrl: "http://wakunode:5555", + }); + + console.log("Listening…"); + + try { + for await (const message of await client.conversations.streamAllMessages()) { + if (message.senderAddress === wallet.address) { + continue; + } + + await message.conversation.send("HI " + message.senderAddress); + console.log(`Replied to ${message.senderAddress}`); + } + } catch (e) { + console.info(`Error:`, e); + await checkAll(); + } +} + +checkAll().then(() => console.log("Done"));