setLocale('fr'); config()->set('app.mobile_scheme', 'dailymeal'); Http::fake([ 'https://exp.host/*' => Http::response([ 'data' => [ ['status' => 'ok', 'id' => 'ticket-one'], ], ]), ]); $eligibleUser = User::factory()->create(['locale' => 'fr']); $eligibleUser->deviceTokens()->create([ 'expo_push_token' => 'ExponentPushToken[eligible]', 'platform' => 'ios', ]); $recentPoster = User::factory()->create(['locale' => 'fr']); $recentPoster->deviceTokens()->create([ 'expo_push_token' => 'ExponentPushToken[recent-poster]', 'platform' => 'ios', ]); MealPosts::factory() ->for($recentPoster, 'user') ->create([ 'created_at' => now()->subHours(2), 'updated_at' => now()->subHours(2), ]); User::factory()->create(['locale' => 'fr']); $suspendedUser = User::factory()->create([ 'locale' => 'fr', 'suspended_at' => now(), ]); $suspendedUser->deviceTokens()->create([ 'expo_push_token' => 'ExponentPushToken[suspended]', 'platform' => 'ios', ]); $this->artisan('notifications:send-engagement-reminders') ->expectsOutput('1 engagement reminder notification(s) sent.') ->assertSuccessful(); Http::assertSentCount(1); Http::assertSent(function (Request $request): bool { $payload = $request->data()[0]; return $payload['to'] === 'ExponentPushToken[eligible]' && $payload['data']['type'] === 'engagement_reminder' && in_array($payload['data']['url'], [ 'dailymeal://create', 'dailymeal://profile/workouts?create=1', ], true); }); }); it('uses the hard-coded localized reminder copy', function () { config()->set('app.mobile_scheme', 'dailymeal'); foreach (['fr', 'en'] as $locale) { app()->setLocale($locale); foreach (EngagementReminderNotification::messageKeys() as $messageKey) { $expectedPath = $messageKey === 'workout_checkin' ? 'profile/workouts?create=1' : 'create'; $payload = (new EngagementReminderNotification($messageKey)) ->toExpoPush(User::factory()->make(['locale' => $locale])); expect($payload['title'])->toBeString()->not->toBeEmpty() ->and($payload['body'])->toBeString()->not->toBeEmpty() ->and($payload['data']['message_key'])->toBe($messageKey) ->and($payload['data']['url'])->toBe("dailymeal://{$expectedPath}"); } } }); it('builds mobile deep links from the configured scheme', function () { config()->set('app.mobile_scheme', 'dailymeal://'); expect(MobileDeepLink::to('/create'))->toBe('dailymeal://create') ->and(MobileDeepLink::to('strava/callback', ['code' => 'abc 123']))->toBe('dailymeal://strava/callback?code=abc%20123'); config()->set('app.mobile_scheme', ''); expect(MobileDeepLink::to('create'))->toBe('bowly://create'); });