From b43e28e09dfa8fbb9dfc4db5e017b04079914ed6 Mon Sep 17 00:00:00 2001 From: Suriya Date: Thu, 5 Mar 2026 17:50:46 +0530 Subject: [PATCH] Implement Maestro E2E Tests for Mobile Happy Paths & Document #572 --- apps/mobile/apps/client/maestro/README.md | 24 +- apps/mobile/apps/staff/maestro/README.md | 23 +- .../billing_connector_repository_impl.dart | 96 ++++---- .../research/maestro-test-run-instructions.md | 4 + docs/testing/maestro-e2e-happy-paths.md | 227 ++++++++++++++++++ makefiles/mobile.mk | 26 +- 6 files changed, 349 insertions(+), 51 deletions(-) create mode 100644 docs/testing/maestro-e2e-happy-paths.md diff --git a/apps/mobile/apps/client/maestro/README.md b/apps/mobile/apps/client/maestro/README.md index 74680bc9..b2ed5785 100644 --- a/apps/mobile/apps/client/maestro/README.md +++ b/apps/mobile/apps/client/maestro/README.md @@ -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,8 @@ maestro/ auth/ sign_in.yaml sign_up.yaml + sign_out.yaml + sign_in_invalid_password.yaml navigation/ home.yaml orders.yaml @@ -20,8 +22,22 @@ maestro/ view_orders.yaml completed_no_edit_icon.yaml # #492 create_order_entry.yaml + create_order_one_time_e2e.yaml + hubs/ + create_hub_e2e.yaml + manage_hubs_from_settings.yaml + billing/ + billing_overview.yaml + invoice_approval_e2e.yaml + reports/ + reports_dashboard.yaml + home/ + home_dashboard_widgets.yaml + tab_bar_roundtrip.yaml settings/ settings_page.yaml + edit_profile.yaml + logout_flow.yaml ``` ## Credentials (env, never hardcoded) @@ -37,7 +53,9 @@ maestro/ ```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) # Direct maestro test apps/mobile/apps/client/maestro/auth/sign_in.yaml \ diff --git a/apps/mobile/apps/staff/maestro/README.md b/apps/mobile/apps/staff/maestro/README.md index 4e5b7463..33ea79f1 100644 --- a/apps/mobile/apps/staff/maestro/README.md +++ b/apps/mobile/apps/staff/maestro/README.md @@ -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,23 +10,38 @@ 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 + bank_account.yaml + faqs.yaml + privacy_security.yaml + emergency_contact.yaml compliance/ document_upload_banner.yaml # #550 certificate_upload_banner.yaml # #551 attire_upload_banner.yaml # #552 + document_upload_e2e.yaml shifts/ find_shifts.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 home/ benefits.yaml # #524 ``` @@ -49,7 +64,9 @@ maestro/ ```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) # Direct maestro test apps/mobile/apps/staff/maestro/auth/sign_in.yaml \ diff --git a/apps/mobile/packages/data_connect/lib/src/connectors/billing/data/repositories/billing_connector_repository_impl.dart b/apps/mobile/packages/data_connect/lib/src/connectors/billing/data/repositories/billing_connector_repository_impl.dart index 63cda77d..523b7f16 100644 --- a/apps/mobile/packages/data_connect/lib/src/connectors/billing/data/repositories/billing_connector_repository_impl.dart +++ b/apps/mobile/packages/data_connect/lib/src/connectors/billing/data/repositories/billing_connector_repository_impl.dart @@ -1,4 +1,4 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs +// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs import 'package:firebase_data_connect/src/core/ref.dart'; import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_domain/krow_domain.dart'; @@ -247,51 +247,59 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository { ); }).toList(); } - // Fallback: If roles is empty, try to get workers from shift applications - else if (invoice.shift != null && - invoice.shift.applications_on_shift != null) { - final List apps = invoice.shift.applications_on_shift; - workers = apps.map((dynamic app) { - final String name = app.staff?.fullName ?? 'Unknown'; - final String roleTitle = app.shiftRole?.role?.name ?? 'Staff'; - final double amount = - (app.shiftRole?.totalValue as num?)?.toDouble() ?? 0.0; - final double hours = (app.shiftRole?.hours as num?)?.toDouble() ?? 0.0; + // Fallback: If roles is empty, try to get workers from shift applications. + // Only when the invoice type has a 'shift' field (e.g. getInvoiceById); listInvoicesByBusinessId + // generated type has shiftId but no shift getter, so we guard with try/catch. + else { + try { + final dynamic shift = (invoice as dynamic).shift; + if (shift != null && shift.applications_on_shift != null) { + final List apps = shift.applications_on_shift; + workers = apps.map((dynamic app) { + final String name = app.staff?.fullName ?? 'Unknown'; + final String roleTitle = app.shiftRole?.role?.name ?? 'Staff'; + final double amount = + (app.shiftRole?.totalValue as num?)?.toDouble() ?? 0.0; + final double hours = (app.shiftRole?.hours as num?)?.toDouble() ?? 0.0; - // Calculate rate if not explicitly provided - double rate = 0.0; - if (hours > 0) { - rate = amount / hours; + // Calculate rate if not explicitly provided + double rate = 0.0; + if (hours > 0) { + rate = amount / hours; + } + + // Map break type to minutes + int breakMin = 0; + final String? breakType = app.shiftRole?.breakType?.toString(); + if (breakType != null) { + if (breakType.contains('10')) + breakMin = 10; + else if (breakType.contains('15')) + breakMin = 15; + else if (breakType.contains('30')) + breakMin = 30; + else if (breakType.contains('45')) + breakMin = 45; + else if (breakType.contains('60')) + breakMin = 60; + } + + return InvoiceWorker( + name: name, + role: roleTitle, + amount: amount, + hours: hours, + rate: rate, + checkIn: _service.toDateTime(app.checkInTime), + checkOut: _service.toDateTime(app.checkOutTime), + breakMinutes: breakMin, + avatarUrl: app.staff?.photoUrl, + ); + }).toList(); } - - // Map break type to minutes - int breakMin = 0; - final String? breakType = app.shiftRole?.breakType?.toString(); - if (breakType != null) { - if (breakType.contains('10')) - breakMin = 10; - else if (breakType.contains('15')) - breakMin = 15; - else if (breakType.contains('30')) - breakMin = 30; - else if (breakType.contains('45')) - breakMin = 45; - else if (breakType.contains('60')) - breakMin = 60; - } - - return InvoiceWorker( - name: name, - role: roleTitle, - amount: amount, - hours: hours, - rate: rate, - checkIn: _service.toDateTime(app.checkInTime), - checkOut: _service.toDateTime(app.checkOutTime), - breakMinutes: breakMin, - avatarUrl: app.staff?.photoUrl, - ); - }).toList(); + } catch (_) { + // Invoice type has no 'shift' getter (e.g. ListInvoicesByBusinessIdInvoices). Skip. + } } return Invoice( diff --git a/docs/research/maestro-test-run-instructions.md b/docs/research/maestro-test-run-instructions.md index 593a0c81..715559f5 100644 --- a/docs/research/maestro-test-run-instructions.md +++ b/docs/research/maestro-test-run-instructions.md @@ -2,6 +2,8 @@ Credentials are injected via env variables — **never hardcoded** in YAML. +**E2E Happy Path test cases (purpose, steps, expected outcomes):** see [docs/testing/maestro-e2e-happy-paths.md](../testing/maestro-e2e-happy-paths.md) (#572). + ## Env variables | Flow | Env variables | @@ -186,6 +188,7 @@ maestro test apps/mobile/apps/staff/maestro/compliance/document_upload_banner.ya | `make test-e2e` | Auth flows (sign_in, sign_up for both apps) | | `make test-e2e-client` | Client auth flows | | `make test-e2e-client-extended` | Client full suite (auth + nav + orders + settings) | +| `make test-e2e-client-happy-path` | Client happy path (auth + hubs + create order E2E + billing + reports + logout) — #572 | | `make test-e2e-client-auth` | Client auth (sign_in, sign_up) | | `make test-e2e-client-navigation` | Client navigation (sign_in + home, orders, billing, coverage, reports) | | `make test-e2e-client-orders` | Client orders (sign_in + view_orders, completed_no_edit_icon, create_order_entry) | @@ -193,6 +196,7 @@ maestro test apps/mobile/apps/staff/maestro/compliance/document_upload_banner.ya | `make test-e2e-client-sign-out` | Client sign out flow | | `make test-e2e-staff` | Staff auth flows | | `make test-e2e-staff-extended` | Staff full suite | +| `make test-e2e-staff-happy-path` | Staff happy path (auth + clock in/out + availability + document upload + payments + sign out) — #572 | | `make test-e2e-staff-auth` | Staff auth (sign_in, sign_up) | | `make test-e2e-staff-navigation` | Staff navigation (sign_in + home, shifts, profile, payments, clock_in) | | `make test-e2e-staff-profile` | Staff profile (sign_in + personal_info, documents_list, certificates_list) | diff --git a/docs/testing/maestro-e2e-happy-paths.md b/docs/testing/maestro-e2e-happy-paths.md new file mode 100644 index 00000000..304046c1 --- /dev/null +++ b/docs/testing/maestro-e2e-happy-paths.md @@ -0,0 +1,227 @@ +# Maestro E2E Happy Path Test Cases + +This document describes the End-to-End (E2E) **happy path** test cases for the KROW mobile applications (Client and Staff), implemented with [Maestro](https://maestro.mobile.dev/). It supports **ticket #572**: implement and document E2E happy path tests so core functionalities remain stable and regressions are caught early. + +## Overview + +| Item | Description | +|------|-------------| +| **Framework** | Maestro | +| **Apps** | Client (`com.krowwithus.client`), Staff (`com.krowwithus.staff`) | +| **Scope** | Happy path user flows only (success paths; negative/edge cases are separate) | +| **Run locally** | See [How to run](#how-to-run) and [docs/research/maestro-test-run-instructions.md](../research/maestro-test-run-instructions.md) | + +## Prerequisites + +- **Maestro CLI** installed ([Install guide](../research/maestro-test-run-instructions.md#1-install-maestro-cli)) +- **Firebase test phone** for Staff app (e.g. +1 555-555-1234 / OTP 123123) +- **Client & Staff APKs** built and installed on device/emulator +- **Env variables** set for credentials (never hardcode; see [Credentials](#credentials)) + +## Credentials + +| App | Flow | Env variables | +|-----|------|----------------| +| Client | sign_in / sign_up | `TEST_CLIENT_EMAIL`, `TEST_CLIENT_PASSWORD`, `TEST_CLIENT_COMPANY` (sign_up only) | +| Staff | sign_in / sign_up | `TEST_STAFF_PHONE`, `TEST_STAFF_OTP`, `TEST_STAFF_SIGNUP_PHONE` (sign_up only) | + +Example (Bash): +`export TEST_CLIENT_EMAIL=testclient@gmail.com TEST_CLIENT_PASSWORD=testclient!` +`export TEST_STAFF_PHONE=5555551234 TEST_STAFF_OTP=123123` + +--- + +## Client App — Happy Path Test Cases + +All client flows assume the app is **not** logged in at start unless noted (e.g. flows that run after `sign_in.yaml`). + +### Auth + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-AUTH-1 | **sign_in** | Verify user can sign in with email/password | Launch app → Sign In → enter email/password (env) → Sign In | Home tab visible, authenticated | `maestro test apps/mobile/apps/client/maestro/auth/sign_in.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...` | +| C-AUTH-2 | **sign_up** | Verify new client can register | Launch app → Create Account → email, password, company (env) → submit | Home tab visible, authenticated | Same as above + `-e TEST_CLIENT_COMPANY=...` | +| C-AUTH-3 | **sign_out** | Verify user can log out | Run after sign_in → Settings (gear) → Log Out → confirm | Create Account / login screen visible | Run `sign_in.yaml` then `auth/sign_out.yaml` (or use `settings/logout_flow.yaml` for gear-based logout) | + +### Navigation + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-NAV-1 | **home** | Home tab loads after login | Launch (logged in) → tap Home | "Welcome back" and home content visible | `navigation/home.yaml` after sign_in | +| C-NAV-2 | **orders** | Orders tab opens | Tap Orders | Orders list or empty state visible | `navigation/orders.yaml` | +| C-NAV-3 | **billing** | Billing tab opens | Tap Billing | Billing / invoices section visible | `navigation/billing.yaml` | +| C-NAV-4 | **coverage** | Coverage tab opens | Tap Coverage | Coverage screen visible | `navigation/coverage.yaml` | +| C-NAV-5 | **reports** | Reports tab opens | Tap Reports | Reports screen visible | `navigation/reports.yaml` | + +### Orders + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-ORD-1 | **view_orders** | Orders list loads | After sign_in → Orders → list loads | Order list or empty state | `orders/view_orders.yaml` | +| C-ORD-2 | **create_order_entry** | Reach create-order entry (type selection) | Home → Create Order | One-Time / Rapid / etc. options visible | `orders/create_order_entry.yaml` | +| C-ORD-3 | **create_order_one_time_e2e** | Full flow: create one-time order | Home → Create Order → One-Time → fill Event Name, Role → Create | Success; back to orders or confirmation | `orders/create_order_one_time_e2e.yaml` | +| C-ORD-4 | **completed_no_edit_icon** | Completed orders do not show edit | Open orders → completed order | No edit action on completed order | `orders/completed_no_edit_icon.yaml` (#492) | + +### Hubs + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-HUB-1 | **create_hub_e2e** | Create a clock-in hub and see it in list | Settings (gear) → Clock-In Hubs → Add Hub → name, address → Create | Hubs list visible; new hub present | `hubs/create_hub_e2e.yaml` | +| C-HUB-2 | **manage_hubs_from_settings** | Open hubs management from settings | Settings → Clock-In Hubs | Hubs list / management screen | `hubs/manage_hubs_from_settings.yaml` | + +### Billing + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-BIL-1 | **billing_overview** | Billing tab and overview load | Tap Billing | Billing overview / tabs visible | `billing/billing_overview.yaml` | +| C-BIL-2 | **invoice_approval_e2e** | Approve a pending invoice | Billing → Awaiting Approval → first invoice → Review & Approve → Approve | Success feedback; back to list | `billing/invoice_approval_e2e.yaml` (requires at least one invoice in "Awaiting Approval") | + +### Reports + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-RPT-1 | **reports_dashboard** | Reports dashboard loads | Tap Reports | Reports dashboard visible | `reports/reports_dashboard.yaml` | + +### Settings & Home + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| C-SET-1 | **settings_page** | Settings screen opens | After sign_in → Settings (gear) | Quick Links, Log Out, etc. visible | `settings/settings_page.yaml` | +| C-SET-2 | **edit_profile** | Edit profile screen opens and is usable | Settings → profile edit entry | Profile form visible | `settings/edit_profile.yaml` | +| C-SET-3 | **logout_flow** | Log out via Settings gear | Settings (gear) → Log Out → confirm | Create Account / login screen | `settings/logout_flow.yaml` | +| C-HOM-1 | **home_dashboard_widgets** | Dashboard widgets and quick actions render | Home → check Actions, Coverage, etc. | Widgets and "Create Order" visible | `home/home_dashboard_widgets.yaml` | +| C-HOM-2 | **tab_bar_roundtrip** | All main tabs can be opened in sequence | Home → Orders → Billing → Coverage → Reports → Home | No crash; each tab loads | `home/tab_bar_roundtrip.yaml` | + +--- + +## Staff App — Happy Path Test Cases + +### Auth + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-AUTH-1 | **sign_in** | Staff signs in with phone + OTP | Launch → enter phone (env) → OTP (env) | Home / main app visible | `maestro test apps/mobile/apps/staff/maestro/auth/sign_in.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...` | +| S-AUTH-2 | **sign_up** | New staff registers with phone | Launch → sign up path → phone (env) → OTP (env) | Onboarding or home | Same + `TEST_STAFF_SIGNUP_PHONE` | +| S-AUTH-3 | **sign_out** | Staff logs out from Profile | After sign_in → Profile → Sign Out | Log In screen visible | `auth/sign_out.yaml` after sign_in | + +### Navigation + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-NAV-1 | **home** | Home tab loads | Tap Home | Welcome / home content | `navigation/home.yaml` | +| S-NAV-2 | **shifts** | Shifts tab opens | Tap Shifts | Shifts list or empty state | `navigation/shifts.yaml` | +| S-NAV-3 | **profile** | Profile tab opens | Tap Profile | Profile sections visible | `navigation/profile.yaml` | +| S-NAV-4 | **payments** | Payments tab opens | Tap Payments | Payments screen | `navigation/payments.yaml` | +| S-NAV-5 | **clock_in** (tab) | Clock In tab opens | Tap Clock In | Clock In UI visible | `navigation/clock_in.yaml` | +| S-NAV-6 | **availability** | Availability screen opens | From home or profile → Availability | Availability UI | `navigation/availability.yaml` | + +### Shifts + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-SHF-1 | **find_shifts** | Shifts list / find shifts loads | Shifts tab → find shifts | Shifts or empty state | `shifts/find_shifts.yaml` | +| S-SHF-2 | **clock_in_e2e** | Clock in to an active shift | Clock In tab → Swipe to Check In (optional attire photo) | Check-in success | `shifts/clock_in_e2e.yaml` (requires active shift today, within range) | +| S-SHF-3 | **clock_out_e2e** | Clock out from shift | After clocked in → Clock Out | Clock-out success | `shifts/clock_out_e2e.yaml` (requires clocked-in state) | + +### Availability + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-AVL-1 | **set_availability_e2e** | Set or view availability | Open availability → set days/times → save | Success or list updated | `availability/set_availability_e2e.yaml` | + +### Profile + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-PRF-1 | **personal_info** | Personal info section opens | Profile → Personal Info | Form/section visible | `profile/personal_info.yaml` | +| S-PRF-2 | **documents_list** | Documents list opens | Profile → Documents | Documents list | `profile/documents_list.yaml` | +| S-PRF-3 | **certificates_list** | Certificates list opens | Profile → Certificates | Certificates list | `profile/certificates_list.yaml` | +| S-PRF-4 | **time_card** | Time card section opens | Profile → Time Card | Time card UI | `profile/time_card.yaml` | +| S-PRF-5 | **bank_account** | Bank account section opens | Profile → Bank Account | Bank account UI | `profile/bank_account.yaml` | +| S-PRF-6 | **faqs** | FAQs open | Profile → FAQs | FAQ content | `profile/faqs.yaml` | +| S-PRF-7 | **privacy_security** | Privacy & Security opens | Profile → Privacy & Security | Screen visible | `profile/privacy_security.yaml` | +| S-PRF-8 | **emergency_contact** | Emergency contact opens | Profile → Emergency Contact | Form/section visible | `profile/emergency_contact.yaml` | + +### Compliance + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-CMP-1 | **document_upload_banner** | Document upload CTA visible | After sign_in → navigate to docs | Upload banner/CTA visible | `compliance/document_upload_banner.yaml` (#550) | +| S-CMP-2 | **certificate_upload_banner** | Certificate upload CTA visible | Navigate to certificates | Upload banner visible | `compliance/certificate_upload_banner.yaml` (#551) | +| S-CMP-3 | **attire_upload_banner** | Attire upload CTA visible | Navigate to attire | Upload banner visible | `compliance/attire_upload_banner.yaml` (#552) | +| S-CMP-4 | **document_upload_e2e** | Upload a document (full flow) | Open documents → upload (e.g. PDF) | Upload success | `compliance/document_upload_e2e.yaml` | + +### Payments & Home + +| ID | Test | Purpose | Steps | Expected outcome | How to run | +|----|------|----------|-------|-------------------|------------| +| S-PAY-1 | **payments_view_e2e** | Payments screen loads and is usable | Tap Payments | Payments list or empty state | `payments/payments_view_e2e.yaml` | +| S-HOM-1 | **benefits** | Benefits section loads | Home → Benefits | Benefits content visible | `home/benefits.yaml` (#524) | + +--- + +## How to run + +### Make targets (recommended) + +From **project root** with env vars set: + +| Target | Description | +|--------|-------------| +| `make test-e2e` | Auth flows only (Client + Staff sign_in, sign_up, invalid password/OTP) | +| `make test-e2e-client` | Client auth (sign_in, sign_up) | +| `make test-e2e-client-extended` | Client: auth + navigation + orders + settings | +| `make test-e2e-client-happy-path` | Client: auth + hubs + create order E2E + billing + reports + logout | +| `make test-e2e-staff` | Staff auth (sign_in, sign_up) | +| `make test-e2e-staff-extended` | Staff: auth + navigation + profile + compliance + shifts + benefits | +| `make test-e2e-staff-happy-path` | Staff: auth + clock in/out + availability + document upload + payments + sign out | +| `make test-e2e-extended` | Both apps: full extended suites | + +### Single flow (Maestro CLI) + +```bash +# Client sign_in then create hub +maestro test --shard-split=1 \ + 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=... + +# Staff sign_in then clock in E2E +maestro test --shard-split=1 \ + apps/mobile/apps/staff/maestro/auth/sign_in.yaml \ + apps/mobile/apps/staff/maestro/shifts/clock_in_e2e.yaml \ + -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=... +``` + +Use `--shard-split=1` to run flows sequentially and reduce "tcp:7001 closed" issues between flows. + +### Success criteria + +- **Pass:** Maestro exits with code 0; all steps in the YAML complete (elements found, assertions passed). +- **Fail:** Any step times out or an assertion fails; Maestro exits non-zero and reports the failing step. + +### Prerequisites that can cause failures + +- **invoice_approval_e2e**: Test account must have at least one invoice in "Awaiting Approval". Otherwise the flow may timeout or not find "Review & Approve". +- **clock_in_e2e / clock_out_e2e**: Staff user must have an active shift (today, within check-in window) and be within GPS range (or use mock location). +- **document_upload_e2e**: May require a fixture file; see `apps/mobile/apps/staff/maestro/compliance/` for scripts/fixtures. + +--- + +## CI/CD + +E2E runs in GitHub Actions: [.github/workflows/maestro-e2e.yml](../../.github/workflows/maestro-e2e.yml). It runs on: + +- `workflow_dispatch` (manual) +- Pull requests / pushes that touch `apps/mobile/**/maestro/**` or the workflow file + +Required secrets: `TEST_CLIENT_EMAIL`, `TEST_CLIENT_PASSWORD`, `TEST_CLIENT_COMPANY`, `TEST_STAFF_PHONE`, `TEST_STAFF_OTP`, `TEST_STAFF_SIGNUP_PHONE`. + +--- + +## References + +- [Maestro test run instructions](../research/maestro-test-run-instructions.md) — install, env vars, troubleshooting +- [Flutter testing tools](../research/flutter-testing-tools.md) — context on Maestro vs other tests +- Client Maestro README: `apps/mobile/apps/client/maestro/README.md` +- Staff Maestro README: `apps/mobile/apps/staff/maestro/README.md` diff --git a/makefiles/mobile.mk b/makefiles/mobile.mk index 6b3a9f5b..3a5f77cf 100644 --- a/makefiles/mobile.mk +++ b/makefiles/mobile.mk @@ -1,6 +1,6 @@ # --- Mobile App Development --- -.PHONY: mobile-install mobile-info mobile-analyze mobile-client-dev-android mobile-staff-dev-android mobile-client-build mobile-staff-build mobile-hot-reload mobile-hot-restart test-e2e test-e2e-setup test-e2e-client test-e2e-staff test-e2e-staff-auth test-e2e-staff-navigation test-e2e-staff-profile test-e2e-staff-profile-extended test-e2e-staff-shifts test-e2e-staff-compliance test-e2e-staff-home test-e2e-staff-sign-out test-e2e-client-auth test-e2e-client-navigation test-e2e-client-orders test-e2e-client-settings test-e2e-client-sign-out +.PHONY: mobile-install mobile-info mobile-analyze mobile-client-dev-android mobile-staff-dev-android mobile-client-build mobile-staff-build mobile-hot-reload mobile-hot-restart test-e2e test-e2e-setup test-e2e-client test-e2e-staff test-e2e-client-happy-path test-e2e-staff-happy-path test-e2e-staff-auth test-e2e-staff-navigation test-e2e-staff-profile test-e2e-staff-profile-extended test-e2e-staff-shifts test-e2e-staff-compliance test-e2e-staff-home test-e2e-staff-sign-out test-e2e-client-auth test-e2e-client-navigation test-e2e-client-orders test-e2e-client-settings test-e2e-client-sign-out MOBILE_DIR := apps/mobile @@ -141,6 +141,18 @@ test-e2e-client-sign-out: test-e2e-setup apps/mobile/apps/client/maestro/auth/sign_out.yaml \ -e TEST_CLIENT_EMAIL="$${TEST_CLIENT_EMAIL}" -e TEST_CLIENT_PASSWORD="$${TEST_CLIENT_PASSWORD}" +# Client happy path (auth + hubs + create order E2E + billing + reports + logout) — #572 +test-e2e-client-happy-path: test-e2e-setup + @echo "--> Running Client E2E happy path (hubs, order, billing, reports, logout)..." + @maestro test $(MAESTRO_SHARDS) apps/mobile/apps/client/maestro/auth/sign_in.yaml \ + apps/mobile/apps/client/maestro/hubs/create_hub_e2e.yaml \ + apps/mobile/apps/client/maestro/orders/create_order_one_time_e2e.yaml \ + apps/mobile/apps/client/maestro/billing/billing_overview.yaml \ + apps/mobile/apps/client/maestro/billing/invoice_approval_e2e.yaml \ + apps/mobile/apps/client/maestro/reports/reports_dashboard.yaml \ + apps/mobile/apps/client/maestro/settings/logout_flow.yaml \ + -e TEST_CLIENT_EMAIL="$${TEST_CLIENT_EMAIL}" -e TEST_CLIENT_PASSWORD="$${TEST_CLIENT_PASSWORD}" + # Client extended (auth + navigation + orders + settings) test-e2e-client-extended: test-e2e-setup @echo "--> Running Client E2E extended suite..." @@ -219,6 +231,18 @@ test-e2e-staff-home: test-e2e-setup apps/mobile/apps/staff/maestro/home/benefits.yaml \ -e TEST_STAFF_PHONE="$${TEST_STAFF_PHONE}" -e TEST_STAFF_OTP="$${TEST_STAFF_OTP}" +# Staff happy path (auth + clock in/out + availability + document upload + payments + sign out) — #572 +test-e2e-staff-happy-path: test-e2e-setup + @echo "--> Running Staff E2E happy path (clock in/out, availability, document upload, payments, sign out)..." + @maestro test $(MAESTRO_SHARDS) apps/mobile/apps/staff/maestro/auth/sign_in.yaml \ + apps/mobile/apps/staff/maestro/shifts/clock_in_e2e.yaml \ + apps/mobile/apps/staff/maestro/shifts/clock_out_e2e.yaml \ + apps/mobile/apps/staff/maestro/availability/set_availability_e2e.yaml \ + apps/mobile/apps/staff/maestro/compliance/document_upload_e2e.yaml \ + apps/mobile/apps/staff/maestro/payments/payments_view_e2e.yaml \ + apps/mobile/apps/staff/maestro/auth/sign_out.yaml \ + -e TEST_STAFF_PHONE="$${TEST_STAFF_PHONE}" -e TEST_STAFF_OTP="$${TEST_STAFF_OTP}" + # Staff extended (auth + navigation + profile + compliance + shifts + benefits) test-e2e-staff-extended: test-e2e-setup @echo "--> Running Staff E2E extended suite..."