From f1d3f95f252f8ba18233eab6340d094bab2c55ef Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:27:27 -0300 Subject: [PATCH 1/3] feat(pos-app): Add Maestro E2E testing infrastructure - Add payment flow E2E test (new sale -> enter amount -> QR code -> open URL) - Add testID props to Button component and key interactive elements - Add GitHub Action workflow for running Maestro tests on Android - Update AGENTS.md with testing guidelines: - Avoid testing mocked components - Use testID for E2E tests - Minimal comments rule - No unused variables rule --- .../workflows/ci_e2e_tests_pos_android.yaml | 142 ++++++++++++++++++ dapps/pos-app/AGENTS.md | 11 ++ dapps/pos-app/app/amount.tsx | 1 + dapps/pos-app/app/index.tsx | 2 + dapps/pos-app/app/scan.tsx | 15 +- dapps/pos-app/components/button.tsx | 9 +- dapps/pos-app/components/numeric-keyboard.tsx | 3 +- dapps/pos-app/e2e/README.md | 83 ++++++++++ dapps/pos-app/e2e/payment-flow.yaml | 39 +++++ 9 files changed, 297 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ci_e2e_tests_pos_android.yaml create mode 100644 dapps/pos-app/e2e/README.md create mode 100644 dapps/pos-app/e2e/payment-flow.yaml diff --git a/.github/workflows/ci_e2e_tests_pos_android.yaml b/.github/workflows/ci_e2e_tests_pos_android.yaml new file mode 100644 index 00000000..080fa8af --- /dev/null +++ b/.github/workflows/ci_e2e_tests_pos_android.yaml @@ -0,0 +1,142 @@ +name: e2e-tests-pos-android + +permissions: + contents: read + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - 'dapps/pos-app/**' + pull_request: + paths: + - 'dapps/pos-app/**' + +jobs: + e2e-tests: + name: Maestro E2E Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/ci-setup + with: + root-path: dapps/pos-app + package-manager: npm + + - name: Install Java 17 + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '17' + architecture: x86_64 + + - name: Create env file + run: | + touch dapps/pos-app/.env + echo ${{ vars.POS_DEV_ENV_FILE }} >> dapps/pos-app/.env + + - name: Expo Prebuild + run: | + cd dapps/pos-app + npm run prebuild + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-pos-e2e-${{ hashFiles('dapps/pos-app/package.json', 'dapps/pos-app/app.json') }} + restore-keys: | + ${{ runner.os }}-gradle-pos-e2e- + + - name: Build Release APK + id: build + run: | + cd dapps/pos-app/android + ./gradlew assembleRelease + + - name: Enable KVM + run: | + 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 + + - name: Install Maestro + run: | + curl -Ls "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> $GITHUB_PATH + + - name: Run Maestro E2E Tests + id: maestro + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 31 + arch: x86_64 + profile: pixel_6 + heap-size: 512M + ram-size: 4096M + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim + disable-animations: true + script: | + adb install dapps/pos-app/android/app/build/outputs/apk/release/app-release.apk + $HOME/.maestro/bin/maestro test dapps/pos-app/e2e/ --format junit --output maestro-report.xml + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: maestro-test-results + path: maestro-report.xml + + - name: Send Slack notification + if: always() && !cancelled() + uses: slackapi/slack-github-action@v2.1.0 + with: + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + { + "text": "POS App E2E Test Report", + "blocks": [ + { + "type": "header", + "text": { "type": "plain_text", "text": "๐Ÿงช POS App E2E Test Report" } + }, + { + "type": "section", + "fields": [ + { "type": "mrkdwn", "text": "*Branch:*\n`${{ github.ref_name }}`" }, + { "type": "mrkdwn", "text": "*Triggered by:*\n`${{ github.actor }}`" } + ] + }, + { + "type": "section", + "fields": [ + { "type": "mrkdwn", "text": "*Build:*\n`${{ steps.build.outcome == 'success' && 'โœ… Success' || 'โŒ Failed' }}`" }, + { "type": "mrkdwn", "text": "*E2E Tests:*\n`${{ steps.maestro.outcome == 'success' && 'โœ… Passed' || 'โŒ Failed' }}`" } + ] + }, + { + "type": "section", + "fields": [ + { "type": "mrkdwn", "text": "*Overall Status:*\n`${{ job.status == 'success' && 'โœ… Success' || 'โŒ Failed' }}`" } + ] + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { "type": "plain_text", "text": "View Workflow Run" }, + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } + ] + } + ] + } diff --git a/dapps/pos-app/AGENTS.md b/dapps/pos-app/AGENTS.md index 8f7c8af9..52345844 100644 --- a/dapps/pos-app/AGENTS.md +++ b/dapps/pos-app/AGENTS.md @@ -624,10 +624,21 @@ const apiKey = await secureStorage.getItem(SECURE_STORAGE_KEYS.MERCHANT_API_KEY) - Use ESLint and Prettier for consistent formatting - Prefer functional components with hooks - Use TypeScript types/interfaces for all props and data structures +- **Minimal comments**: Do not add comments unless absolutely necessary to understand the code. Code should be self-documenting through clear naming and structure. Avoid explanatory comments that describe what the code does - the code itself should be clear enough. +- **No unused variables**: Ensure code changes do not leave unused variables, imports, or functions. ESLint will flag these - fix them before committing. - **No trailing whitespace**: New code must not have trailing whitespace at the end of lines. Most editors can be configured to remove trailing whitespace on save. - **Run lint after changing code**: Always run `npm run lint` after making code changes to ensure code quality and catch any formatting or linting issues before committing. - **Check TypeScript errors**: Always run `npx tsc --noEmit` after making code changes to check for TypeScript errors. Fix any TypeScript errors in files you've modified before committing. Note: Pre-existing TypeScript errors in other files can be ignored if they're unrelated to your changes. +### Testing Guidelines + +- **Avoid testing mocked components**: Component tests that mock underlying UI primitives (PressableScale, Pressable, QRCodeSkia, etc.) don't provide real value - they just test that mocks work correctly, not actual component behavior. For meaningful component testing, either: + - Use the real components (may require native setup) + - Focus on testing business logic in hooks/utils instead + - Use E2E tests for UI behavior +- **Focus on business logic**: Unit tests should focus on utilities, stores, services, and hooks that contain actual business logic rather than UI rendering. +- **Use testID for E2E tests**: Always use `testID` props to identify components in E2E tests (Maestro). Prefer testID over text strings as text can change and may be localized. Add testID to interactive components (buttons, inputs, etc.) that need to be targeted by E2E tests. + ## Troubleshooting ### Printer Issues diff --git a/dapps/pos-app/app/amount.tsx b/dapps/pos-app/app/amount.tsx index 180c87ca..add6e4cf 100644 --- a/dapps/pos-app/app/amount.tsx +++ b/dapps/pos-app/app/amount.tsx @@ -116,6 +116,7 @@ export default function AmountScreen() { )} />