/** * View Data Layer Tests - Onboarding Functionality * * This test file covers the view data layer for onboarding functionality. * * The view data layer is responsible for: * - DTO → UI model mapping * - Formatting, sorting, and grouping * - Derived fields and defaults * - UI-specific semantics * * This layer isolates the UI from API churn by providing a stable interface * between the API layer and the presentation layer. * * Test coverage includes: * - Onboarding page data transformation and validation * - Onboarding wizard view models and field formatting * - Authentication and authorization checks for onboarding flow * - Redirect logic based on onboarding status (already onboarded, not authenticated) * - Onboarding-specific formatting and validation * - Derived fields for onboarding UI components (progress, completion status, etc.) * - Default values and fallbacks for onboarding views * - Onboarding step data mapping and state management * - Error handling and fallback UI states for onboarding flow */ import { OnboardingViewDataBuilder } from '@/lib/builders/view-data/OnboardingViewDataBuilder'; import { OnboardingPageViewDataBuilder } from '@/lib/builders/view-data/OnboardingPageViewDataBuilder'; import { CompleteOnboardingViewDataBuilder } from '@/lib/builders/view-data/CompleteOnboardingViewDataBuilder'; import { Result } from '@/lib/contracts/Result'; import { PresentationError } from '@/lib/contracts/page-queries/PresentationError'; import type { CompleteOnboardingOutputDTO } from '@/lib/types/generated/CompleteOnboardingOutputDTO'; describe('OnboardingViewDataBuilder', () => { describe('happy paths', () => { it('should transform successful onboarding check to ViewData correctly', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.ok({ isAlreadyOnboarded: false, }); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isOk()).toBe(true); expect(result.unwrap()).toEqual({ isAlreadyOnboarded: false, }); }); it('should handle already onboarded user correctly', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.ok({ isAlreadyOnboarded: true, }); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isOk()).toBe(true); expect(result.unwrap()).toEqual({ isAlreadyOnboarded: true, }); }); it('should handle missing isAlreadyOnboarded field with default false', () => { const apiDto: Result<{ isAlreadyOnboarded?: boolean }, PresentationError> = Result.ok({}); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isOk()).toBe(true); expect(result.unwrap()).toEqual({ isAlreadyOnboarded: false, }); }); }); describe('error handling', () => { it('should propagate unauthorized error', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.err('unauthorized'); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isErr()).toBe(true); expect(result.getError()).toBe('unauthorized'); }); it('should propagate notFound error', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.err('notFound'); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isErr()).toBe(true); expect(result.getError()).toBe('notFound'); }); it('should propagate serverError', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.err('serverError'); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isErr()).toBe(true); expect(result.getError()).toBe('serverError'); }); it('should propagate networkError', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.err('networkError'); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isErr()).toBe(true); expect(result.getError()).toBe('networkError'); }); it('should propagate validationError', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.err('validationError'); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isErr()).toBe(true); expect(result.getError()).toBe('validationError'); }); it('should propagate unknown error', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.err('unknown'); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isErr()).toBe(true); expect(result.getError()).toBe('unknown'); }); }); describe('data transformation', () => { it('should preserve all DTO fields in the output', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.ok({ isAlreadyOnboarded: false, }); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.unwrap().isAlreadyOnboarded).toBe(false); }); it('should not modify the input DTO', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean }, PresentationError> = Result.ok({ isAlreadyOnboarded: false, }); const originalDto = { ...apiDto.unwrap() }; OnboardingViewDataBuilder.build(apiDto); expect(apiDto.unwrap()).toEqual(originalDto); }); }); describe('edge cases', () => { it('should handle null isAlreadyOnboarded as false', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean | null }, PresentationError> = Result.ok({ isAlreadyOnboarded: null, }); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isOk()).toBe(true); expect(result.unwrap()).toEqual({ isAlreadyOnboarded: false, }); }); it('should handle undefined isAlreadyOnboarded as false', () => { const apiDto: Result<{ isAlreadyOnboarded: boolean | undefined }, PresentationError> = Result.ok({ isAlreadyOnboarded: undefined, }); const result = OnboardingViewDataBuilder.build(apiDto); expect(result.isOk()).toBe(true); expect(result.unwrap()).toEqual({ isAlreadyOnboarded: false, }); }); }); }); describe('OnboardingPageViewDataBuilder', () => { describe('happy paths', () => { it('should transform driver data to ViewData correctly when driver exists', () => { const apiDto = { id: 'driver-123', name: 'Test Driver' }; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: true, }); }); it('should handle empty object as driver data', () => { const apiDto = {}; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: true, }); }); it('should handle null driver data', () => { const apiDto = null; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: false, }); }); it('should handle undefined driver data', () => { const apiDto = undefined; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: false, }); }); }); describe('data transformation', () => { it('should preserve all driver data fields in the output', () => { const apiDto = { id: 'driver-123', name: 'Test Driver', email: 'test@example.com', createdAt: '2024-01-01T00:00:00.000Z', }; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result.isAlreadyOnboarded).toBe(true); }); it('should not modify the input driver data', () => { const apiDto = { id: 'driver-123', name: 'Test Driver' }; const originalDto = { ...apiDto }; OnboardingPageViewDataBuilder.build(apiDto); expect(apiDto).toEqual(originalDto); }); }); describe('edge cases', () => { it('should handle empty string as driver data', () => { const apiDto = ''; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: false, }); }); it('should handle zero as driver data', () => { const apiDto = 0; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: false, }); }); it('should handle false as driver data', () => { const apiDto = false; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: false, }); }); it('should handle array as driver data', () => { const apiDto = ['driver-123']; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: true, }); }); it('should handle function as driver data', () => { const apiDto = () => {}; const result = OnboardingPageViewDataBuilder.build(apiDto); expect(result).toEqual({ isAlreadyOnboarded: true, }); }); }); }); describe('CompleteOnboardingViewDataBuilder', () => { describe('happy paths', () => { it('should transform successful onboarding completion DTO to ViewData correctly', () => { const apiDto: CompleteOnboardingOutputDTO = { success: true, driverId: 'driver-123', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result).toEqual({ success: true, driverId: 'driver-123', errorMessage: undefined, }); }); it('should handle onboarding completion with error message', () => { const apiDto: CompleteOnboardingOutputDTO = { success: false, driverId: undefined, errorMessage: 'Failed to complete onboarding', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result).toEqual({ success: false, driverId: undefined, errorMessage: 'Failed to complete onboarding', }); }); it('should handle onboarding completion with only success field', () => { const apiDto: CompleteOnboardingOutputDTO = { success: true, }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result).toEqual({ success: true, driverId: undefined, errorMessage: undefined, }); }); }); describe('data transformation', () => { it('should preserve all DTO fields in the output', () => { const apiDto: CompleteOnboardingOutputDTO = { success: true, driverId: 'driver-123', errorMessage: undefined, }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.success).toBe(apiDto.success); expect(result.driverId).toBe(apiDto.driverId); expect(result.errorMessage).toBe(apiDto.errorMessage); }); it('should not modify the input DTO', () => { const apiDto: CompleteOnboardingOutputDTO = { success: true, driverId: 'driver-123', errorMessage: undefined, }; const originalDto = { ...apiDto }; CompleteOnboardingViewDataBuilder.build(apiDto); expect(apiDto).toEqual(originalDto); }); }); describe('edge cases', () => { it('should handle false success value', () => { const apiDto: CompleteOnboardingOutputDTO = { success: false, driverId: undefined, errorMessage: 'Error occurred', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.success).toBe(false); expect(result.driverId).toBeUndefined(); expect(result.errorMessage).toBe('Error occurred'); }); it('should handle empty string error message', () => { const apiDto: CompleteOnboardingOutputDTO = { success: false, driverId: undefined, errorMessage: '', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.success).toBe(false); expect(result.errorMessage).toBe(''); }); it('should handle very long driverId', () => { const longDriverId = 'driver-' + 'a'.repeat(1000); const apiDto: CompleteOnboardingOutputDTO = { success: true, driverId: longDriverId, }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.driverId).toBe(longDriverId); }); it('should handle special characters in error message', () => { const apiDto: CompleteOnboardingOutputDTO = { success: false, driverId: undefined, errorMessage: 'Error: "Failed to create driver" (code: 500)', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.errorMessage).toBe('Error: "Failed to create driver" (code: 500)'); }); }); describe('derived fields calculation', () => { it('should calculate isSuccessful derived field correctly', () => { const apiDto: CompleteOnboardingOutputDTO = { success: true, driverId: 'driver-123', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); // Note: The builder doesn't add derived fields, but we can verify the structure expect(result.success).toBe(true); expect(result.driverId).toBe('driver-123'); }); it('should handle success with no driverId', () => { const apiDto: CompleteOnboardingOutputDTO = { success: true, driverId: undefined, }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.success).toBe(true); expect(result.driverId).toBeUndefined(); }); it('should handle failure with driverId', () => { const apiDto: CompleteOnboardingOutputDTO = { success: false, driverId: 'driver-123', errorMessage: 'Partial failure', }; const result = CompleteOnboardingViewDataBuilder.build(apiDto); expect(result.success).toBe(false); expect(result.driverId).toBe('driver-123'); expect(result.errorMessage).toBe('Partial failure'); }); }); });