maestro cases

This commit is contained in:
2026-03-02 19:18:35 +05:30
parent 07a0a29106
commit c0a69707e6
46 changed files with 1108 additions and 89 deletions

View File

@@ -10,6 +10,18 @@ maestro/
auth/
sign_in.yaml
sign_up.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
settings/
settings_page.yaml
```
## Credentials (env, never hardcoded)
@@ -19,6 +31,8 @@ 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

View File

@@ -0,0 +1,23 @@
# 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"

View File

@@ -0,0 +1,13 @@
# 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
- tapOn: "Settings"
- extendedWaitUntil:
visible: "Log Out"
timeout: 5000
- tapOn: "Log Out"
- assertVisible: "Are you sure you want to log out?"
- tapOn: "Log Out"
- assertVisible: "Sign In"

View File

@@ -0,0 +1,7 @@
# Client App — Billing tab navigation
# Run: maestro test auth/sign_in.yaml navigation/billing.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Billing"
- assertVisible: "Billing"

View File

@@ -0,0 +1,7 @@
# Client App — Coverage tab navigation
# Run: maestro test auth/sign_in.yaml navigation/coverage.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Coverage"
- assertVisible: "Coverage"

View File

@@ -0,0 +1,6 @@
# Client App — Home tab (default after sign-in)
# Run: maestro test auth/sign_in.yaml navigation/home.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- assertVisible: "Orders"

View File

@@ -0,0 +1,7 @@
# Client App — Orders tab navigation
# Run: maestro test auth/sign_in.yaml navigation/orders.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Orders"
- assertVisible: "Orders"

View File

@@ -0,0 +1,7 @@
# Client App — Reports tab navigation
# Run: maestro test auth/sign_in.yaml navigation/reports.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Reports"
- assertVisible: "Reports"

View File

@@ -0,0 +1,8 @@
# Client App — Completed tab: edit icon hidden for past/completed orders (#492)
# Run: maestro test auth/sign_in.yaml orders/completed_no_edit_icon.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Orders"
- tapOn: "Completed"
- assertVisible: "Completed"

View File

@@ -0,0 +1,9 @@
# Client App — Create order flow entry (tap Post, see order type selection)
# Run: maestro test auth/sign_in.yaml orders/create_order_entry.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Orders"
- assertVisible: "Post"
- tapOn: "Post"
- assertVisible: "Rapid"

View File

@@ -0,0 +1,9 @@
# Client App — View Orders page with filter tabs
# Run: maestro test auth/sign_in.yaml orders/view_orders.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Orders"
- assertVisible: "Up Next"
- assertVisible: "Active"
- assertVisible: "Completed"

View File

@@ -0,0 +1,14 @@
# Client App — Edit Profile page (Settings > Edit Profile)
# Run: maestro test auth/sign_in.yaml settings/edit_profile.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Settings"
- extendedWaitUntil:
visible: "Edit Profile"
timeout: 5000
- tapOn: "Edit Profile"
- extendedWaitUntil:
visible: "Save Changes"
timeout: 10000
- assertVisible: "Edit Profile"

View File

@@ -0,0 +1,7 @@
# Client App — Settings page (reached via settings icon on Home header)
# Run: maestro test auth/sign_in.yaml settings/settings_page.yaml -e TEST_CLIENT_EMAIL=... -e TEST_CLIENT_PASSWORD=...
appId: com.krowwithus.client
---
- launchApp
- tapOn: "Settings"
- assertVisible: "Edit Profile"

View File

@@ -75,7 +75,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")

View File

@@ -10,6 +10,25 @@ maestro/
auth/
sign_in.yaml
sign_up.yaml
navigation/
home.yaml
shifts.yaml
profile.yaml
payments.yaml
clock_in.yaml
profile/
personal_info.yaml
documents_list.yaml
certificates_list.yaml
compliance/
document_upload_banner.yaml # #550
certificate_upload_banner.yaml # #551
attire_upload_banner.yaml # #552
shifts/
find_shifts.yaml
incomplete_profile_banner.yaml # #549 (requires incomplete-profile user)
home/
benefits.yaml # #524
```
## Prerequisites
@@ -24,6 +43,8 @@ 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

View File

@@ -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

View File

@@ -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"

View 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"

View File

@@ -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

View File

@@ -0,0 +1,19 @@
# 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
- assertVisible: "Profile"
- tapOn: "Profile"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "Attire"
- assertVisible: "Attire"
- tapOn: "Attire"
- tapOn: "Shirt"
- assertVisible: "Only JPEG, JPG, and PNG"

View File

@@ -0,0 +1,19 @@
# 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
- assertVisible: "Profile"
- tapOn: "Profile"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "Certificates"
- assertVisible: "Certificates"
- tapOn: "Certificates"
- tapOn: "Add Another Certificate"
- assertVisible: "Only PDF files are accepted"

