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..1ed52b84 --- /dev/null +++ b/.github/workflows/ci_e2e_tests_pos_android.yaml @@ -0,0 +1,144 @@ +name: e2e-tests-pos-android + +permissions: + contents: read + +on: + workflow_dispatch: + push: + branches: + - main + - develop + 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: | + if [ -n "${{ vars.POS_DEV_ENV_FILE }}" ]; then + echo "${{ vars.POS_DEV_ENV_FILE }}" > dapps/pos-app/.env 2>/dev/null + fi + + - 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..27f417ac 100644 --- a/dapps/pos-app/AGENTS.md +++ b/dapps/pos-app/AGENTS.md @@ -624,10 +624,25 @@ 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. +### Security Guidelines + +- **Never print secrets in CI logs**: When creating or modifying GitHub Actions workflows, ensure that environment variables, API keys, secrets, or any sensitive data are never printed to CI logs. Use output redirection (`2>/dev/null`), avoid `echo` statements that output secrets, and ensure file creation commands don't expose content. Always verify that sensitive values stored in GitHub secrets/variables are not accidentally logged during workflow execution. + +### 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() { )} />