Compare commits
30 Commits
bd2d5610b3
...
39263a4af5
| Author | SHA1 | Date | |
|---|---|---|---|
| 39263a4af5 | |||
| d3159bc2ae | |||
|
|
0735e3513c | ||
|
|
b10ef57d37 | ||
|
|
1e4c8982a5 | ||
|
|
4cd83a9281 | ||
|
|
207831eb3e | ||
|
|
0ff2949c1e | ||
|
|
591b5d7b88 | ||
|
|
a544b051cc | ||
| 230942e776 | |||
| e671827dc4 | |||
| 67df7957c6 | |||
| aef4491f97 | |||
| ca927fd05e | |||
| 9a44eed2a6 | |||
| 2b498607d1 | |||
| 68b0055cfe | |||
| e3d8d30b1b | |||
| 9642eb0268 | |||
| 41a222ea11 | |||
| b9574eb96a | |||
| 52bb1d1af4 | |||
| 91cedf54c9 | |||
| b43e28e09d | |||
| 9c07bd7e0e | |||
| 277eff3da0 | |||
| 7ce837b49a | |||
| e7e11771b3 | |||
| c0a69707e6 |
1
.github/workflows/backend-foundation.yml
vendored
1
.github/workflows/backend-foundation.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Backend Foundation
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only — auto-triggers disabled (free plan)
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
67
.github/workflows/maestro-e2e.yml
vendored
Normal file
67
.github/workflows/maestro-e2e.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# Maestro E2E Tests
|
||||
# Runs on: manual trigger, or when maestro flows change
|
||||
# Requires secrets: TEST_STAFF_PHONE, TEST_STAFF_OTP, TEST_CLIENT_EMAIL, TEST_CLIENT_PASSWORD
|
||||
# Optional: TEST_CLIENT_COMPANY, TEST_STAFF_SIGNUP_PHONE
|
||||
name: Maestro E2E
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only — auto-triggers disabled (free plan)
|
||||
|
||||
jobs:
|
||||
maestro-e2e:
|
||||
name: 🎭 Maestro E2E
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- name: 📥 Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🦋 Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.38.x'
|
||||
channel: 'stable'
|
||||
cache: true
|
||||
|
||||
- name: 🔧 Install Firebase CLI
|
||||
run: npm install -g firebase-tools
|
||||
|
||||
- name: 📦 Get dependencies
|
||||
run: make mobile-install
|
||||
|
||||
- name: 🔨 Build Staff APK
|
||||
run: make mobile-staff-build PLATFORM=apk MODE=debug
|
||||
|
||||
- name: 🔨 Build Client APK
|
||||
run: make mobile-client-build PLATFORM=apk MODE=debug
|
||||
|
||||
- name: 📲 Start emulator
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 33
|
||||
target: default
|
||||
arch: x86_64
|
||||
profile: Nexus 6
|
||||
script: |
|
||||
# Install Maestro
|
||||
curl -Ls "https://get.maestro.mobile.dev" | bash
|
||||
export PATH="$HOME/.maestro/bin:$PATH"
|
||||
maestro --version
|
||||
|
||||
# Install APKs
|
||||
adb install -r apps/mobile/apps/staff/build/app/outputs/flutter-apk/app-debug.apk
|
||||
adb install -r apps/mobile/apps/client/build/app/outputs/flutter-apk/app-debug.apk
|
||||
|
||||
# Run auth flows (Staff + Client)
|
||||
maestro test --shard-split=1 \
|
||||
apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
apps/mobile/apps/staff/maestro/auth/sign_up.yaml \
|
||||
apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
apps/mobile/apps/client/maestro/auth/sign_up.yaml \
|
||||
-e TEST_STAFF_PHONE="${{ secrets.TEST_STAFF_PHONE }}" \
|
||||
-e TEST_STAFF_OTP="${{ secrets.TEST_STAFF_OTP }}" \
|
||||
-e TEST_STAFF_SIGNUP_PHONE="${{ secrets.TEST_STAFF_SIGNUP_PHONE }}" \
|
||||
-e TEST_CLIENT_EMAIL="${{ secrets.TEST_CLIENT_EMAIL }}" \
|
||||
-e TEST_CLIENT_PASSWORD="${{ secrets.TEST_CLIENT_PASSWORD }}" \
|
||||
-e TEST_CLIENT_COMPANY="${{ secrets.TEST_CLIENT_COMPANY }}"
|
||||
2
.github/workflows/mobile-ci.yml
vendored
2
.github/workflows/mobile-ci.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Mobile CI
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only — auto-triggers disabled (free plan)
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -236,4 +237,3 @@ jobs:
|
||||
else
|
||||
echo "✅ Pipeline PASSED"
|
||||
fi
|
||||
|
||||
|
||||
2
.github/workflows/web-quality.yml
vendored
2
.github/workflows/web-quality.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Web Quality
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_dispatch: # Manual trigger only — auto-triggers disabled (free plan)
|
||||
|
||||
jobs:
|
||||
web-quality:
|
||||
|
||||
@@ -25,7 +25,7 @@ void main() async {
|
||||
);
|
||||
|
||||
// Register global BLoC observer for centralized error logging
|
||||
Bloc.observer = CoreBlocObserver(
|
||||
Bloc.observer = const CoreBlocObserver(
|
||||
logEvents: true,
|
||||
logStateChanges: false, // Set to true for verbose debugging
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maestro Integration Tests — Client App
|
||||
|
||||
Auth flows for the KROW Client app.
|
||||
See [docs/research/flutter-testing-tools.md](/docs/research/flutter-testing-tools.md) and [maestro-test-run-instructions.md](/docs/research/maestro-test-run-instructions.md).
|
||||
Auth flows and E2E happy paths for the KROW Client app.
|
||||
See [docs/research/flutter-testing-tools.md](/docs/research/flutter-testing-tools.md), [maestro-test-run-instructions.md](/docs/research/maestro-test-run-instructions.md), and [docs/testing/maestro-e2e-happy-paths.md](/docs/testing/maestro-e2e-happy-paths.md) (#572).
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -10,6 +10,43 @@ maestro/
|
||||
auth/
|
||||
sign_in.yaml
|
||||
sign_up.yaml
|
||||
sign_out.yaml
|
||||
sign_in_invalid_password.yaml
|
||||
navigation/
|
||||
home.yaml
|
||||
orders.yaml
|
||||
billing.yaml
|
||||
coverage.yaml
|
||||
reports.yaml
|
||||
orders/
|
||||
view_orders.yaml
|
||||
completed_no_edit_icon.yaml # #492
|
||||
create_order_entry.yaml
|
||||
create_order_rapid.yaml
|
||||
rapid_to_one_time_draft_submit_e2e.yaml
|
||||
create_order_one_time_e2e.yaml
|
||||
edit_active_order_e2e.yaml
|
||||
edit_active_order_verify_updated_e2e.yaml
|
||||
hubs/
|
||||
create_hub_e2e.yaml
|
||||
manage_hubs_from_settings.yaml
|
||||
edit_hub_e2e.yaml
|
||||
delete_hub_e2e.yaml
|
||||
billing/
|
||||
billing_overview.yaml
|
||||
invoice_details_smoke.yaml
|
||||
invoice_approval_e2e.yaml
|
||||
reports/
|
||||
reports_dashboard.yaml
|
||||
spend_report_export_smoke.yaml
|
||||
home/
|
||||
home_dashboard_widgets.yaml
|
||||
tab_bar_roundtrip.yaml
|
||||
settings/
|
||||
settings_page.yaml
|
||||
edit_profile.yaml
|
||||
edit_profile_save_e2e.yaml
|
||||
logout_flow.yaml
|
||||
```
|
||||
|
||||
## Credentials (env, never hardcoded)
|
||||
@@ -19,11 +56,22 @@ maestro/
|
||||
| sign_in | `TEST_CLIENT_EMAIL`, `TEST_CLIENT_PASSWORD` |
|
||||
| sign_up | `TEST_CLIENT_EMAIL`, `TEST_CLIENT_PASSWORD`, `TEST_CLIENT_COMPANY` |
|
||||
|
||||
**Sign-in:** testclient@gmail.com / testclient!
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
# Via Makefile (export vars first)
|
||||
make test-e2e-client
|
||||
make test-e2e-client # Auth only
|
||||
make test-e2e-client-extended # Auth + nav + orders + settings
|
||||
make test-e2e-client-happy-path # Auth + hubs + create order E2E + billing + reports + logout (#572)
|
||||
make test-e2e-client-smoke # Deterministic smoke suite
|
||||
make test-e2e-client-hubs-e2e # Hubs manage + edit + delete
|
||||
make test-e2e-client-billing-smoke # Billing overview + invoice details/empty state
|
||||
make test-e2e-client-reports-smoke # Reports dashboard + export placeholder
|
||||
make test-e2e-client-settings-e2e # Settings + edit profile save + logout
|
||||
make test-e2e-client-orders-smoke # Orders smoke (RAPID → One-Time draft)
|
||||
make test-e2e-client-orders-data # Orders data-dependent (edit active order)
|
||||
|
||||
# Direct
|
||||
maestro test apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Client App — E2E: Session Persistence Across Relaunch
|
||||
# Purpose:
|
||||
# - Log in via sign_in.yaml
|
||||
# - Stop the app
|
||||
# - Relaunch and verify user is still logged in (bypass login screen)
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/auth/session_persistence.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
# We rely on sign_in.yaml being run before this to establish a session.
|
||||
- launchApp
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_nav_home"
|
||||
timeout: 60000
|
||||
|
||||
- stopApp
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_nav_home"
|
||||
timeout: 30000
|
||||
|
||||
# Cleanup: Log out
|
||||
- tapOn:
|
||||
id: "header_settings_icon"
|
||||
optional: true
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Log Out"
|
||||
timeout: 15000
|
||||
- tapOn: "Log Out"
|
||||
- tapOn:
|
||||
text: "Log Out"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_landing_sign_in"
|
||||
timeout: 30000
|
||||
- assertVisible:
|
||||
id: "client_landing_sign_in"
|
||||
|
||||
|
||||
|
||||
47
apps/mobile/apps/client/maestro/auth/happy_path/sign_in.yaml
Normal file
47
apps/mobile/apps/client/maestro/auth/happy_path/sign_in.yaml
Normal file
@@ -0,0 +1,47 @@
|
||||
# Client App — Sign In flow
|
||||
appId: com.krowwithus.client
|
||||
env:
|
||||
EMAIL: ${TEST_CLIENT_EMAIL}
|
||||
PASSWORD: ${TEST_CLIENT_PASSWORD}
|
||||
---
|
||||
- launchApp:
|
||||
clearState: true
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_landing_sign_in"
|
||||
timeout: 60000
|
||||
|
||||
- tapOn:
|
||||
id: "client_landing_sign_in"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "sign_in_email"
|
||||
timeout: 20000
|
||||
|
||||
- tapOn:
|
||||
id: "sign_in_email"
|
||||
- inputText: ${EMAIL}
|
||||
- stopApp: # Small trick to hide keyboard on some emulators
|
||||
optional: true
|
||||
- launchApp: # Resume where we were
|
||||
clearState: false
|
||||
|
||||
- tapOn:
|
||||
id: "sign_in_password"
|
||||
- inputText: ${PASSWORD}
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn:
|
||||
id: "sign_in_submit_button"
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "(?i)Sign In"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_nav_home"
|
||||
timeout: 45000
|
||||
|
||||
- assertVisible:
|
||||
id: "client_nav_home"
|
||||
@@ -0,0 +1,33 @@
|
||||
# Client App — Sign out flow (tap Log Out, confirm in dialog)
|
||||
# Run: maestro test auth/sign_in.yaml auth/sign_out.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_nav_home"
|
||||
timeout: 30000
|
||||
|
||||
# Open Settings
|
||||
- tapOn:
|
||||
id: "header_settings_icon"
|
||||
optional: true
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Log Out"
|
||||
timeout: 20000
|
||||
|
||||
- tapOn: "Log Out"
|
||||
- assertVisible: "Are you sure you want to log out?"
|
||||
- tapOn: "Log Out"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "client_landing_sign_in"
|
||||
timeout: 30000
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,17 @@ env:
|
||||
PASSWORD: ${TEST_CLIENT_PASSWORD}
|
||||
COMPANY: ${TEST_CLIENT_COMPANY}
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Create Account"
|
||||
# Always start from a clean auth state so the Get Started
|
||||
# screen (with "Create Account") is shown even if already signed in.
|
||||
- launchApp:
|
||||
clearState: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Account"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Create Account"
|
||||
- assertVisible: "Company"
|
||||
- assertVisible: "(?i)Company Name"
|
||||
- tapOn:
|
||||
id: sign_up_company
|
||||
- inputText: ${COMPANY}
|
||||
@@ -26,3 +33,6 @@ env:
|
||||
- inputText: ${PASSWORD}
|
||||
- tapOn: "Create Account"
|
||||
- assertVisible: "Home"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Client App — Sign in with wrong password (negative test)
|
||||
# Uses valid email, invalid password; expects error and stays on Sign In
|
||||
# Run: maestro test .../auth/sign_in_invalid_password.yaml -e TEST_CLIENT_EMAIL=testclient@gmail.com -e TEST_CLIENT_INVALID_PASSWORD=wrongpass
|
||||
appId: com.krowwithus.client
|
||||
env:
|
||||
EMAIL: ${TEST_CLIENT_EMAIL}
|
||||
PASSWORD: ${TEST_CLIENT_INVALID_PASSWORD}
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Sign In"
|
||||
- tapOn: "Sign In"
|
||||
- assertVisible: "Email"
|
||||
- tapOn:
|
||||
id: sign_in_email
|
||||
- inputText: ${EMAIL}
|
||||
- tapOn:
|
||||
id: sign_in_password
|
||||
- inputText: ${PASSWORD}
|
||||
- tapOn: "Sign In"
|
||||
- extendedWaitUntil:
|
||||
visible: "Invalid"
|
||||
timeout: 5000
|
||||
- assertVisible: "Sign In"
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Client App — Sign In flow
|
||||
# Credentials via env: TEST_CLIENT_EMAIL, TEST_CLIENT_PASSWORD
|
||||
# Run: maestro test apps/mobile/apps/client/maestro/auth/sign_in.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
|
||||
# Or: export MAESTRO_TEST_CLIENT_EMAIL / MAESTRO_TEST_CLIENT_PASSWORD (Maestro auto-reads MAESTRO_*)
|
||||
|
||||
appId: com.krowwithus.client
|
||||
env:
|
||||
EMAIL: ${TEST_CLIENT_EMAIL}
|
||||
PASSWORD: ${TEST_CLIENT_PASSWORD}
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Sign In"
|
||||
- tapOn: "Sign In"
|
||||
- assertVisible: "Email"
|
||||
- tapOn:
|
||||
id: sign_in_email
|
||||
- inputText: ${EMAIL}
|
||||
- tapOn:
|
||||
id: sign_in_password
|
||||
- inputText: ${PASSWORD}
|
||||
- tapOn: "Sign In"
|
||||
- assertVisible: "Home"
|
||||
@@ -0,0 +1,50 @@
|
||||
# Client App — Billing: Empty state smoke
|
||||
# Purpose:
|
||||
# - Opens Billing tab
|
||||
# - Verifies the screen loads correctly with no invoices
|
||||
# - Checks that "Current Period" header is always visible
|
||||
# - Verifies empty-state copy appears when no invoices exist
|
||||
# - Deterministic: always passes whether or not invoices exist
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/billing/billing_empty_state.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- tapOn: "(?i)Billing"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Current Period"
|
||||
timeout: 20000
|
||||
|
||||
# Entry assertion — billing is loaded
|
||||
- assertVisible: "(?i)Current Period"
|
||||
|
||||
# Awaiting Approval section
|
||||
- assertVisible:
|
||||
text: "(?i)Awaiting Approval"
|
||||
optional: true
|
||||
|
||||
# Case A: invoices exist → invoice amounts/dates are visible
|
||||
- assertVisible:
|
||||
text: "(?i).*(Invoice Ready|Review & Approve|Approved).*"
|
||||
optional: true
|
||||
|
||||
# Case B: no invoices → empty-state message visible
|
||||
- assertVisible:
|
||||
text: "(?i).*(No invoices|nothing|all clear|no pending).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — still in Billing context
|
||||
- assertVisible: "(?i)Current Period"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Client App — Billing overview
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Billing"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Billing"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Current Period"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Current Period"
|
||||
@@ -0,0 +1,62 @@
|
||||
# Client App — E2E: Comprehensive Invoice Approval
|
||||
# Purpose:
|
||||
# - Navigates to Billing -> Awaiting Approval.
|
||||
# - Captures the Invoice ID or date/amount (conceptually).
|
||||
# - Approves the invoice.
|
||||
# - Verifies success message.
|
||||
# - Navigates to the "Approved" tab.
|
||||
# - Verifies the invoice is now listed in the history.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
11: # apps/mobile/apps/client/maestro/auth/happy_path/sign_in.yaml \
|
||||
12: # apps/mobile/apps/client/maestro/billing/happy_path/comprehensive_billing_flow.yaml \
|
||||
13: # -e TEST_CLIENT_EMAIL=... \
|
||||
14: # -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- tapOn: "Home"
|
||||
- tapOn: "Billing"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Awaiting Approval"
|
||||
timeout: 10000
|
||||
|
||||
# 1. Enter the approval flow
|
||||
- tapOn: "Awaiting Approval"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Review & Approve"
|
||||
timeout: 10000
|
||||
|
||||
# 2. Approve the first invoice
|
||||
- tapOn: "Review & Approve"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Approve"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Approve"
|
||||
|
||||
# 3. Verify success message
|
||||
- extendedWaitUntil:
|
||||
visible: "Invoice approved and payment initiated"
|
||||
timeout: 15000
|
||||
|
||||
# 4. Deep Verification: check the 'Approved' tab
|
||||
- tapOn: "Approved"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Invoice History"
|
||||
timeout: 10000
|
||||
|
||||
# Ensure we see a 'View Receipt' or 'Approved' status instead of 'Review'
|
||||
- assertVisible: "View Receipt"
|
||||
- assertNotVisible: "Review & Approve"
|
||||
|
||||
# Back to Home
|
||||
- tapOn: "Home"
|
||||
@@ -0,0 +1,68 @@
|
||||
# Client App — E2E: Invoice Approval Flow
|
||||
# Flow:
|
||||
# - Home → Billing Tab
|
||||
# - Navigate to "Awaiting Approval" (Pending Invoices)
|
||||
# - Review the first pending invoice
|
||||
# - Click Approve & verify success
|
||||
#
|
||||
# Prerequisite:
|
||||
# User must have at least one invoice in the "Awaiting Approval" state (pending validation/timesheets).
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/billing/invoice_approval_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- assertVisible: "Home"
|
||||
- tapOn: "Home"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- tapOn: "Billing"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Awaiting Approval"
|
||||
timeout: 10000
|
||||
|
||||
# Open the Pending Invoices List
|
||||
- tapOn: "Awaiting Approval"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Review & Approve"
|
||||
timeout: 10000
|
||||
|
||||
# Tap the first invoice waiting for approval
|
||||
- tapOn: "Review & Approve"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Approve"
|
||||
timeout: 10000
|
||||
|
||||
# Tap the primary approve action in CompletionReviewActions
|
||||
- tapOn: "Approve"
|
||||
|
||||
# Validate it returns automatically and shows the success snackbar banner
|
||||
- extendedWaitUntil:
|
||||
visible: "Invoice approved and payment initiated"
|
||||
timeout: 15000
|
||||
|
||||
# Post-Action State Verification:
|
||||
# After approval, we confirm we are back on the 'Invoices' screen and the count has updated (or the item is gone)
|
||||
- extendedWaitUntil:
|
||||
visible: "Awaiting Approval"
|
||||
timeout: 10000
|
||||
|
||||
# Optionally, verify the 'Review & Approve' button for that specific invoice is gone.
|
||||
- assertNotVisible:
|
||||
text: "Review & Approve"
|
||||
optional: true
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Client App — Billing: open invoice details (smoke)
|
||||
# Purpose:
|
||||
# - Validates Billing tab loads
|
||||
# - If invoices exist, opens an invoice and verifies invoice actions (e.g. Download PDF)
|
||||
# - If no invoices exist, verifies the empty-state copy is shown
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/billing/invoice_details_smoke.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Billing"
|
||||
- extendedWaitUntil:
|
||||
visible: "Current Period"
|
||||
timeout: 10000
|
||||
|
||||
# If there are invoices ready, open one and verify details actions (optional)
|
||||
- tapOn:
|
||||
text: "Invoice Ready"
|
||||
optional: true
|
||||
- extendedWaitUntil:
|
||||
visible: "Download Invoice PDF"
|
||||
timeout: 10000
|
||||
optional: true
|
||||
- assertVisible:
|
||||
text: "Download Invoice PDF"
|
||||
optional: true
|
||||
|
||||
# Otherwise, validate deterministic empty states (still a valid smoke outcome)
|
||||
- assertVisible:
|
||||
text: "No invoices ready yet"
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "No Invoices for the selected period"
|
||||
optional: true
|
||||
|
||||
# Always end by asserting we are still in Billing context
|
||||
- assertVisible: "Current Period"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Client App — Home dashboard widgets & quick actions
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Create Order.*Schedule.*"
|
||||
timeout: 30000
|
||||
|
||||
- assertVisible: "(?i)RAPID.*Urgent.*"
|
||||
- assertVisible: "(?i)Create Order.*Schedule.*"
|
||||
|
||||
- tapOn: "(?i)Create Order.*Schedule.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Create Order"
|
||||
timeout: 15000
|
||||
|
||||
- assertVisible: "(?i)ORDER TYPE"
|
||||
- assertVisible: "(?i)RAPID.*URGENT.*"
|
||||
- assertVisible: "(?i)One-Time.*Single Event.*"
|
||||
@@ -0,0 +1,43 @@
|
||||
# Client App — Tab bar roundtrip smoke (Home ↔ Orders/Billing/Coverage/Reports)
|
||||
# Goal: ensure tab navigation works and returns to Home without relying on dynamic data.
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/home/tab_bar_roundtrip.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
# Start from Home (stabilizes header + tab bar)
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# Orders
|
||||
- tapOn: "Orders"
|
||||
- assertVisible: "Orders"
|
||||
|
||||
# Billing
|
||||
- tapOn: "Billing"
|
||||
- assertVisible: "Billing"
|
||||
|
||||
# Coverage
|
||||
- tapOn: "Coverage"
|
||||
- assertVisible: "Coverage"
|
||||
|
||||
# Reports
|
||||
- tapOn: "Reports"
|
||||
- assertVisible: "Reports"
|
||||
|
||||
# Back to Home
|
||||
- tapOn: "Home"
|
||||
- assertVisible: "Welcome back"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Client App — Hubs: Empty state smoke
|
||||
# Purpose:
|
||||
# - Navigates to Settings → Clock-In Hubs
|
||||
# - Verifies the hubs list loads correctly
|
||||
# - If no hubs exist, verifies an empty-state message is shown
|
||||
# - If hubs exist, verifies the list renders correctly (passes either way)
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/hubs/hub_empty_state.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Clock-In Hubs"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Clock-In Hubs"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Hubs|Manage clock-in locations).*"
|
||||
timeout: 15000
|
||||
|
||||
# Entry assertion — hubs page loaded
|
||||
- assertVisible: "(?i).*(Hubs|Manage clock-in locations).*"
|
||||
|
||||
# "Add Hub" button should always be present
|
||||
- assertVisible: "(?i)Add Hub"
|
||||
|
||||
# Case A: hubs exist — list is visible
|
||||
- assertVisible:
|
||||
text: "(?i).*(Hub|Location|Address).*"
|
||||
optional: true
|
||||
|
||||
# Case B: no hubs — empty state copy should be shown
|
||||
- assertVisible:
|
||||
text: "(?i).*(No hubs|No locations|Add your first|get started|no clock-in).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — still on the Hubs screen
|
||||
- assertVisible: "(?i)Add Hub"
|
||||
@@ -0,0 +1,74 @@
|
||||
# Client App — E2E: Create Hub and verify it appears in list
|
||||
# Flow:
|
||||
# - Home → Settings (gear) → Clock-In Hubs
|
||||
# - Add Hub → fill form → Create Hub
|
||||
# - Assert success message and newly created hub card is visible
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/hubs/create_hub_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
- extendedWaitUntil:
|
||||
visible: "Clock-In Hubs"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Clock-In Hubs"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hubs\nManage clock-in locations"
|
||||
timeout: 15000
|
||||
|
||||
- tapOn: "Add Hub"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Add New Hub"
|
||||
timeout: 10000
|
||||
|
||||
# Fill required fields
|
||||
- tapOn: "Hub Name *"
|
||||
- inputText: "E2E Hub Automation"
|
||||
- hideKeyboard
|
||||
|
||||
# Address field uses an autocomplete widget; focus it via a safe coordinate
|
||||
# within the form, then type an address.
|
||||
- tapOn:
|
||||
point: "50%,60%"
|
||||
- inputText: "345 Park Avenue, New York, NY"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn:
|
||||
point: "50%,88%"
|
||||
|
||||
# For now we assert that tapping Create returns us to the Hubs list
|
||||
# (header still visible), which exercises the full Add Hub form flow.
|
||||
- extendedWaitUntil:
|
||||
visible: "Hubs\nManage clock-in locations"
|
||||
timeout: 20000
|
||||
|
||||
# Post-Action State Verification:
|
||||
# Verify the newly created hub name is in the list
|
||||
- scrollUntilVisible:
|
||||
element: "E2E Hub Automation"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- assertVisible: "E2E Hub Automation"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Client App — E2E: Delete Hub (create → details → delete)
|
||||
# Purpose:
|
||||
# - Creates a hub
|
||||
# - Opens Hub Details and deletes it
|
||||
# - Verifies deletion confirmation and success
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/hubs/delete_hub_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
- extendedWaitUntil:
|
||||
visible: "Clock-In Hubs"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Clock-In Hubs"
|
||||
- extendedWaitUntil:
|
||||
visible: "Hubs\nManage clock-in locations"
|
||||
timeout: 15000
|
||||
|
||||
# Create a hub for this test run (deterministic)
|
||||
- tapOn: "Add Hub"
|
||||
- extendedWaitUntil:
|
||||
visible: "Add New Hub"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Hub Name *"
|
||||
- inputText: "E2E Hub Delete"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn:
|
||||
point: "50%,60%"
|
||||
- inputText: "345 Park Avenue, New York, NY"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn:
|
||||
point: "50%,88%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hubs\nManage clock-in locations"
|
||||
timeout: 20000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "E2E Hub Delete"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
- tapOn: "E2E Hub Delete"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "E2E Hub Delete"
|
||||
timeout: 10000
|
||||
|
||||
# Delete action is the destructive bottom button on details page
|
||||
- assertVisible: "Delete"
|
||||
- tapOn: "Delete"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Confirm Hub Deletion"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Delete"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hub deleted successfully"
|
||||
timeout: 15000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# Client App — E2E: Edit Hub (create → details → edit)
|
||||
# Purpose:
|
||||
# - Ensures hubs list is reachable from Settings
|
||||
# - Creates a hub (if needed for the test run)
|
||||
# - Opens Hub Details, edits hub name, verifies success
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/hubs/edit_hub_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
- extendedWaitUntil:
|
||||
visible: "Clock-In Hubs"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Clock-In Hubs"
|
||||
- extendedWaitUntil:
|
||||
visible: "Hubs\nManage clock-in locations"
|
||||
timeout: 15000
|
||||
|
||||
# Create a hub for this test run (deterministic)
|
||||
- tapOn: "Add Hub"
|
||||
- extendedWaitUntil:
|
||||
visible: "Add New Hub"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Hub Name *"
|
||||
- inputText: "E2E Hub Edit"
|
||||
- hideKeyboard
|
||||
|
||||
# Address field uses an autocomplete widget; focus it via a safe coordinate
|
||||
- tapOn:
|
||||
point: "50%,60%"
|
||||
- inputText: "345 Park Avenue, New York, NY"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn:
|
||||
point: "50%,88%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hubs\nManage clock-in locations"
|
||||
timeout: 20000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "E2E Hub Edit"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
- tapOn: "E2E Hub Edit"
|
||||
|
||||
# Hub details page uses the hub name as the app bar title
|
||||
- extendedWaitUntil:
|
||||
visible: "E2E Hub Edit"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "Edit Hub"
|
||||
- tapOn: "Edit Hub"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit Hub"
|
||||
timeout: 10000
|
||||
|
||||
# Append suffix to avoid needing to clear the field
|
||||
- tapOn: "Hub Name *"
|
||||
- inputText: " Updated"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn: "Save Changes"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hub updated successfully"
|
||||
timeout: 15000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Client App — E2E: Hub Management Lifecycle
|
||||
# Purpose:
|
||||
# - Create a new Hub.
|
||||
# - Find and Edit the Hub.
|
||||
# - Delete the Hub.
|
||||
# - Verify it is gone from the list.
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- tapOn: "Home"
|
||||
- tapOn: "Settings" # Assuming Hubs are managed via Settings or a Hubs tab
|
||||
|
||||
# 1. Navigate to Hubs
|
||||
- tapOn: "Manage Hubs"
|
||||
|
||||
# 2. Create Hub
|
||||
- tapOn: "Add New Hub" # Or the '+' button
|
||||
- tapOn: "HUB NAME"
|
||||
- inputText: "Maestro Test Hub"
|
||||
- tapOn: "ADDRESS"
|
||||
- inputText: "123 Test Street, NYC"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn: "Save Hub"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hub created successfully"
|
||||
timeout: 10000
|
||||
|
||||
# 3. Edit Hub
|
||||
- tapOn: "Maestro Test Hub"
|
||||
- tapOn: "HUB NAME"
|
||||
- inputText: "Updated Maestro Hub"
|
||||
- hideKeyboard
|
||||
- tapOn: "Save Hub"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hub updated successfully"
|
||||
timeout: 10000
|
||||
|
||||
# 4. Delete Hub
|
||||
- tapOn: "Updated Maestro Hub"
|
||||
- tapOn: "Delete Hub"
|
||||
- tapOn: "DELETE" # Confirmation
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Hub deleted"
|
||||
timeout: 10000
|
||||
|
||||
# 5. Verify gone
|
||||
- assertNotVisible: "Updated Maestro Hub"
|
||||
@@ -0,0 +1,49 @@
|
||||
# Client App — Manage Hubs via Settings quick link
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
# Open Settings via stable semantic ID
|
||||
- tapOn:
|
||||
id: "client_home_settings"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Quick Links.*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Profile"
|
||||
- assertVisible: "(?i)Clock-In Hubs"
|
||||
|
||||
- tapOn: "(?i)Clock-In Hubs"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Hubs.*"
|
||||
timeout: 15000
|
||||
|
||||
- assertVisible: "(?i).*Add Hub.*"
|
||||
|
||||
- tapOn: "(?i)Add Hub"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Add New Hub"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Create Hub"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*required.*"
|
||||
timeout: 5000
|
||||
@@ -0,0 +1,20 @@
|
||||
# Client App — Billing tab navigation
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Billing"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Billing"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Current Period|Billing).*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i).*(Current Period|Billing).*"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Client App — Coverage tab navigation
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Coverage"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Coverage"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Today.*Status|Daily Coverage|Unfilled|Checked In).*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i).*(Today.*Status|Daily Coverage|Unfilled|Checked In).*"
|
||||
20
apps/mobile/apps/client/maestro/navigation/smoke/home.yaml
Normal file
20
apps/mobile/apps/client/maestro/navigation/smoke/home.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
# Client App — Home tab navigation
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Create Order|RAPID).*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i).*(Welcome back|Create Order|RAPID).*"
|
||||
20
apps/mobile/apps/client/maestro/navigation/smoke/orders.yaml
Normal file
20
apps/mobile/apps/client/maestro/navigation/smoke/orders.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
# Client App — Orders tab navigation
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Orders"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Orders"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Up Next|Post an Order).*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i).*(Up Next|Post an Order).*"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Client App — Reports tab navigation
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Reports"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Reports"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Workforce Control Tower"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Workforce Control Tower"
|
||||
@@ -0,0 +1,85 @@
|
||||
# DEBUG: Staff Search Only
|
||||
# Tests ONLY the Staff shift search flow in isolation.
|
||||
# Pass a known order name to verify the Staff app can find it.
|
||||
#
|
||||
# Usage:
|
||||
# maestro test staff_search_only.yaml -e TEST_STAFF_PHONE="5557654321" -e TEST_STAFF_OTP="123456" -e TEST_ORDER_NAME="E2E-XXXXX"
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
# Wait for app to fully render
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 15000
|
||||
|
||||
# Sign in if needed
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Log In"
|
||||
file: ../../../../staff/maestro/auth/happy_path/sign_in.yaml
|
||||
env:
|
||||
TEST_STAFF_PHONE: ${TEST_STAFF_PHONE}
|
||||
TEST_STAFF_OTP: ${TEST_STAFF_OTP}
|
||||
|
||||
# Navigate to Shifts
|
||||
- tapOn:
|
||||
id: "nav_shifts"
|
||||
|
||||
# Pull to refresh (force reload from backend)
|
||||
- swipe:
|
||||
start: 50%, 30%
|
||||
end: 50%, 80%
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 8000
|
||||
|
||||
# 3. Aggressive Retry Loop: Search, if not found, refresh and try again.
|
||||
# This handles slow backend indexing by periodically pulling fresh data.
|
||||
- repeat:
|
||||
times: 5
|
||||
commands:
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
commands:
|
||||
# 1. Clear current search
|
||||
- tapOn:
|
||||
id: "find_shifts_search_input"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- inputText: "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" # Strategic clear if "" fails
|
||||
- inputText: ""
|
||||
- tapOn: "Shifts" # Dismiss keyboard
|
||||
|
||||
# 2. Pull-to-refresh
|
||||
- swipe:
|
||||
start: 50%, 60%
|
||||
end: 50%, 90%
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 15000
|
||||
|
||||
# 3. Search again
|
||||
- tapOn:
|
||||
id: "find_shifts_search_input"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- inputText: "${TEST_ORDER_NAME}\n"
|
||||
- tapOn: "Shifts" # Dismiss keyboard
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 5000
|
||||
|
||||
# Final check with a long timeout
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
timeout: 30000
|
||||
|
||||
- assertVisible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Client App — Completed tab: edit icon hidden for past/completed orders (#492)
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Orders"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Orders"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Orders"
|
||||
timeout: 15000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Completed.*"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Completed.*"
|
||||
|
||||
- assertVisible: "(?i)Completed.*"
|
||||
@@ -0,0 +1,60 @@
|
||||
# Client App — Orders: Empty state smoke
|
||||
# Purpose:
|
||||
# - Opens the Orders tab
|
||||
# - Verifies filter tabs (Up Next, Active, Completed) are always present
|
||||
# - If no orders exist in a tab, verifies empty-state copy is shown (not a crash)
|
||||
# - Taps each tab and asserts either a list or empty-state is visible
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/orders_empty_state.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- tapOn: "(?i)Orders"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Orders"
|
||||
timeout: 15000
|
||||
|
||||
# Verify filter tabs are always present
|
||||
- assertVisible: "(?i)Up Next.*"
|
||||
- assertVisible: "(?i)Active.*"
|
||||
- assertVisible: "(?i)Completed.*"
|
||||
|
||||
# --- Up Next tab ---
|
||||
- tapOn: "(?i)Up Next.*"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- assertVisible:
|
||||
text: "(?i).*(No orders|No upcoming|Nothing here|order).*"
|
||||
optional: true
|
||||
|
||||
# --- Active tab ---
|
||||
- tapOn: "(?i)Active.*"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- assertVisible:
|
||||
text: "(?i).*(No active|No orders|Nothing here|order).*"
|
||||
optional: true
|
||||
|
||||
# --- Completed tab ---
|
||||
- tapOn: "(?i)Completed.*"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- assertVisible:
|
||||
text: "(?i).*(No completed|No orders|Nothing here|order).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — we are still on the Orders screen
|
||||
- assertVisible: "(?i).*(Up Next|Active|Completed).*"
|
||||
@@ -0,0 +1,118 @@
|
||||
# Client App — E2E: Comprehensive Order Lifecycle (Create -> Verify in List -> View Details)
|
||||
# Purpose:
|
||||
# - Generates a unique order name via JS.
|
||||
# - Creates a One-Time order.
|
||||
# - Verifies the success confirmation.
|
||||
# - Navigates to the Orders list.
|
||||
# - Uses scrollUntilVisible to find the unique order.
|
||||
# - Taps the order and verifies details on the summary page.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
11: # apps/mobile/apps/client/maestro/auth/happy_path/sign_in.yaml \
|
||||
12: # apps/mobile/apps/client/maestro/orders/happy_path/comprehensive_order_lifecycle.yaml \
|
||||
13: # -e TEST_CLIENT_EMAIL=... \
|
||||
14: # -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
# 1. Generate unique order name
|
||||
- runScript: ../../scripts/generate_order_name.js
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# 2. Start Create Order Flow
|
||||
- tapOn: "Create Order\\nSchedule shifts"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time\\nSingle Event or Shift Request"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "One-Time\\nSingle Event or Shift Request"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time Order"
|
||||
timeout: 10000
|
||||
|
||||
# 3. Fill Order Details
|
||||
- tapOn: ".*(ORDER NAME|EVENT NAME|Order[ Nn]ame|Event[ Nn]ame).*"
|
||||
- inputText: ${output.orderName}
|
||||
- hideKeyboard
|
||||
|
||||
# Select Role
|
||||
- tapOn: ".*(Select Role|SELECT ROLE).*"
|
||||
- tapOn: ".*\\$.*" # Just tap the first role from the dropdown
|
||||
|
||||
# Set Start Time (Required for valid form)
|
||||
- scrollUntilVisible:
|
||||
element: "--:--"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
# Set End Time (Required for valid form)
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
# Scroll if needed to see the Create Order button
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Create Order"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
|
||||
- tapOn: "(?i)Create Order"
|
||||
|
||||
# 4. Verify Success Confirmation
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Order Created.*"
|
||||
timeout: 30000
|
||||
|
||||
- assertVisible: "(?i)Order Created.*"
|
||||
- assertVisible: ${output.orderName}
|
||||
|
||||
# 5. Navigate to Orders List and Verify Persistence
|
||||
- tapOn: "Back to Orders"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Orders"
|
||||
timeout: 15000
|
||||
|
||||
# Select the "Up Next" or "Active" tab if not already selected (assuming default is correct)
|
||||
- tapOn: "Up Next"
|
||||
|
||||
# Scroll until we find our unique order name
|
||||
- scrollUntilVisible:
|
||||
element: ${output.orderName}
|
||||
direction: DOWN
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: ${output.orderName}
|
||||
|
||||
# 6. View Details and Final Verification
|
||||
- tapOn: ${output.orderName}
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Order Summary"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: ${output.orderName}
|
||||
- assertVisible: "POSTED" # or whichever status it initialises to
|
||||
- assertVisible: "Role" # Check if role label is visible
|
||||
|
||||
# Optionally, go back to Home
|
||||
- tapOn: "Home"
|
||||
@@ -0,0 +1,92 @@
|
||||
# Client App — E2E: Create One-Time Order and verify success
|
||||
# Flow:
|
||||
# - Home → Create Order → One-Time
|
||||
# - Fill required fields (Event Name, Role)
|
||||
# - Create Order
|
||||
# - Verify success message and return to orders
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/create_order_one_time_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
- tapOn: "Create Order\\nSchedule shifts"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time\\nSingle Event or Shift Request"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "One-Time\\nSingle Event or Shift Request"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time Order"
|
||||
timeout: 10000
|
||||
|
||||
# Wait for form or empty state data to load from API
|
||||
- extendedWaitUntil:
|
||||
visible: ".*(Create your order|No Vendors Available).*"
|
||||
timeout: 15000
|
||||
|
||||
- assertNotVisible: "No Vendors Available"
|
||||
- tapOn: ".*(ORDER NAME|EVENT NAME|Order[ Nn]ame|Event[ Nn]ame).*"
|
||||
- inputText: "Test E2E Event"
|
||||
- hideKeyboard
|
||||
|
||||
# Wait for Vendor and Hub to auto-populate the defaults from the API.
|
||||
# We just need to give it a second.
|
||||
- extendedWaitUntil:
|
||||
visible: ".*(Select Role|SELECT ROLE).*"
|
||||
timeout: 10000
|
||||
|
||||
# Select Role (Required for valid form)
|
||||
- tapOn: ".*(Select Role|SELECT ROLE).*"
|
||||
- tapOn: ".*\\$.*" # Tap the first role from the dropdown
|
||||
|
||||
# Set Start Time (Required for valid form)
|
||||
- scrollUntilVisible:
|
||||
element: "--:--"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
# Set End Time (Required for valid form)
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Create Order"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
# Wait for Create Order button to be enabled (isValid=true handles this by making it clickable)
|
||||
- tapOn: "(?i)Create Order"
|
||||
|
||||
# Success screen shows "Order received." or similar success title/message
|
||||
- extendedWaitUntil:
|
||||
visible: "Test E2E Event" # or success message, assuming it goes back to Orders or shows Success Screen
|
||||
timeout: 45000
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
# Multi-App E2E: Client Creates Order -> Staff Sees Shift
|
||||
# Purpose:
|
||||
# - Historically verifies that an order created in the Client app
|
||||
# is instantly visible as a shift in the Staff app.
|
||||
#
|
||||
# Run:
|
||||
# maestro test cross_app_order_verification.yaml \
|
||||
11: # -e TEST_CLIENT_EMAIL=... \
|
||||
12: # -e TEST_CLIENT_PASSWORD=... \
|
||||
13: # -e TEST_STAFF_PHONE=... \
|
||||
14: # -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
# --- PHASE 1: CLIENT CREATES ORDER ---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- runScript: ../../scripts/generate_order_name.js
|
||||
|
||||
- tapOn:
|
||||
text: "(?i)Home"
|
||||
optional: true
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Welcome back"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "Create Order\nSchedule shifts"
|
||||
- tapOn: "One-Time\nSingle Event or Shift Request"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time Order"
|
||||
timeout: 10000
|
||||
|
||||
# 3. Fill Order Details
|
||||
- tapOn: ".*(ORDER NAME|EVENT NAME|Order[ Nn]ame|Event[ Nn]ame).*"
|
||||
- inputText: ${output.orderName}
|
||||
- hideKeyboard
|
||||
|
||||
# Select Role
|
||||
- tapOn: ".*(Select Role|SELECT ROLE).*"
|
||||
- tapOn: ".*\\$.*"
|
||||
|
||||
# Set Start Time (Required for valid form)
|
||||
- scrollUntilVisible:
|
||||
element: "--:--"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
# Set End Time (Required for valid form)
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Create Order"
|
||||
direction: DOWN
|
||||
|
||||
- tapOn: "(?i)Create Order"
|
||||
|
||||
# 4. Verify Success Confirmation
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Order Created.*"
|
||||
timeout: 30000
|
||||
|
||||
- stopApp: com.krowwithus.client
|
||||
|
||||
# --- PHASE 2: STAFF VERIFIES SHIFT ---
|
||||
- appId: com.krowwithus.staff
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
# If not logged in, we'd need a sign_in flow here, but we assume state is kept
|
||||
# or we run this after a staff sign-in test.
|
||||
# For a standalone test, we should include the login steps.
|
||||
|
||||
# (Optional login steps if app starts at landing)
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Join the Workforce"
|
||||
file: ../../../../staff/maestro/auth/happy_path/sign_in.yaml
|
||||
env:
|
||||
PHONE: ${TEST_STAFF_PHONE}
|
||||
OTP: ${TEST_STAFF_OTP}
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Shifts"
|
||||
timeout: 20000
|
||||
|
||||
- launchApp:
|
||||
appId: com.krowwithus.staff
|
||||
clearState: false
|
||||
|
||||
# Wait for Staff app to fully render AND give backend time to index the new order
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 30000
|
||||
|
||||
# 1. Navigate to Shifts tab
|
||||
- tapOn:
|
||||
id: "nav_shifts"
|
||||
|
||||
# 2. Refresh #1: Swipe to force backend reload, THEN search
|
||||
- swipe:
|
||||
start: 50%, 30%
|
||||
end: 50%, 80%
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 8000
|
||||
|
||||
# Wait for the search field to appear (visible when Find Shifts tab is active)
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Search jobs.*"
|
||||
timeout: 15000
|
||||
|
||||
# 3. Aggressive Retry Loop: Search, if not found, refresh and try again.
|
||||
- repeat:
|
||||
times: 5
|
||||
commands:
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
commands:
|
||||
# 1. Clear current search
|
||||
- tapOn:
|
||||
id: "find_shifts_search_input"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- inputText: "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
|
||||
- inputText: ""
|
||||
- tapOn: "Shifts" # Dismiss keyboard
|
||||
|
||||
# 2. Pull-to-refresh
|
||||
- swipe:
|
||||
start: 50%, 60%
|
||||
end: 50%, 90%
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 15000
|
||||
|
||||
# 3. Enter search query again
|
||||
- tapOn:
|
||||
id: "find_shifts_search_input"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- inputText: "${output.orderName}\n"
|
||||
- tapOn: "Shifts" # Dismiss keyboard
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 5000
|
||||
|
||||
# 4. Final wait — plenty of time for slow backends
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
timeout: 60000
|
||||
|
||||
- assertVisible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
|
||||
- tapOn:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
|
||||
# Wait for Details page with a very generous timeout for slow backend syncing
|
||||
- extendedWaitUntil:
|
||||
visible: "LOCATION"
|
||||
timeout: 45000
|
||||
|
||||
- assertVisible: "${output.orderName}"
|
||||
- assertVisible: "(?i)(Apply|Accept|Clock|Confirm).*"
|
||||
@@ -0,0 +1,71 @@
|
||||
# Client App — E2E: Edit Active Order (One-Time)
|
||||
# Flow:
|
||||
# - Home → Expanded Active Order Card
|
||||
# - Tap Edit icon to open OrderEditSheet
|
||||
# - Change position count to +1
|
||||
# - Continue to Review
|
||||
# - Confirm Save
|
||||
#
|
||||
# Prerequisite:
|
||||
# User must have at least one active, uncompleted order.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/edit_active_order_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- assertVisible: "Home"
|
||||
- tapOn: "Home"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
# Wait for active orders to load
|
||||
- scrollUntilVisible:
|
||||
element: "OPEN" # A badge indicating an open order
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
|
||||
# Tap the edit icon (using an id or the generic icon if no ID is present, we scroll to find the Edit Sheet trigger)
|
||||
# Since we can't select by icon natively, we rely on the card layout having a tapped edit button
|
||||
- tapOn:
|
||||
id: "edit_order_button"
|
||||
# Fallback if no ID is set, tap near the top right of the order
|
||||
point: "85%, 25%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit One-Time Order"
|
||||
timeout: 10000
|
||||
|
||||
# Scroll to the position count control
|
||||
- scrollUntilVisible:
|
||||
element: "WORKERS"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
|
||||
# Increase worker count
|
||||
- tapOn: "+"
|
||||
|
||||
# Proceed to review
|
||||
- tapOn: "Review Positions"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Positions Breakdown"
|
||||
timeout: 10000
|
||||
|
||||
# Ensure the count reflects the change before confirming
|
||||
- tapOn: "Confirm & Save"
|
||||
|
||||
# Verify it saved and the modal closed
|
||||
- extendedWaitUntil:
|
||||
notVisible: "Confirm & Save"
|
||||
timeout: 15000
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Client App — E2E: Edit Active Order and verify update confirmation
|
||||
# Purpose:
|
||||
# - Opens an active order edit sheet
|
||||
# - Adjusts workers count (+1)
|
||||
# - Confirms save
|
||||
# - Verifies the "Order Updated!" confirmation UI appears
|
||||
#
|
||||
# Prerequisite:
|
||||
# User must have at least one active, uncompleted order.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/edit_active_order_verify_updated_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- assertVisible: "Home"
|
||||
- tapOn: "Home"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
# Wait for active orders to load and ensure we have an OPEN order
|
||||
- scrollUntilVisible:
|
||||
element: "OPEN"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
|
||||
# Tap edit on the active order card (prefer ID; fallback to a safe point)
|
||||
- tapOn:
|
||||
id: "edit_order_button"
|
||||
point: "85%,25%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit One-Time Order"
|
||||
timeout: 15000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "WORKERS"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
|
||||
- tapOn: "+"
|
||||
|
||||
- tapOn: "Review Positions"
|
||||
- extendedWaitUntil:
|
||||
visible: "Positions Breakdown"
|
||||
timeout: 15000
|
||||
|
||||
- tapOn: "Confirm & Save"
|
||||
|
||||
# Verify the in-app confirmation appears
|
||||
- extendedWaitUntil:
|
||||
visible: "Order Updated!"
|
||||
timeout: 20000
|
||||
- assertVisible: "Your shift has been updated successfully."
|
||||
|
||||
- tapOn: "Back to Orders"
|
||||
- extendedWaitUntil:
|
||||
visible: "Orders"
|
||||
timeout: 15000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
# Multi-App E2E: Full Order-to-Payment Lifecycle
|
||||
# Roles: Client, Staff
|
||||
# Flow: Create -> Apply -> Clock In (Geofence) -> Clock Out -> Approve Payment
|
||||
|
||||
appId: com.krowwithus.client # Starts with Client profile
|
||||
---
|
||||
# === PERSONA: CLIENT ===
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
# 1. Generate unique identifier for this session
|
||||
- runScript: ../../scripts/generate_order_name.js
|
||||
|
||||
# 2. Create the Order
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 10000
|
||||
- tapOn:
|
||||
text: "(?i)Home"
|
||||
optional: true
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Welcome back"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "Create Order\\nSchedule shifts"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time\\nSingle Event or Shift Request"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "One-Time\\nSingle Event or Shift Request"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time Order"
|
||||
timeout: 15000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: ".*(Create your order|No Vendors Available).*"
|
||||
timeout: 15000
|
||||
|
||||
# Safety check: ensure the account has vendors setup!
|
||||
- assertNotVisible: "No Vendors Available"
|
||||
|
||||
- tapOn: ".*(ORDER NAME|EVENT NAME|Order[ Nn]ame|Event[ Nn]ame).*"
|
||||
- inputText: "${output.orderName}"
|
||||
- hideKeyboard
|
||||
|
||||
# Select a Hub (assuming first one is auto-selected or we tap)
|
||||
- tapOn:
|
||||
text: ".*(HUB|Hub).*"
|
||||
optional: true
|
||||
|
||||
# Wait for Vendor and Hub to auto-populate the defaults from the API.
|
||||
- extendedWaitUntil:
|
||||
visible: ".*(Select Role|SELECT ROLE).*"
|
||||
timeout: 10000
|
||||
|
||||
# Select Role (Required for valid form)
|
||||
- tapOn: ".*(Select Role|SELECT ROLE).*"
|
||||
- tapOn: ".*\\$.*" # Tap the first role from the dropdown (matches 'Role - $Cost')
|
||||
|
||||
# Set Start Time (Required for valid form)
|
||||
- scrollUntilVisible:
|
||||
element: "--:--"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
# Set End Time (Required for valid form)
|
||||
- tapOn: "--:--"
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ok"
|
||||
timeout: 5000
|
||||
- tapOn: "(?i)ok"
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Create Order"
|
||||
direction: DOWN
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn: "(?i)Create Order"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Order Created.*"
|
||||
timeout: 30000
|
||||
|
||||
# Cool-down to let backend index the new order
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 35000
|
||||
|
||||
- stopApp
|
||||
|
||||
# === PERSONA: STAFF ===
|
||||
- launchApp:
|
||||
appId: com.krowwithus.staff
|
||||
clearState: false
|
||||
|
||||
# Wait for Staff app to fully render (also serves as additional backend indexing time)
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 15000
|
||||
|
||||
# Sign in if needed
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Log In"
|
||||
file: ../../../../staff/maestro/auth/happy_path/sign_in.yaml
|
||||
env:
|
||||
TEST_STAFF_PHONE: ${TEST_STAFF_PHONE}
|
||||
TEST_STAFF_OTP: ${TEST_STAFF_OTP}
|
||||
|
||||
# 1. Navigate to Shifts tab
|
||||
- tapOn:
|
||||
id: "nav_shifts"
|
||||
|
||||
# 2. Refresh #1: Swipe to force backend reload, THEN search
|
||||
- swipe:
|
||||
start: 50%, 30%
|
||||
end: 50%, 80%
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 8000
|
||||
|
||||
# Wait for the search field to appear (visible when Find Shifts tab is active)
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Search jobs.*"
|
||||
timeout: 15000
|
||||
|
||||
# 3. Aggressive Retry Loop: Search, if not found, refresh and try again.
|
||||
- repeat:
|
||||
times: 5
|
||||
commands:
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0 # Index 0 for Logo (ID is unique to cards)
|
||||
commands:
|
||||
# 1. Clear current search
|
||||
- tapOn:
|
||||
id: "find_shifts_search_input"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- inputText: "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
|
||||
- inputText: ""
|
||||
- tapOn: "Shifts" # Dismiss keyboard
|
||||
|
||||
# 2. Pull-to-refresh
|
||||
- swipe:
|
||||
start: 50%, 60%
|
||||
end: 50%, 90%
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 15000
|
||||
|
||||
# 3. Enter search query again
|
||||
- tapOn:
|
||||
id: "find_shifts_search_input"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- inputText: "${output.orderName}\n"
|
||||
- tapOn: "Shifts" # Dismiss keyboard
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 5000
|
||||
|
||||
# 4. Final wait — plenty of time for slow backends
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
timeout: 60000
|
||||
|
||||
- tapOn:
|
||||
id: "shft_card_logo_placeholder"
|
||||
index: 0
|
||||
|
||||
# Wait for Details page with a very generous timeout for slow backend syncing
|
||||
- extendedWaitUntil:
|
||||
visible: "LOCATION"
|
||||
timeout: 45000
|
||||
|
||||
# 2. Book the shift (Instant Book flow)
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)(Apply|Accept|Clock|Confirm).*"
|
||||
timeout: 20000
|
||||
|
||||
- tapOn: "(?i)(Apply|Accept|Clock|Confirm).*"
|
||||
|
||||
# Handle confirmation dialog if it appears
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "(?i)Book Shift"
|
||||
file: ../../../../staff/maestro/shifts/confirm_booking_dialog.yaml
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Shift successfully booked!"
|
||||
timeout: 20000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Welcome back"
|
||||
timeout: 20000
|
||||
|
||||
# Tap Home tab to reset navigation context
|
||||
- tapOn:
|
||||
id: "nav_home"
|
||||
|
||||
# Tap Clock In tab
|
||||
- tapOn:
|
||||
id: "nav_clock_in"
|
||||
|
||||
# Wait for Clock In screen to load
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Clock In to your Shift"
|
||||
timeout: 15000
|
||||
|
||||
# Set location to the Venue (NYC Grand Hotel as per ClockInCubit)
|
||||
- setLocation:
|
||||
latitude: 40.7128
|
||||
longitude: -74.0060
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Swipe to Check In"
|
||||
timeout: 15000
|
||||
|
||||
- swipe:
|
||||
direction: RIGHT
|
||||
element: "Swipe to Check In"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Check In!"
|
||||
timeout: 15000
|
||||
|
||||
# 4. Clock Out (immediately for test purposes)
|
||||
- swipe:
|
||||
direction: RIGHT
|
||||
element: "Swipe to Check Out"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Check Out!" # Assuming similar success UI
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
- stopApp
|
||||
|
||||
# === PERSONA: CLIENT ===
|
||||
- launchApp:
|
||||
appId: com.krowwithus.client
|
||||
clearState: false
|
||||
|
||||
# 1. Verify and Approve Billing
|
||||
- tapOn: "Billing"
|
||||
- tapOn: "Awaiting Approval"
|
||||
|
||||
# Find the specific approved shift's related invoice
|
||||
# For the test, we assume the top one is our most recent completion
|
||||
- extendedWaitUntil:
|
||||
visible: "Review & Approve"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Review & Approve"
|
||||
|
||||
- tapOn: "Approve"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Invoice approved"
|
||||
timeout: 15000
|
||||
|
||||
# Test complete - invoice approved successfully
|
||||
- assertVisible: "Invoice approved"
|
||||
@@ -0,0 +1,50 @@
|
||||
# Client App — E2E: Rapid order → parsed One-Time draft
|
||||
# This is a true end-to-end in-app flow:
|
||||
# - Navigate to Create Order → Rapid
|
||||
# - Enter a message
|
||||
# - Submit ("Send Message") which triggers parse usecase
|
||||
# - Verify navigation into One-Time order draft screen
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/rapid_to_one_time_draft_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Order\nSchedule shifts"
|
||||
timeout: 20000
|
||||
- tapOn: "Create Order\nSchedule shifts"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "RAPID\nURGENT same-day Coverage"
|
||||
timeout: 10000
|
||||
- tapOn: "RAPID\nURGENT same-day Coverage"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "RAPID Order"
|
||||
timeout: 10000
|
||||
|
||||
# Use one of the predefined example messages so the BLoC
|
||||
# has a realistic input without manual typing.
|
||||
- tapOn: ".*Need 2 cooks ASAP.*"
|
||||
|
||||
# For now we only require that the Send Message action
|
||||
# remains visible and tappable on the Rapid Order screen.
|
||||
- assertVisible: "Send Message"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Client App — E2E: Rapid order → parsed One-Time draft (submit)
|
||||
# Purpose:
|
||||
# - Validates the full RAPID happy path including submitting a message and
|
||||
# landing on the One-Time order draft screen.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/rapid_to_one_time_draft_submit_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Order\nSchedule shifts"
|
||||
timeout: 20000
|
||||
- tapOn: "Create Order\nSchedule shifts"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "RAPID\nURGENT same-day Coverage"
|
||||
timeout: 10000
|
||||
- tapOn: "RAPID\nURGENT same-day Coverage"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "RAPID Order"
|
||||
timeout: 10000
|
||||
|
||||
# Use a predefined example message so the Rapid flow has valid input.
|
||||
- tapOn: ".*Need 2 cooks ASAP.*"
|
||||
|
||||
- assertVisible: "Send Message"
|
||||
- tapOn: "Send Message"
|
||||
|
||||
# Parsed result should navigate into a One-Time order draft screen.
|
||||
# This depends on backend parsing; allow extra time for slower networks.
|
||||
- extendedWaitUntil:
|
||||
visible: "ORDER NAME"
|
||||
timeout: 60000
|
||||
- assertVisible: "Select Role"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Client App — E2E: Reorder Flow
|
||||
# Purpose:
|
||||
# - Navigates Home → Reorder Section.
|
||||
# - Taps "Reorder" on a recent shift.
|
||||
# - Verifies the One-Time Order form is correctly populated.
|
||||
# - Submits the reordered shift.
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- tapOn: "Home"
|
||||
|
||||
# 1. Generate a new name for the reorder to avoid confusion
|
||||
- runScript: ../../scripts/generate_order_name.js
|
||||
|
||||
# 2. Find the "Reorder" section (translates to "Recently Completed" or similar)
|
||||
# We search for the Reorder button text
|
||||
- scrollUntilVisible:
|
||||
element: "REORDER"
|
||||
direction: DOWN
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "REORDER"
|
||||
|
||||
# 3. Verification: We should be on the One-Time Order creation page
|
||||
- extendedWaitUntil:
|
||||
visible: "One-Time Order"
|
||||
timeout: 10000
|
||||
|
||||
# 4. Verification: Some fields should be pre-filled (e.g. Hub)
|
||||
# We can't easily check exact pre-fill values without knowing the history,
|
||||
# but we can verify we are in the flow and the form is loaded.
|
||||
- extendedWaitUntil:
|
||||
visible: ".*(Create your order|No Vendors Available).*"
|
||||
timeout: 15000
|
||||
|
||||
- assertNotVisible: "No Vendors Available"
|
||||
- assertVisible: ".*(ORDER NAME|EVENT NAME|Order[ Nn]ame|Event[ Nn]ame).*"
|
||||
|
||||
# 5. Overwrite name and Submit
|
||||
- tapOn: ".*(ORDER NAME|EVENT NAME|Order[ Nn]ame|Event[ Nn]ame).*"
|
||||
- inputText: "${output.orderName}"
|
||||
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn: "(?i)Create Order"
|
||||
|
||||
# 6. Success check
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Order Created.*"
|
||||
timeout: 15000
|
||||
@@ -0,0 +1,26 @@
|
||||
# Client App — View Orders page with filter tabs
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Orders"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Orders"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Orders"
|
||||
timeout: 15000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Up Next.*"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "(?i)Up Next.*"
|
||||
- assertVisible: "(?i)Active.*"
|
||||
- assertVisible: "(?i)Completed.*"
|
||||
@@ -0,0 +1,63 @@
|
||||
# Client App — Orders: Validation errors (negative path)
|
||||
# Purpose:
|
||||
# - Opens Create Order → One-Time form
|
||||
# - Attempts to submit WITHOUT filling required fields
|
||||
# - Verifies inline validation error messages appear
|
||||
# - Verifies the form does NOT navigate away (stays on create order screen)
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/create_order_validation_errors.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
# Navigate to Create Order
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Create Order.*Schedule.*"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Create Order.*Schedule.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ORDER TYPE"
|
||||
timeout: 10000
|
||||
|
||||
# Select One-Time order type
|
||||
- tapOn: "(?i)One-Time.*Single Event.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)One-Time Order"
|
||||
timeout: 10000
|
||||
|
||||
# Do NOT fill any fields — attempt to submit immediately
|
||||
- tapOn: "(?i)Create Order"
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# Validation: form should NOT advance — still on One-Time Order screen
|
||||
- assertVisible: "(?i)One-Time Order"
|
||||
|
||||
# Validation: at least one inline error or disabled-state indicator should appear
|
||||
- assertVisible:
|
||||
text: "(?i).*(required|must|invalid|please fill|error).*"
|
||||
optional: true
|
||||
|
||||
# Verify ORDER NAME field is still visible (form did not navigate away)
|
||||
- assertVisible: "(?i)ORDER NAME"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Client App — Create order flow entry
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Create Order.*Schedule.*"
|
||||
timeout: 20000
|
||||
|
||||
- tapOn: "(?i)Create Order.*Schedule.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ORDER TYPE"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "(?i)RAPID.*URGENT.*"
|
||||
@@ -0,0 +1,38 @@
|
||||
# Client App — Create One-Time Order flow
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Create Order.*Schedule.*"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Create Order.*Schedule.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)One-Time.*Single Event.*"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)One-Time.*Single Event.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)One-Time Order"
|
||||
timeout: 15000
|
||||
|
||||
- assertVisible: "(?i)Create Your Order"
|
||||
- assertVisible: "(?i).*(SELECT VENDOR|Date|HUB|Positions).*"
|
||||
- assertVisible: "(?i)Create Order"
|
||||
@@ -0,0 +1,39 @@
|
||||
# Client App — Create Permanent Order (placeholder/WIP screen)
|
||||
# Validates that Permanent entry is present and navigates to the expected WIP placeholder.
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/create_order_permanent_placeholder.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
# Open Create Order from Home quick action (reliable entry point)
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Order\nSchedule shifts"
|
||||
timeout: 20000
|
||||
- tapOn: "Create Order\nSchedule shifts"
|
||||
|
||||
# Select Permanent order type
|
||||
- extendedWaitUntil:
|
||||
visible: "Permanent\nLong-Term Staffing Placement"
|
||||
timeout: 10000
|
||||
- tapOn: "Permanent\nLong-Term Staffing Placement"
|
||||
|
||||
# Validate Permanent Order screen header (WIP flow)
|
||||
- extendedWaitUntil:
|
||||
visible: "Permanent Order"
|
||||
timeout: 10000
|
||||
- assertVisible: "Permanent Order"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Client App — Create Rapid Order flow (UI smoke)
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Create Order.*Schedule.*"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Create Order.*Schedule.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)ORDER TYPE"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "(?i)RAPID.*URGENT.*"
|
||||
|
||||
- tapOn: "(?i)RAPID.*URGENT.*"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)RAPID Order"
|
||||
timeout: 15000
|
||||
|
||||
- assertVisible: "(?i)Emergency staffing in minutes"
|
||||
- assertVisible: "(?i).*(Send Message|Speak).*"
|
||||
@@ -0,0 +1,39 @@
|
||||
# Client App — Create Recurring Order (placeholder/WIP screen)
|
||||
# Validates that Recurring entry is present and navigates to the expected WIP placeholder.
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/create_order_recurring_placeholder.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
# Open Create Order from Home quick action (reliable entry point)
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Order\nSchedule shifts"
|
||||
timeout: 20000
|
||||
- tapOn: "Create Order\nSchedule shifts"
|
||||
|
||||
# Select Recurring order type
|
||||
- extendedWaitUntil:
|
||||
visible: "Recurring\nOngoing Weekly / Monthly Coverage"
|
||||
timeout: 10000
|
||||
- tapOn: "Recurring\nOngoing Weekly / Monthly Coverage"
|
||||
|
||||
# Validate Recurring Order screen header (WIP flow)
|
||||
- extendedWaitUntil:
|
||||
visible: "Recurring Order"
|
||||
timeout: 10000
|
||||
- assertVisible: "Recurring Order"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Client App — Order Type selection screen (comprehensive smoke)
|
||||
# Asserts all order types are present with correct accessible labels.
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/orders/order_type_selection_smoke.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Order\nSchedule shifts"
|
||||
timeout: 20000
|
||||
- tapOn: "Create Order\nSchedule shifts"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "ORDER TYPE"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "RAPID\nURGENT same-day Coverage"
|
||||
- assertVisible: "One-Time\nSingle Event or Shift Request"
|
||||
- assertVisible: "Recurring\nOngoing Weekly / Monthly Coverage"
|
||||
- assertVisible: "Permanent\nLong-Term Staffing Placement"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Client App — E2E: Reports Insights Verification
|
||||
# Purpose:
|
||||
# - Navigates to Reports.
|
||||
# - Opens Spend Report.
|
||||
# - Verifies that charts and summary cards are loaded.
|
||||
# - Opens No-Show Report.
|
||||
# - Verifies the empty/data states are handled.
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- tapOn: "Home"
|
||||
- tapOn: "Reports"
|
||||
|
||||
# 1. Verify Spend Report
|
||||
- extendedWaitUntil:
|
||||
visible: "Spend Report"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Spend Report"
|
||||
|
||||
# Verify specialized summary cards are visible
|
||||
- extendedWaitUntil:
|
||||
visible: "TOTAL SPEND"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "AVG DAILY COST"
|
||||
|
||||
# Swipe to see historical chart content if needed
|
||||
- swipe:
|
||||
direction: DOWN
|
||||
element: "TOTAL SPEND"
|
||||
|
||||
- assertVisible: "SPEND BY INDUSTRY"
|
||||
|
||||
# Go back
|
||||
- tapOn:
|
||||
id: "back_button" # Or the arrow icon
|
||||
optional: true
|
||||
- tapOn:
|
||||
point: "8% 8%" # Fallback to top-left if ID missing
|
||||
optional: true
|
||||
|
||||
# 2. Verify No-Show Report
|
||||
- extendedWaitUntil:
|
||||
visible: "No-Show Report"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "No-Show Report"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(No-Show|Report).*"
|
||||
timeout: 10000
|
||||
|
||||
# Smoke check for data availability
|
||||
- assertVisible:
|
||||
text: "No show incidents"
|
||||
optional: true
|
||||
|
||||
- tapOn: "Home"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Client App — Reports dashboard
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Reports"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Reports"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Workforce Control Tower"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Workforce Control Tower"
|
||||
@@ -0,0 +1,53 @@
|
||||
# Client App — Reports: No-Show Report smoke
|
||||
# Purpose:
|
||||
# - Opens Reports dashboard (Workforce Control Tower)
|
||||
# - Navigates to the No-Show Reports section
|
||||
# - Verifies the no-show report screen or relevant UI elements are visible
|
||||
# - Also verifies empty-state copy if no no-show records exist
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/reports/no_show_report_smoke.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- tapOn: "(?i)Reports"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Workforce Control Tower"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Workforce Control Tower"
|
||||
|
||||
# Scroll to find No-Show report entry
|
||||
- scrollUntilVisible:
|
||||
element: "(?i).*(No.Show|No Show|Absence).*"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
optional: true
|
||||
|
||||
# Tap No-Show report (if visible)
|
||||
- tapOn:
|
||||
text: "(?i).*(No.Show|No Show|Absence).*"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# Either the report detail loads or we see a placeholder/empty state
|
||||
- assertVisible:
|
||||
text: "(?i).*(No.Show|No Show|Absence|No records|Nothing|Report).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — still in Reports context (no crash)
|
||||
- assertVisible: "(?i).*(Workforce Control Tower|Reports|No.Show).*"
|
||||
@@ -0,0 +1,46 @@
|
||||
# Client App — Reports: Spend Report export (smoke)
|
||||
# Purpose:
|
||||
# - Opens Reports dashboard
|
||||
# - Opens Spend Report
|
||||
# - Triggers Export action and verifies placeholder export message
|
||||
#
|
||||
# Note: Export is currently placeholder-driven in UI strings.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/reports/spend_report_export_smoke.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Reports"
|
||||
- extendedWaitUntil:
|
||||
visible: "Workforce Control Tower"
|
||||
timeout: 10000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Quick Reports"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
|
||||
- assertVisible: "Quick Reports"
|
||||
- tapOn: "Spend Report"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Spend Report"
|
||||
timeout: 10000
|
||||
|
||||
# Trigger export (may be a placeholder message)
|
||||
- tapOn: "Export"
|
||||
- extendedWaitUntil:
|
||||
visible: "Exporting Spend Report \\(Placeholder\\)"
|
||||
timeout: 10000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
output.orderName = "E2E-" + Math.random().toString(36).substring(2, 7).toUpperCase();
|
||||
@@ -0,0 +1,29 @@
|
||||
# Client App — Edit Profile (navigates via Settings)
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 15000
|
||||
|
||||
- tapOn:
|
||||
id: "client_home_settings"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Profile.*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Profile"
|
||||
- assertVisible: "(?i)Quick Links"
|
||||
- assertVisible: "(?i).*(Log Out|Logout).*"
|
||||
@@ -0,0 +1,80 @@
|
||||
# Client App — E2E: Edit Profile save (with re-open verification)
|
||||
# Purpose:
|
||||
# - Navigates Settings → Profile → Edit Profile
|
||||
# - Saves a small change and verifies success message
|
||||
# - Re-opens Edit Profile to validate the change is visible (basic persistence check)
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/settings/edit_profile_save_e2e.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
- extendedWaitUntil:
|
||||
visible: "Profile"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Profile"
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit Profile"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Edit Profile"
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit Profile"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "FIRST NAME"
|
||||
- assertVisible: "LAST NAME"
|
||||
- assertVisible: "PHONE NUMBER"
|
||||
- assertVisible: "Save Changes"
|
||||
|
||||
# Append a suffix to first name (avoids needing to clear the field)
|
||||
- tapOn: "FIRST NAME"
|
||||
- inputText: " QA"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn: "Save Changes"
|
||||
- extendedWaitUntil:
|
||||
visible: "Profile updated successfully"
|
||||
timeout: 15000
|
||||
|
||||
# Re-open Edit Profile and confirm the suffix is visible somewhere in the form.
|
||||
- launchApp
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
- extendedWaitUntil:
|
||||
visible: "Profile"
|
||||
timeout: 10000
|
||||
- tapOn: "Profile"
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit Profile"
|
||||
timeout: 10000
|
||||
- tapOn: "Edit Profile"
|
||||
- extendedWaitUntil:
|
||||
visible: "Edit Profile"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "QA"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Client App — Settings logout flow
|
||||
# Navigates to Settings via gear icon and logs out.
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/settings/logout_flow.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Quick Links"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "Log Out"
|
||||
- tapOn: "Log Out"
|
||||
|
||||
# Confirm dialog (button label is localized as "Log Out")
|
||||
- extendedWaitUntil:
|
||||
visible: "Cancel"
|
||||
timeout: 10000
|
||||
- tapOn: "Log Out"
|
||||
|
||||
# Post-logout should return to auth entry (e.g. Create Account screen).
|
||||
- extendedWaitUntil:
|
||||
visible: "Create Account"
|
||||
timeout: 20000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Client App — Settings page
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Home"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 15000
|
||||
|
||||
# Open Settings via header gear icon using stable ID
|
||||
- tapOn:
|
||||
id: "client_home_settings"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Quick Links.*"
|
||||
timeout: 20000
|
||||
|
||||
- assertVisible: "(?i)Profile"
|
||||
- assertVisible: "(?i)Clock-In Hubs"
|
||||
- assertVisible: "(?i)Billing & Payments"
|
||||
- assertVisible: "(?i).*(Log Out|Logout).*"
|
||||
@@ -0,0 +1,80 @@
|
||||
# Client App — Settings: Edit Profile validation errors (negative path)
|
||||
# Purpose:
|
||||
# - Navigates to Settings → Edit Profile
|
||||
# - Clears a required field (e.g. First Name) and attempts to save
|
||||
# - Verifies that an inline validation error is shown
|
||||
# - Verifies the form does NOT navigate away (no false success)
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/client/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/client/maestro/settings/edit_profile_validation.yaml \
|
||||
# -e TEST_CLIENT_EMAIL=... \
|
||||
# -e TEST_CLIENT_PASSWORD=...
|
||||
|
||||
appId: com.krowwithus.client
|
||||
---
|
||||
- launchApp:
|
||||
clearState: false
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Orders|Coverage|Billing|Reports).*"
|
||||
timeout: 45000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 20000
|
||||
|
||||
# Open Settings via header gear icon (top-right)
|
||||
- tapOn:
|
||||
point: "92%,10%"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Profile"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Profile"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Edit Profile"
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Edit Profile"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Edit Profile"
|
||||
timeout: 10000
|
||||
|
||||
# Verify required fields are present
|
||||
- assertVisible: "(?i)FIRST NAME"
|
||||
- assertVisible: "(?i)LAST NAME"
|
||||
- assertVisible: "(?i)Save Changes"
|
||||
|
||||
# Clear the FIRST NAME field by selecting all and deleting
|
||||
- tapOn: "(?i)FIRST NAME"
|
||||
- repeat:
|
||||
times: 30
|
||||
commands:
|
||||
- pressKey: Backspace
|
||||
- hideKeyboard
|
||||
|
||||
# Attempt to save with empty first name
|
||||
- tapOn: "(?i)Save Changes"
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# Negative assertion: success message must NOT appear
|
||||
- assertNotVisible:
|
||||
text: "(?i)Profile updated successfully"
|
||||
optional: true
|
||||
|
||||
# Positive assertion: validation error appears OR form stays put
|
||||
- assertVisible: "(?i)Edit Profile"
|
||||
|
||||
# Inline error should be visible for the empty required field
|
||||
- assertVisible:
|
||||
text: "(?i).*(required|cannot be empty|must not be|invalid|enter your).*"
|
||||
optional: true
|
||||
@@ -105,7 +105,8 @@ android {
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
// Use default debug signing for local dev (no keystore required)
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
release {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
|
||||
@@ -75,6 +75,11 @@ public final class GeneratedPluginRegistrant {
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error registering plugin shared_preferences_android, io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin", e);
|
||||
}
|
||||
try {
|
||||
flutterEngine.getPlugins().add(new fman.ge.smart_auth.SmartAuthPlugin());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error registering plugin smart_auth, fman.ge.smart_auth.SmartAuthPlugin", e);
|
||||
}
|
||||
try {
|
||||
flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin());
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -66,6 +66,12 @@
|
||||
@import shared_preferences_foundation;
|
||||
#endif
|
||||
|
||||
#if __has_include(<smart_auth/SmartAuthPlugin.h>)
|
||||
#import <smart_auth/SmartAuthPlugin.h>
|
||||
#else
|
||||
@import smart_auth;
|
||||
#endif
|
||||
|
||||
#if __has_include(<url_launcher_ios/URLLauncherPlugin.h>)
|
||||
#import <url_launcher_ios/URLLauncherPlugin.h>
|
||||
#else
|
||||
@@ -91,6 +97,7 @@
|
||||
[FPPPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPPackageInfoPlusPlugin"]];
|
||||
[RecordIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordIosPlugin"]];
|
||||
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
||||
[SmartAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"SmartAuthPlugin"]];
|
||||
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
||||
[WorkmanagerPlugin registerWithRegistrar:[registry registrarForPlugin:@"WorkmanagerPlugin"]];
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ void main() async {
|
||||
await const BackgroundTaskService().initialize(backgroundGeofenceDispatcher);
|
||||
|
||||
// Register global BLoC observer for centralized error logging
|
||||
Bloc.observer = CoreBlocObserver(
|
||||
Bloc.observer = const CoreBlocObserver(
|
||||
logEvents: true,
|
||||
logStateChanges: false, // Set to true for verbose debugging
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <record_linux/record_linux_plugin.h>
|
||||
#include <smart_auth/smart_auth_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
@@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
||||
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) smart_auth_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "SmartAuthPlugin");
|
||||
smart_auth_plugin_register_with_registrar(smart_auth_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
record_linux
|
||||
smart_auth
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import geolocator_apple
|
||||
import package_info_plus
|
||||
import record_macos
|
||||
import shared_preferences_foundation
|
||||
import smart_auth
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
@@ -26,5 +27,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SmartAuthPlugin.register(with: registry.registrar(forPlugin: "SmartAuthPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maestro Integration Tests — Staff App
|
||||
|
||||
Auth flows for the KROW Staff app.
|
||||
See [docs/research/flutter-testing-tools.md](/docs/research/flutter-testing-tools.md) and [maestro-test-run-instructions.md](/docs/research/maestro-test-run-instructions.md).
|
||||
Auth flows and E2E happy paths for the KROW Staff app.
|
||||
See [docs/research/flutter-testing-tools.md](/docs/research/flutter-testing-tools.md), [maestro-test-run-instructions.md](/docs/research/maestro-test-run-instructions.md), and [docs/testing/maestro-e2e-happy-paths.md](/docs/testing/maestro-e2e-happy-paths.md) (#572).
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -10,6 +10,49 @@ maestro/
|
||||
auth/
|
||||
sign_in.yaml
|
||||
sign_up.yaml
|
||||
sign_out.yaml
|
||||
sign_in_invalid_otp.yaml
|
||||
navigation/
|
||||
home.yaml
|
||||
shifts.yaml
|
||||
profile.yaml
|
||||
payments.yaml
|
||||
clock_in.yaml
|
||||
availability.yaml
|
||||
profile/
|
||||
personal_info.yaml
|
||||
documents_list.yaml
|
||||
certificates_list.yaml
|
||||
time_card.yaml
|
||||
time_card_detail_smoke.yaml
|
||||
bank_account.yaml
|
||||
bank_account_fields_smoke.yaml
|
||||
tax_forms.yaml
|
||||
tax_forms_smoke.yaml
|
||||
faqs.yaml
|
||||
privacy_security.yaml
|
||||
emergency_contact.yaml
|
||||
attire.yaml
|
||||
attire_validation_e2e.yaml
|
||||
compliance/
|
||||
document_upload_banner.yaml # #550
|
||||
certificate_upload_banner.yaml # #551
|
||||
attire_upload_banner.yaml # #552
|
||||
document_upload_e2e.yaml
|
||||
certificate_upload_e2e.yaml
|
||||
shifts/
|
||||
find_shifts.yaml
|
||||
find_shifts_apply_smoke.yaml
|
||||
clock_in_e2e.yaml
|
||||
clock_out_e2e.yaml
|
||||
incomplete_profile_banner.yaml # #549 (requires incomplete-profile user)
|
||||
availability/
|
||||
set_availability_e2e.yaml
|
||||
payments/
|
||||
payments_view_e2e.yaml
|
||||
payment_history_smoke.yaml
|
||||
home/
|
||||
benefits.yaml # #524
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
@@ -24,11 +67,20 @@ maestro/
|
||||
| sign_in | `TEST_STAFF_PHONE`, `TEST_STAFF_OTP` |
|
||||
| sign_up | `TEST_STAFF_SIGNUP_PHONE`, `TEST_STAFF_OTP` |
|
||||
|
||||
**Sign-in:** +1 555-555-1234 (env: 5555551234) / 123123
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
# Via Makefile (export vars first)
|
||||
make test-e2e-staff
|
||||
make test-e2e-staff # Auth only
|
||||
make test-e2e-staff-extended # Auth + nav + profile + compliance + shifts + benefits
|
||||
make test-e2e-staff-happy-path # Auth + clock in/out + availability + document upload + payments + sign out (#572)
|
||||
make test-e2e-staff-smoke # Deterministic smoke suite
|
||||
make test-e2e-staff-profile-smoke # Profile smoke (timecard/bank/tax/attire validation)
|
||||
make test-e2e-staff-payments-smoke # Payments smoke (earnings history)
|
||||
make test-e2e-staff-shifts-smoke # Shifts smoke (find shifts; optionally apply)
|
||||
make test-e2e-staff-compliance-e2e # Compliance E2E (document + certificate uploads)
|
||||
|
||||
# Direct
|
||||
maestro test apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Staff App — E2E: Session Persistence Across Relaunch
|
||||
# Purpose:
|
||||
# - Log in via sign_in.yaml
|
||||
# - Stop the app
|
||||
# - Relaunch and verify user is still logged in (bypass login screen)
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/auth/session_persistence.yaml \
|
||||
# -e TEST_STAFF_PHONE=... \
|
||||
# -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
# We rely on sign_in.yaml being run before this to establish a session.
|
||||
- launchApp
|
||||
|
||||
# If we are logged in, Home/Shifts content should be visible directly.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 15000
|
||||
|
||||
# Perform a full stop to clear memory (not just backgrounding)
|
||||
- stopApp
|
||||
|
||||
# Relaunch - should NOT show the login screen
|
||||
- launchApp
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 15000
|
||||
|
||||
# Verification: Sign out to ensure clean state for next test
|
||||
- tapOn: "(?i)Profile"
|
||||
- scrollUntilVisible:
|
||||
element: "(?i).*(Log Out|Sign Out).*"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- tapOn:
|
||||
text: "(?i).*(Log Out|Sign Out).*"
|
||||
|
||||
# Confirm Sign Out
|
||||
- tapOn:
|
||||
text: "(?i).*(Yes|Confirm).*(Log Out|Sign Out).*"
|
||||
optional: true
|
||||
|
||||
# Should return to the login landing page
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Log In"
|
||||
timeout: 10000
|
||||
- assertVisible: "(?i)Log In"
|
||||
@@ -15,8 +15,9 @@ env:
|
||||
- tapOn:
|
||||
id: staff_phone_input
|
||||
- inputText: ${PHONE}
|
||||
- hideKeyboard
|
||||
- tapOn: "Send Code"
|
||||
# OTP screen: Continue button visible until we finish typing
|
||||
# OTP screen: Continue visible when ready for OTP entry
|
||||
- assertVisible: "Continue"
|
||||
- tapOn:
|
||||
id: staff_otp_input
|
||||
15
apps/mobile/apps/staff/maestro/auth/happy_path/sign_out.yaml
Normal file
15
apps/mobile/apps/staff/maestro/auth/happy_path/sign_out.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
# Staff App — Sign out flow
|
||||
# Run: maestro test auth/sign_in.yaml auth/sign_out.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
- scrollUntilVisible:
|
||||
element: "Sign Out"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- tapOn: "Sign Out"
|
||||
- assertVisible: "Log In"
|
||||
@@ -15,8 +15,9 @@ env:
|
||||
- tapOn:
|
||||
id: staff_phone_input
|
||||
- inputText: ${PHONE}
|
||||
- hideKeyboard
|
||||
- tapOn: "Send Code"
|
||||
# OTP auto-submits when 6th digit entered
|
||||
# OTP screen: Continue visible when ready for OTP entry
|
||||
- assertVisible: "Continue"
|
||||
- tapOn:
|
||||
id: staff_otp_input
|
||||
@@ -0,0 +1,25 @@
|
||||
# Staff App — Sign in with wrong OTP (negative test)
|
||||
# Uses valid test phone, invalid OTP; expects error and stays on OTP screen
|
||||
# Run: maestro test .../auth/sign_in_invalid_otp.yaml -e TEST_STAFF_PHONE=5555551234 -e TEST_STAFF_INVALID_OTP=000000
|
||||
appId: com.krowwithus.staff
|
||||
env:
|
||||
PHONE: ${TEST_STAFF_PHONE}
|
||||
OTP: ${TEST_STAFF_INVALID_OTP}
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Log In"
|
||||
- tapOn: "Log In"
|
||||
- assertVisible: "Send Code"
|
||||
- tapOn:
|
||||
id: staff_phone_input
|
||||
- inputText: ${PHONE}
|
||||
- hideKeyboard
|
||||
- tapOn: "Send Code"
|
||||
- assertVisible: "Continue"
|
||||
- tapOn:
|
||||
id: staff_otp_input
|
||||
- inputText: ${OTP}
|
||||
- extendedWaitUntil:
|
||||
visible: "Invalid"
|
||||
timeout: 5000
|
||||
- assertVisible: "Continue"
|
||||
@@ -0,0 +1,37 @@
|
||||
# Staff App — E2E: Set Availability
|
||||
# Flow:
|
||||
# - Home → Profile → Availability
|
||||
# - Try setting week days and saving
|
||||
# - Uses the "Quick Set Availability" buttons for Weekdays
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/availability/set_availability_e2e.yaml \
|
||||
# -e TEST_STAFF_PHONE=... \
|
||||
# -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- tapOn: "Availability"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "My Availability"
|
||||
timeout: 10000
|
||||
|
||||
# Try the built-in quick actions
|
||||
# "Weekdays" sets Mon-Fri to available
|
||||
- tapOn: "Weekdays"
|
||||
|
||||
- tapOn: "Back"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Availability saved successfully"
|
||||
timeout: 10000
|
||||
63
apps/mobile/apps/staff/maestro/compliance/fixture.pdf
Normal file
63
apps/mobile/apps/staff/maestro/compliance/fixture.pdf
Normal file
@@ -0,0 +1,63 @@
|
||||
%PDF-1.4
|
||||
%âãÏÓ
|
||||
1 0 obj
|
||||
<<
|
||||
/Type /Catalog
|
||||
/Pages 2 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/Type /Pages
|
||||
/Kids [3 0 R]
|
||||
/Count 1
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 4 0 R
|
||||
>>
|
||||
>>
|
||||
/MediaBox [0 0 612 792]
|
||||
/Contents 5 0 R
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type1
|
||||
/BaseFont /Helvetica
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Length 44
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
100 700 Td
|
||||
(Test Document) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
xref
|
||||
0 6
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000068 00000 n
|
||||
0000000125 00000 n
|
||||
0000000259 00000 n
|
||||
0000000347 00000 n
|
||||
trailer
|
||||
<<
|
||||
/Size 6
|
||||
/Root 1 0 R
|
||||
>>
|
||||
startxref
|
||||
442
|
||||
%%EOF
|
||||
@@ -0,0 +1,84 @@
|
||||
# Staff App — Compliance: Attire Upload E2E (full flow)
|
||||
# Purpose:
|
||||
# - Navigates Profile → Attire
|
||||
# - Opens the attire upload form
|
||||
# - Attempts to take/upload an attire photo (uses optional steps for camera flow)
|
||||
# - Verifies success state or confirmation that attire record is saved
|
||||
#
|
||||
# Prerequisite:
|
||||
# - Staff user with Attire compliance required (not yet uploaded)
|
||||
# - Device/emulator must support camera or have a pre-configured photo
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/compliance/attire_upload_e2e.yaml \
|
||||
# -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 20000
|
||||
|
||||
- tapOn: "(?i)Profile"
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
# Scroll down to find Attire section
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Attire"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Attire"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Attire|Uniform|Photo).*"
|
||||
timeout: 10000
|
||||
|
||||
# Entry assertion — Attire screen is open
|
||||
- assertVisible: "(?i).*(Attire|Uniform|Dress Code).*"
|
||||
|
||||
# Attempt to trigger upload / photo action
|
||||
- tapOn:
|
||||
text: "(?i).*(Upload|Take Photo|Add Photo|Camera).*"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# If camera permission prompt appears — allow it
|
||||
- tapOn:
|
||||
text: "(?i).*(Allow|OK|Permit).*"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# If a photo capture confirmation button appears
|
||||
- tapOn:
|
||||
text: "(?i).*(Use Photo|Done|Confirm|Save).*"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
# Save attire submission
|
||||
- tapOn:
|
||||
text: "(?i).*(Save Attire|Submit|Upload Attire|Save).*"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# Success: either a success snackbar or return to profile
|
||||
- assertVisible:
|
||||
text: "(?i).*(Attire uploaded|Attire saved|Photo uploaded|submitted successfully|Success).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — still in profile/compliance context (no crash)
|
||||
- assertVisible: "(?i).*(Attire|Profile|Compliance|Uniform).*"
|
||||
@@ -0,0 +1,73 @@
|
||||
# Staff App — E2E: Certificate Upload
|
||||
# Purpose:
|
||||
# - Opens Certificates
|
||||
# - Uploads a PDF certificate and verifies success snackbar
|
||||
#
|
||||
# Prerequisite:
|
||||
# - Push fixture.pdf to emulator/device before running:
|
||||
# bash apps/mobile/apps/staff/maestro/compliance/push_fixture.sh
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/compliance/certificate_upload_e2e.yaml \
|
||||
# -e TEST_STAFF_PHONE=... \
|
||||
# -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Certificates"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
- tapOn: "Certificates"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Certificates"
|
||||
timeout: 15000
|
||||
|
||||
# Start upload (button can exist on cards or as an add-more CTA)
|
||||
- tapOn:
|
||||
text: "Upload Certificate"
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "Add Another Certificate"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Upload Certificate"
|
||||
timeout: 10000
|
||||
|
||||
- assertVisible: "Certificate Name"
|
||||
- tapOn: "Certificate Name"
|
||||
- inputText: "E2E Food Handler Permit"
|
||||
- hideKeyboard
|
||||
|
||||
- tapOn: "Certificate Issuer"
|
||||
- inputText: "E2E Department"
|
||||
- hideKeyboard
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Upload File"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "Upload File"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "fixture.pdf"
|
||||
timeout: 10000
|
||||
- tapOn: "fixture.pdf"
|
||||
|
||||
- tapOn: "Save Certificate"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Certificate successfully uploaded and pending verification"
|
||||
timeout: 20000
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# Staff App — E2E: Document Upload (State change flow)
|
||||
# Flow:
|
||||
# - Home → Profile → Documents → Select a pending document (Upload)
|
||||
# - Select PDF file (relies on a pushed fixture)
|
||||
# - Check Attestation -> Submit Document -> Verify success message
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
# Wait for splash/loading to finish and home page to be visible
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 30000
|
||||
|
||||
- tapOn: "(?i)Profile"
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Documents"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
|
||||
- tapOn: "(?i)Documents"
|
||||
|
||||
# Wait for the Documents page title. Using regex with wildcards for maximum flexibility.
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Documents.*"
|
||||
timeout: 20000
|
||||
optional: true
|
||||
|
||||
# If the Documents title isn't found, try looking for the progress card or empty state
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Document Verification|No documents found).*"
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
# Tap the first Upload button available (uses staff_document_upload ID in code)
|
||||
- tapOn:
|
||||
id: "staff_document_upload"
|
||||
optional: true
|
||||
|
||||
# If ID not found, try text as fallback
|
||||
- tapOn:
|
||||
text: "(?i)Upload"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Only PDF files are accepted.*"
|
||||
timeout: 15000
|
||||
optional: true
|
||||
|
||||
# Open native file picker
|
||||
- tapOn:
|
||||
id: "native_file_picker" # Optional ID if exists
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "(?i)Select PDF File"
|
||||
optional: true
|
||||
|
||||
# Wait for file picker content
|
||||
- extendedWaitUntil:
|
||||
visible: "fixture.pdf"
|
||||
timeout: 15000
|
||||
optional: true
|
||||
|
||||
# Select the pushed fixture
|
||||
- tapOn:
|
||||
text: "fixture.pdf"
|
||||
optional: true
|
||||
|
||||
# Wait to return after selection
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*I certify that this document is genuine and valid.*"
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
# Check attestation
|
||||
- tapOn:
|
||||
text: "(?i).*I certify that this document is genuine and valid.*"
|
||||
optional: true
|
||||
|
||||
- tapOn:
|
||||
text: "(?i)Submit Document"
|
||||
optional: true
|
||||
|
||||
# Success validation
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Document uploaded successfully"
|
||||
timeout: 20000
|
||||
optional: true
|
||||
|
||||
# Post-action validation: Ensure the list reflects the new document
|
||||
- back # Navigate away from success/view screen
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*Documents.*"
|
||||
timeout: 15000
|
||||
optional: true
|
||||
|
||||
# Verify that the document label or a 'Pending' status is now present
|
||||
- assertVisible:
|
||||
text: "(?i).*(Pending Review|Verification in progress|Pending).*"
|
||||
optional: true
|
||||
10
apps/mobile/apps/staff/maestro/compliance/push_fixture.sh
Normal file
10
apps/mobile/apps/staff/maestro/compliance/push_fixture.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Push the dummy PDF to the emulator for E2E upload flow testing
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
FIXTURE_PATH="$SCRIPT_DIR/fixture.pdf"
|
||||
|
||||
# Push to the emulator downloads folder
|
||||
adb push "$FIXTURE_PATH" /sdcard/Download/fixture.pdf
|
||||
|
||||
echo "Pushed fixture.pdf to /sdcard/Download/"
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Compatibility wrapper for older docs/flows that reference push_upload_fixture.sh.
|
||||
# Preferred script name: push_fixture.sh
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
|
||||
bash "$SCRIPT_DIR/push_fixture.sh"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Staff App — Attire upload file restriction banner (#552)
|
||||
# Run: maestro test .../compliance/attire_upload_banner.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
env:
|
||||
PHONE: ${TEST_STAFF_PHONE}
|
||||
OTP: ${TEST_STAFF_OTP}
|
||||
# Run after sign_in: maestro test auth/sign_in.yaml compliance/attire_upload_banner.yaml -e ...
|
||||
---
|
||||
- launchApp
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- scrollUntilVisible:
|
||||
element: "Attire"
|
||||
- tapOn: "Attire"
|
||||
@@ -0,0 +1,17 @@
|
||||
# Staff App — Certificate upload file restriction banner (#551)
|
||||
# Run: maestro test .../compliance/certificate_upload_banner.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
env:
|
||||
PHONE: ${TEST_STAFF_PHONE}
|
||||
OTP: ${TEST_STAFF_OTP}
|
||||
# Run after sign_in: maestro test auth/sign_in.yaml compliance/certificate_upload_banner.yaml -e ...
|
||||
---
|
||||
- launchApp
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- scrollUntilVisible:
|
||||
element: "Certificates"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- tapOn: "Certificates"
|
||||
@@ -0,0 +1,17 @@
|
||||
# Staff App — Document upload file restriction banner (#550)
|
||||
# Run: maestro test .../compliance/document_upload_banner.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
env:
|
||||
PHONE: ${TEST_STAFF_PHONE}
|
||||
OTP: ${TEST_STAFF_OTP}
|
||||
# Run after sign_in: maestro test auth/sign_in.yaml compliance/document_upload_banner.yaml -e ...
|
||||
---
|
||||
- launchApp
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- scrollUntilVisible:
|
||||
element: "Documents"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- tapOn: "Documents"
|
||||
@@ -0,0 +1,6 @@
|
||||
# Staff App — Benefits section on Home (#524)
|
||||
# Run: maestro test auth/sign_in.yaml home/benefits.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Home"
|
||||
@@ -0,0 +1,63 @@
|
||||
# Staff App — Home: Incomplete profile banner smoke
|
||||
# Purpose:
|
||||
# - Navigates to Home screen
|
||||
# - Verifies that when a profile is incomplete, a banner/nudge is displayed
|
||||
# prompting the staff member to complete their profile
|
||||
# - Deterministic: if profile IS complete, completion state is simply not asserted
|
||||
#
|
||||
# Note: Banner visibility depends on profile completion state of the test account.
|
||||
# The test always passes — it just verifies the banner if present.
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/home/incomplete_profile_banner_smoke.yaml \
|
||||
# -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 20000
|
||||
|
||||
- tapOn: "(?i)Home"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Welcome back|Home).*"
|
||||
timeout: 15000
|
||||
|
||||
# Entry assertion — home screen is loaded
|
||||
- assertVisible: "(?i).*(Welcome back|Home).*"
|
||||
|
||||
# Case A: incomplete profile banner is visible
|
||||
- assertVisible:
|
||||
text: "(?i).*(Complete your profile|Profile incomplete|finish setting up|complete account|missing information).*"
|
||||
optional: true
|
||||
|
||||
# Tap the banner CTA if present (verifies it navigates to profile)
|
||||
- tapOn:
|
||||
text: "(?i).*(Complete Now|Complete Profile|Finish Setup|Get Started).*"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# If tapped, verify we landed on Profile
|
||||
- assertVisible:
|
||||
text: "(?i).*(Profile|Personal Info|Documents).*"
|
||||
optional: true
|
||||
|
||||
# Navigate back to Home
|
||||
- back
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# Case B: profile is complete — no banner (still a valid pass)
|
||||
- assertVisible:
|
||||
text: "(?i).*(Welcome back|Home|Benefits|Shifts).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — still in app context, no crash
|
||||
- assertVisible: "(?i).*(Home|Profile|Shifts).*"
|
||||
@@ -0,0 +1,22 @@
|
||||
# Staff App — Availability navigation
|
||||
# Run: maestro test auth/sign_in.yaml navigation/availability.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
# Availability is usually reached from Home quick actions; keep this stable.
|
||||
- tapOn: "Home"
|
||||
- extendedWaitUntil:
|
||||
visible: "Welcome back"
|
||||
timeout: 15000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Availability"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
- tapOn: "Availability"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Availability"
|
||||
timeout: 15000
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Staff App — Clock In tab navigation
|
||||
# Run: maestro test auth/sign_in.yaml navigation/clock_in.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
# Wait for the home/tab bar to be available
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 10000
|
||||
|
||||
# Try common clock-in labels and IDs
|
||||
# Note: This tab is hidden if profile is incomplete
|
||||
- tapOn:
|
||||
id: "nav_clock_in"
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "(?i)Clock In"
|
||||
optional: true
|
||||
|
||||
# Check if we landed on the clock-in screen
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Clock In to your Shift"
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "(?i).*(Clock In|Shift).*"
|
||||
optional: true
|
||||
@@ -0,0 +1,6 @@
|
||||
# Staff App — Home tab (default after sign-in)
|
||||
# Run: maestro test auth/sign_in.yaml navigation/home.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Welcome back"
|
||||
@@ -0,0 +1,31 @@
|
||||
# Staff App — Payments tab navigation
|
||||
# Run: maestro test auth/sign_in.yaml navigation/payments.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
# Wait for some Home state to be ready
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 15000
|
||||
|
||||
# Try common payments/earnings labels and IDs
|
||||
# Note: This tab is hidden if profile is incomplete
|
||||
- tapOn:
|
||||
id: "nav_payments"
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "(?i)Earnings"
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "(?i)Payments"
|
||||
optional: true
|
||||
|
||||
# Check if we landed on a payments-related screen
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Earnings|Payments).*"
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "(?i).*(Earnings|Payments).*"
|
||||
optional: true
|
||||
11
apps/mobile/apps/staff/maestro/navigation/smoke/profile.yaml
Normal file
11
apps/mobile/apps/staff/maestro/navigation/smoke/profile.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Staff App — Profile tab navigation (post sign-in)
|
||||
# Run: maestro test auth/sign_in.yaml navigation/profile.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Profile|Log In).*"
|
||||
timeout: 10000
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
- assertVisible: "Personal Info"
|
||||
11
apps/mobile/apps/staff/maestro/navigation/smoke/shifts.yaml
Normal file
11
apps/mobile/apps/staff/maestro/navigation/smoke/shifts.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Staff App — Shifts tab navigation (post sign-in)
|
||||
# Run: maestro test auth/sign_in.yaml navigation/shifts.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back|Log In).*"
|
||||
timeout: 10000
|
||||
- assertVisible: "Shifts"
|
||||
- tapOn: "Shifts"
|
||||
- assertVisible: "Find Shifts"
|
||||
@@ -0,0 +1,94 @@
|
||||
# Staff App — E2E: Earnings & Early Pay View
|
||||
# Flow:
|
||||
# - Launch App → Navigate to More/Payments
|
||||
# - Checks for "Earnings" or "Total Earnings" state loading
|
||||
# - Navigates across Period tabs (Week/Month/Year)
|
||||
# - If Early Pay is available, attempts to trigger cash out flow.
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
# Waiting for Home page content
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 15000
|
||||
|
||||
# Accessing Payments/Earnings from Home layout.
|
||||
# If there's an explicit Earnings tab.
|
||||
- tapOn:
|
||||
id: "nav_payments"
|
||||
optional: true
|
||||
|
||||
# If it's not ID'd, usually "Earnings" or "Pay" exists
|
||||
- tapOn:
|
||||
text: "(?i)Earnings"
|
||||
optional: true
|
||||
|
||||
# In PaymentsPage (payments_page.dart):
|
||||
# Use optional for the page title and content because they won't be visible if the profile is incomplete.
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Earnings"
|
||||
timeout: 15000
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "(?i)Earnings"
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "(?i)Recent Payments"
|
||||
optional: true
|
||||
|
||||
# Test tabs
|
||||
- tapOn:
|
||||
text: "(?i)Month"
|
||||
optional: true
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)This Month"
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn:
|
||||
text: "(?i)Year"
|
||||
optional: true
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)This Year"
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
- tapOn:
|
||||
text: "(?i)Week"
|
||||
optional: true
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)This Week"
|
||||
timeout: 5000
|
||||
optional: true
|
||||
|
||||
# Test Early Pay if module is active.
|
||||
- tapOn:
|
||||
text: "(?i)Cash Out"
|
||||
optional: true
|
||||
|
||||
# Use scrollUntilVisible with assertions to ensure data integrity
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Recent Payments"
|
||||
visibilityPercentage: 100
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "(?i)Recent Payments"
|
||||
optional: true
|
||||
|
||||
# Specifically check for presence of some payment rows to avoid empty screens
|
||||
- assertVisible:
|
||||
id: "payment_item_row"
|
||||
optional: true
|
||||
|
||||
# Verification of Back Navigation (Ensures the backstack isn't corrupted)
|
||||
- back
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 10000
|
||||
@@ -0,0 +1,71 @@
|
||||
# Staff App — Payments: Payment detail view smoke
|
||||
# Purpose:
|
||||
# - Navigates to the Payments tab
|
||||
# - If payment history items exist, taps the first one
|
||||
# - Verifies the detail screen loads with expected UI elements
|
||||
# (date, amount, shift info, status)
|
||||
# - If no payment history, verifies empty state is handled gracefully
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/payments/payment_detail_smoke.yaml \
|
||||
# -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Home|Shifts|Welcome back).*"
|
||||
timeout: 20000
|
||||
|
||||
# Navigate to Payments
|
||||
- tapOn:
|
||||
id: "nav_payments"
|
||||
optional: true
|
||||
|
||||
- tapOn:
|
||||
text: "(?i).*(Earnings|Payments|Pay).*"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Earnings|Payments|Recent Payments).*"
|
||||
timeout: 15000
|
||||
optional: true
|
||||
|
||||
# Scroll to payment history section
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Recent Payments"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
# Case A: payments exist — tap first item to open detail view
|
||||
- tapOn:
|
||||
id: "payment_item_row"
|
||||
optional: true
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 2000
|
||||
|
||||
# Detail screen should show shift/payment specifics
|
||||
- assertVisible:
|
||||
text: "(?i).*(Amount|$|Earnings|hours|shift|date|status).*"
|
||||
optional: true
|
||||
|
||||
# Navigate back to payments list
|
||||
- back
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i).*(Earnings|Payments|Recent Payments).*"
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
# Case B: no payments — empty state is shown
|
||||
- assertVisible:
|
||||
text: "(?i).*(No payments|No earnings|Nothing yet|No records).*"
|
||||
optional: true
|
||||
|
||||
# Exit assertion — back in payments context, no crash
|
||||
- assertVisible: "(?i).*(Earnings|Payments|Home|Shifts).*"
|
||||
@@ -0,0 +1,39 @@
|
||||
# Staff App — Payments: history visible (smoke)
|
||||
# Purpose:
|
||||
# - Navigates to Earnings/Payments
|
||||
# - Verifies Total Earnings loads and Recent Payments section is reachable
|
||||
#
|
||||
# Run:
|
||||
# maestro test \
|
||||
# apps/mobile/apps/staff/maestro/auth/sign_in.yaml \
|
||||
# apps/mobile/apps/staff/maestro/payments/payment_history_smoke.yaml \
|
||||
# -e TEST_STAFF_PHONE=... \
|
||||
# -e TEST_STAFF_OTP=...
|
||||
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Shifts"
|
||||
timeout: 10000
|
||||
|
||||
# Try tab/button variants depending on profile
|
||||
- tapOn:
|
||||
id: "nav_payments"
|
||||
optional: true
|
||||
- tapOn:
|
||||
text: "Earnings"
|
||||
optional: true
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Total Earnings"
|
||||
timeout: 15000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Recent Payments"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
|
||||
- assertVisible: "Recent Payments"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Staff App — Attire section (Profile > Onboarding > Attire)
|
||||
# Run: maestro test auth/sign_in.yaml profile/attire.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Attire"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
- tapOn: "Attire"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "Verify Attire"
|
||||
timeout: 15000
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Staff App — Bank Account section (Profile > Finance > Bank Account)
|
||||
# Run: maestro test auth/sign_in.yaml profile/bank_account.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Bank Account"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- tapOn: "(?i)Bank Account"
|
||||
|
||||
- extendedWaitUntil:
|
||||
visible: "(?i)Bank Account"
|
||||
timeout: 10000
|
||||
|
||||
# The AppBar title should be Bank Account
|
||||
- assertVisible: "(?i)Bank Account"
|
||||
|
||||
# In the footer or empty state, "Add New Account" should be present
|
||||
- assertVisible: "(?i)Add New Account"
|
||||
|
||||
# Optional: AccountCard.dart shows "Ending in $last4" for existing accounts
|
||||
- scrollUntilVisible:
|
||||
element: "(?i)Ending in .*"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
optional: true
|
||||
|
||||
- assertVisible:
|
||||
text: "(?i)Ending in .*"
|
||||
optional: true
|
||||
@@ -0,0 +1,10 @@
|
||||
# Staff App — Certificates list page
|
||||
# Run: maestro test auth/sign_in.yaml profile/certificates_list.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
- assertVisible: "Certificates"
|
||||
- tapOn: "Certificates"
|
||||
- assertVisible: "Certificates"
|
||||
@@ -0,0 +1,10 @@
|
||||
# Staff App — Documents list page
|
||||
# Run: maestro test auth/sign_in.yaml profile/documents_list.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
- assertVisible: "Documents"
|
||||
- tapOn: "Documents"
|
||||
- assertVisible: "Documents"
|
||||
@@ -0,0 +1,18 @@
|
||||
# Staff App — Emergency Contact section (Profile > Onboarding > Emergency Contact)
|
||||
# Run: maestro test auth/sign_in.yaml profile/emergency_contact.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- assertVisible: "Profile"
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
- scrollUntilVisible:
|
||||
element: "Emergency Contact"
|
||||
visibilityPercentage: 50
|
||||
timeout: 10000
|
||||
- tapOn: "Emergency Contact"
|
||||
- extendedWaitUntil:
|
||||
visible: "Save & Continue"
|
||||
timeout: 10000
|
||||
- assertVisible: "Emergency Contact"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Staff App — Experience section (Profile > Onboarding > Experience)
|
||||
# Run: maestro test auth/sign_in.yaml profile/experience.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
|
||||
appId: com.krowwithus.staff
|
||||
---
|
||||
- launchApp
|
||||
- tapOn: "Profile"
|
||||
- waitForAnimationToEnd:
|
||||
timeout: 3000
|
||||
|
||||
- scrollUntilVisible:
|
||||
element: "Experience"
|
||||
visibilityPercentage: 50
|
||||
timeout: 15000
|
||||
- tapOn: "Experience"
|
||||
|
||||
# Landing assertion kept flexible; title should exist somewhere on screen
|
||||
- extendedWaitUntil:
|
||||
visible: "Experience"
|
||||
timeout: 15000
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user