View File

@@ -0,0 +1,24 @@
# 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
- assertVisible: "Profile"
- tapOn: "Profile"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "Documents"
- assertVisible: "Documents"
- tapOn: "Documents"
- extendedWaitUntil:
visible:
id: "staff_document_upload"
timeout: 20000
- tapOn:
id: "staff_document_upload"
- assertVisible: "Only PDF files are accepted"

View File

@@ -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"

View File

@@ -0,0 +1,8 @@
# 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
- assertVisible: "Clock In"
- tapOn: "Clock In"
- assertVisible: "Clock In"

View File

@@ -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: "Find Shifts"

View File

@@ -0,0 +1,8 @@
# 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
- assertVisible: "Payments"
- tapOn: "Payments"
- assertVisible: "Payments"

View File

@@ -0,0 +1,8 @@
# 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
- assertVisible: "Profile"
- tapOn: "Profile"
- assertVisible: "Personal Info"

View File

@@ -0,0 +1,8 @@
# 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
- assertVisible: "Shifts"
- tapOn: "Shifts"
- assertVisible: "Find Shifts"

View File

@@ -0,0 +1,18 @@
# 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: "Bank Account"
visibilityPercentage: 50
timeout: 10000
- tapOn: "Bank Account"
- extendedWaitUntil:
visible: "Bank Account"
timeout: 10000
- assertVisible: "Bank Account"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -0,0 +1,18 @@
# Staff App — FAQs section (Profile > Support > FAQs)
# Run: maestro test auth/sign_in.yaml profile/faqs.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
appId: com.krowwithus.staff
---
- launchApp
- assertVisible: "Profile"
- tapOn: "Profile"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "FAQs"
visibilityPercentage: 50
timeout: 10000
- tapOn: "FAQs"
- extendedWaitUntil:
visible: "FAQs"
timeout: 10000
- assertVisible: "FAQs"

View File

@@ -0,0 +1,10 @@
# Staff App — Personal Info page
# Run: maestro test auth/sign_in.yaml profile/personal_info.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
appId: com.krowwithus.staff
---
- launchApp
- assertVisible: "Profile"
- tapOn: "Profile"
- assertVisible: "Personal Info"
- tapOn: "Personal Info"
- assertVisible: "Full Name"

View File

@@ -0,0 +1,18 @@
# Staff App — Privacy & Security section (Profile > Support > Privacy & Security)
# Run: maestro test auth/sign_in.yaml profile/privacy_security.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
appId: com.krowwithus.staff
---
- launchApp
- assertVisible: "Profile"
- tapOn: "Profile"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "Privacy & Security"
visibilityPercentage: 50
timeout: 10000
- tapOn: "Privacy & Security"
- extendedWaitUntil:
visible: "Privacy & Security"
timeout: 10000
- assertVisible: "Privacy Policy"

View File

@@ -0,0 +1,18 @@
# Staff App — Timecard section (Profile > Finance > Timecard)
# Run: maestro test auth/sign_in.yaml profile/time_card.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
appId: com.krowwithus.staff
---
- launchApp
- assertVisible: "Profile"
- tapOn: "Profile"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "Timecard"
visibilityPercentage: 50
timeout: 10000
- tapOn: "Timecard"
- extendedWaitUntil:
visible: "Hours Worked"
timeout: 10000
- assertVisible: "Timecard"

View File

@@ -0,0 +1,10 @@
# Staff App — Find Shifts tab
# Run: maestro test auth/sign_in.yaml shifts/find_shifts.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
appId: com.krowwithus.staff
---
- launchApp
- assertVisible: "Shifts"
- tapOn: "Shifts"
- assertVisible: "Find Shifts"
- tapOn: "Find Shifts"
- assertVisible: "Find Shifts"

View File

@@ -0,0 +1,12 @@
# Staff App — Incomplete profile banner in Find Shifts (#549)
# Requires staff user with INCOMPLETE profile
# Run: maestro test auth/sign_in.yaml shifts/incomplete_profile_banner.yaml -e TEST_STAFF_PHONE=... -e TEST_STAFF_OTP=...
appId: com.krowwithus.staff
---
- launchApp
- assertVisible: "Shifts"
- tapOn: "Shifts"
- assertVisible: "Find Shifts"
- tapOn: "Find Shifts"
- assertVisible: "Your account isn't complete yet."
- assertVisible: "Complete your account now"

View File

