Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions scripts/seed_verified_enterprises.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

const fs = require('fs');
const path = require('path');
const admin = require('firebase-admin');
const { createRequire } = require('module');
const requireFromCwd = createRequire(path.join(process.cwd(), 'package.json'));
const admin = requireFromCwd('firebase-admin');

function fail(message) {
console.error(message);
Expand Down Expand Up @@ -38,7 +40,7 @@ function initAdminFromEnv() {
async function main() {
const inputPath =
process.argv[2] ||
path.resolve(process.cwd(), 'docs/ops/verified_enterprises.seed.json');
path.resolve(__dirname, '../docs/ops/verified_enterprises.seed.json');

if (!fs.existsSync(inputPath)) {
fail(`Seed file not found: ${inputPath}`);
Expand Down
82 changes: 82 additions & 0 deletions test/core/app_strings_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,74 @@ void main() {
expect(strings.highImpactEnterprise, 'High-impact donor');
expect(strings.flexiblePickupEnterprise, contains('Flexible'));
expect(strings.stableShelfLifeEnterprise, contains('Stable'));
expect(strings.appTitle, isNotEmpty);
expect(strings.navMap, 'Map');
expect(strings.navPost, 'Post');
expect(strings.listingsTitle, isNotEmpty);
expect(strings.refresh, 'Refresh');
expect(strings.privateDonor, isNotEmpty);
expect(strings.noActiveListings, contains('No active listings'));
expect(strings.localDemoModeNotice, contains('local demo mode'));
expect(strings.platformDisclaimer, contains('matching service'));
expect(strings.mapTitle, 'Venue Map');
expect(strings.activeCount(3), '3 active');
expect(strings.listingDetailTitle, isNotEmpty);
expect(strings.myReservationsTitle, isNotEmpty);
expect(strings.myReservationsCta, isNotEmpty);
expect(strings.noMyReservations, isNotEmpty);
expect(strings.cancelReservation, isNotEmpty);
expect(strings.reservationCancelled, isNotEmpty);
expect(strings.listingNotFound, isNotEmpty);
expect(strings.reserveOneItem, isNotEmpty);
expect(strings.beforeReserving, isNotEmpty);
expect(strings.reserveDisclaimer, isNotEmpty);
expect(strings.publicPickupOnlyNotice, isNotEmpty);
expect(strings.reserveDisclaimerAccept, isNotEmpty);
expect(strings.cancel, 'Cancel');
expect(strings.reserve, 'Reserve');
expect(strings.enterprisePostTitle, isNotEmpty);
expect(strings.enterpriseEditTitle, isNotEmpty);
expect(strings.reservationSection, isNotEmpty);
expect(strings.noReservationsYet, isNotEmpty);
expect(strings.reservationConfirmed, isNotEmpty);
expect(strings.reservationNotFound, isNotEmpty);
expect(strings.offlineIdentityMode, isNotEmpty);
expect(strings.reportSafetyConcern, isNotEmpty);
expect(strings.riskReasonPrivateLocation, isNotEmpty);
expect(strings.riskReasonNoShow, isNotEmpty);
expect(strings.riskReasonUnsafeCondition, isNotEmpty);
expect(strings.riskReasonOther, isNotEmpty);
expect(strings.abuseReported, isNotEmpty);
expect(strings.verifiedEnterprise, isNotEmpty);
expect(strings.trustedQualityEnterprise, isNotEmpty);
expect(strings.pendingConfirm, isNotEmpty);
expect(strings.confirmedFilter, isNotEmpty);
expect(strings.showPickupCodeHelp, isNotEmpty);
expect(strings.retry, isNotEmpty);
expect(strings.genericLoadErrorTitle, isNotEmpty);
expect(strings.genericLoadErrorBody, isNotEmpty);
expect(strings.statusLabel(AppStatusLabel.active), 'Active');
expect(strings.statusLabel(AppStatusLabel.reserved), 'Reserved');
expect(strings.statusLabel(AppStatusLabel.expired), 'Expired');
expect(strings.statusLabel(AppStatusLabel.cancelled), 'Cancelled');
expect(strings.enterpriseBadgeLabel('verified'), strings.verifiedEnterprise);
expect(
strings.enterpriseBadgeLabel('quality_trusted'),
strings.trustedQualityEnterprise,
);
expect(
strings.enterpriseBadgeLabel('high_impact'),
strings.highImpactEnterprise,
);
expect(
strings.enterpriseBadgeLabel('flexible_pickup'),
strings.flexiblePickupEnterprise,
);
expect(
strings.enterpriseBadgeLabel('stable_shelf_life'),
strings.stableShelfLifeEnterprise,
);
expect(strings.enterpriseBadgeLabel('unknown_badge'), isNull);
});

testWidgets('app strings returns zh-TW labels', (tester) async {
Expand Down Expand Up @@ -68,5 +136,19 @@ void main() {
expect(find.text('隱私與常見問題'), findsOneWidget);
expect(find.text('請選擇回報原因'), findsOneWidget);
expect(find.text('高量捐贈企業'), findsOneWidget);
await tester.pumpWidget(
AppScope(
dependencies: dependencies,
child: MaterialApp(
home: Builder(
builder: (context) {
final strings = AppStrings.of(context);
return Text(strings.statusLabel(AppStatusLabel.cancelled));
},
),
),
),
);
expect(find.text('已取消'), findsOneWidget);
});
}
138 changes: 138 additions & 0 deletions test/presentation/browse/browse_pages_additional_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Listing _forcedListing(
required DateTime expiresAt,
String? displayNameOptional,
bool enterpriseVerified = false,
List<String> enterpriseBadges = const <String>[],
}) {
return Listing(
id: id,
Expand All @@ -167,6 +168,7 @@ Listing _forcedListing(
expiresAt: expiresAt,
displayNameOptional: displayNameOptional,
enterpriseVerified: enterpriseVerified,
enterpriseBadges: enterpriseBadges,
visibility: ListingVisibility.minimal,
status: status,
editTokenHash: 'hash',
Expand Down Expand Up @@ -224,6 +226,11 @@ Future<void> _pumpConfirmation(
required String reservationId,
RecipientIdentityService? identityService,
}) async {
tester.view.devicePixelRatio = 1.0;
tester.view.physicalSize = const Size(1200, 2400);
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);

final dependencies = await buildTestDependencies(
repository: repo,
identityService: identityService,
Expand Down Expand Up @@ -588,4 +595,135 @@ void main() {
},
);

testWidgets(
'reservation confirmation opens and cancels risk reason dialog',
(tester) async {
final now = DateTime.now();
final repo = _InstrumentedRepository()
..forcedListings['reason-dialog'] = _forcedListing(
now,
id: 'reason-dialog',
status: ListingStatus.active,
quantityRemaining: 1,
expiresAt: now.add(const Duration(hours: 2)),
)
..forcedReservations['reason-dialog-r'] = _forcedReservation(
now,
id: 'reason-dialog-r',
listingId: 'reason-dialog',
status: ReservationStatus.reserved,
);

await _pumpConfirmation(
tester,
repo,
listingId: 'reason-dialog',
reservationId: 'reason-dialog-r',
);

final reportButton = find.byIcon(Icons.report_gmailerrorred_outlined);
await tester.scrollUntilVisible(
reportButton,
200,
scrollable: find.byType(Scrollable).first,
);
await tester.tap(reportButton);
await tester.pumpAndSettle();

expect(find.text('Select a reason'), findsOneWidget);
await tester.tap(find.widgetWithText(TextButton, 'Cancel'));
await tester.pumpAndSettle();
expect(find.text('Select a reason'), findsNothing);
},
);

testWidgets(
'reservation confirmation reports selected risk reason and shows badges',
(tester) async {
final now = DateTime.now();
final repo = _InstrumentedRepository()
..forcedListings['reason-submit'] = _forcedListing(
now,
id: 'reason-submit',
status: ListingStatus.active,
quantityRemaining: 1,
expiresAt: now.add(const Duration(hours: 2)),
enterpriseBadges: const <String>['verified', 'high_impact'],
)
..forcedReservations['reason-submit-r'] = _forcedReservation(
now,
id: 'reason-submit-r',
listingId: 'reason-submit',
status: ReservationStatus.reserved,
);

await _pumpConfirmation(
tester,
repo,
listingId: 'reason-submit',
reservationId: 'reason-submit-r',
);

expect(find.text('Verified enterprise'), findsOneWidget);
expect(find.text('High-impact donor'), findsOneWidget);

final reportButton = find.byIcon(Icons.report_gmailerrorred_outlined);
await tester.scrollUntilVisible(
reportButton,
200,
scrollable: find.byType(Scrollable).first,
);
await tester.tap(reportButton);
await tester.pumpAndSettle();

await tester.tap(find.text('Suspicious behavior / harassment'));
await tester.pumpAndSettle();

expect(repo.lastAbuseReason, 'recipient_report_suspicious_behavior');
expect(find.text('Safety report submitted.'), findsOneWidget);
},
);

testWidgets(
'reservation confirmation shows error when abuse report fails',
(tester) async {
final now = DateTime.now();
final repo = _InstrumentedRepository()
..throwOnAbuseSignal = true
..forcedListings['reason-fail'] = _forcedListing(
now,
id: 'reason-fail',
status: ListingStatus.active,
quantityRemaining: 1,
expiresAt: now.add(const Duration(hours: 2)),
)
..forcedReservations['reason-fail-r'] = _forcedReservation(
now,
id: 'reason-fail-r',
listingId: 'reason-fail',
status: ReservationStatus.reserved,
);

await _pumpConfirmation(
tester,
repo,
listingId: 'reason-fail',
reservationId: 'reason-fail-r',
);

final reportButton = find.byIcon(Icons.report_gmailerrorred_outlined);
await tester.scrollUntilVisible(
reportButton,
200,
scrollable: find.byType(Scrollable).first,
);
await tester.tap(reportButton);
await tester.pumpAndSettle();
await tester.tap(find.text('Other risk'));
await tester.pumpAndSettle();

expect(find.text('Abuse report failed for test.'), findsOneWidget);
},
);

}
Loading