@@ -36,8 +36,22 @@ class _OtpInputFieldState extends State<OtpInputField> {
);
final List<FocusNode> _focusNodes = List.generate(6, (_) => FocusNode());
/// Hidden field for E2E: Maestro inputText sends full OTP in one call;
/// the 6 visible boxes have maxLength:1 and would truncate.
late final TextEditingController _hiddenController;
late final FocusNode _hiddenFocusNode;
@override
void initState() {
super.initState();
_hiddenController = TextEditingController();
_hiddenFocusNode = FocusNode();
}
@override
void dispose() {
_hiddenController.dispose();
_hiddenFocusNode.dispose();
for (final TextEditingController controller in _controllers) {
controller.dispose();
}
@@ -47,6 +61,22 @@ class _OtpInputFieldState extends State<OtpInputField> {
super.dispose();
}
/// Distributes full OTP from hidden field to the 6 visible boxes and notifies Bloc.
void _syncFromHidden(BuildContext context, String value) {
final String raw = value.replaceAll(RegExp(r'\D'), '');
final String digits = raw.length > 6 ? raw.substring(0, 6) : raw;
for (int i = 0; i < 6; i++) {
final String digit = i < digits.length ? digits[i] : '';
if (_controllers[i].text != digit) {
_controllers[i].text = digit;
}
}
BlocProvider.of<AuthBloc>(context).add(AuthOtpUpdated(digits));
if (digits.length == 6) {
widget.onCompleted(digits);
}
}
/// Helper getter to compute the current OTP code from all controllers.
String get _otpCode => _controllers.map((TextEditingController c) => c.text).join();
@@ -70,57 +100,79 @@ class _OtpInputFieldState extends State<OtpInputField> {
@override
Widget build(BuildContext context) {
const double boxWidth = 45;
const double boxHeight = 56;
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(6, (int index) {
final TextField field = TextField(
controller: _controllers[index],
focusNode: _focusNodes[index],
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
textAlign: TextAlign.center,
maxLength: 1,
style: UiTypography.headline3m,
decoration: InputDecoration(
counterText: '',
border: OutlineInputBorder(
borderSide: BorderSide(
color: widget.error.isNotEmpty
? UiColors.textError
: (_controllers[index].text.isNotEmpty
? UiColors.primary
: UiColors.border),
width: 2,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: BorderSide(
color: widget.error.isNotEmpty
? UiColors.textError
: (_controllers[index].text.isNotEmpty
? UiColors.primary
: UiColors.border),
width: 2,
SizedBox(
width: 300,
height: boxHeight,
child: Stack(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(6, (int index) {
final TextField field = TextField(
controller: _controllers[index],
focusNode: _focusNodes[index],
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
textAlign: TextAlign.center,
maxLength: 1,
style: UiTypography.headline3m,
decoration: InputDecoration(
counterText: '',
border: OutlineInputBorder(
borderSide: BorderSide(
color: widget.error.isNotEmpty
? UiColors.textError
: (_controllers[index].text.isNotEmpty
? UiColors.primary
: UiColors.border),
width: 2,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: BorderSide(
color: widget.error.isNotEmpty
? UiColors.textError
: (_controllers[index].text.isNotEmpty
? UiColors.primary
: UiColors.border),
width: 2,
),
),
),
onChanged: (String value) =>
_onChanged(context: context, index: index, value: value),
);
return SizedBox(width: boxWidth, height: boxHeight, child: field);
}),
),
Positioned.fill(
child: Semantics(
identifier: 'staff_otp_input',
container: false,
child: Opacity(
opacity: 0.01,
child: TextField(
controller: _hiddenController,
focusNode: _hiddenFocusNode,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(6),
],
maxLength: 6,
onChanged: (String value) =>
_syncFromHidden(context, value),
),
),
),
onChanged: (String value) =>
_onChanged(context: context, index: index, value: value),
);
return SizedBox(
width: 45,
height: 56,
child: index == 0
? Semantics(
identifier: 'staff_otp_input',
child: field,
)
: field,
);
}),
),
],
),
),
if (widget.error.isNotEmpty)
Padding(

View File

@@ -84,6 +84,7 @@ class _PhoneInputFormFieldState extends State<PhoneInputFormField> {
Expanded(
child: Semantics(
identifier: 'staff_phone_input',
container: false, // Merge with TextField so tap/input reach the actual field
child: TextField(
controller: _controller,
keyboardType: TextInputType.phone,

View File

@@ -152,9 +152,14 @@ class DocumentCard extends StatelessWidget {
Widget _buildActionButton(DocumentStatus status) {
final bool isVerified = status == DocumentStatus.verified;
return InkWell(
onTap: onTap,
borderRadius: UiConstants.radiusSm,
return Semantics(
identifier: 'staff_document_upload',
label: isVerified
? t.staff_documents.card.view
: t.staff_documents.card.upload,
child: InkWell(
onTap: onTap,
borderRadius: UiConstants.radiusSm,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
@@ -174,6 +179,7 @@ class DocumentCard extends StatelessWidget {
],
),
),
),
);
}
}