496 lines
17 KiB
TypeScript
496 lines
17 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { ForgotPasswordViewModelBuilder } from './ForgotPasswordViewModelBuilder';
|
|
import type { ForgotPasswordViewData } from '@/lib/builders/view-data/types/ForgotPasswordViewData';
|
|
|
|
describe('ForgotPasswordViewModelBuilder', () => {
|
|
describe('happy paths', () => {
|
|
it('should transform ForgotPasswordViewData to ForgotPasswordViewModel correctly', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.returnTo).toBe('/dashboard');
|
|
expect(result.formState).toBeDefined();
|
|
expect(result.formState.fields).toBeDefined();
|
|
expect(result.formState.fields.email).toBeDefined();
|
|
expect(result.formState.fields.email.value).toBe('');
|
|
expect(result.formState.fields.email.error).toBeUndefined();
|
|
expect(result.formState.fields.email.touched).toBe(false);
|
|
expect(result.formState.fields.email.validating).toBe(false);
|
|
expect(result.formState.isValid).toBe(true);
|
|
expect(result.formState.isSubmitting).toBe(false);
|
|
expect(result.formState.submitError).toBeUndefined();
|
|
expect(result.formState.submitCount).toBe(0);
|
|
expect(result.hasInsufficientPermissions).toBe(false);
|
|
expect(result.error).toBeNull();
|
|
expect(result.successMessage).toBeNull();
|
|
expect(result.isProcessing).toBe(false);
|
|
});
|
|
|
|
it('should handle different returnTo paths', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/login',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/login');
|
|
});
|
|
|
|
it('should handle empty returnTo', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('data transformation', () => {
|
|
it('should preserve all viewData fields in the output', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe(forgotPasswordViewData.returnTo);
|
|
});
|
|
|
|
it('should not modify the input viewData', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard',
|
|
};
|
|
|
|
const originalViewData = { ...forgotPasswordViewData };
|
|
ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(forgotPasswordViewData).toEqual(originalViewData);
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle null returnTo', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: null,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBeNull();
|
|
});
|
|
|
|
it('should handle undefined returnTo', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: undefined,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBeUndefined();
|
|
});
|
|
|
|
it('should handle complex returnTo paths', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/leagues/league-123/settings',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/leagues/league-123/settings');
|
|
});
|
|
|
|
it('should handle returnTo with query parameters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?tab=settings',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?tab=settings');
|
|
});
|
|
|
|
it('should handle returnTo with hash', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard#section',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard#section');
|
|
});
|
|
|
|
it('should handle returnTo with special characters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/leagues/league-123/settings?tab=general#section',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/leagues/league-123/settings?tab=general#section');
|
|
});
|
|
|
|
it('should handle very long returnTo path', () => {
|
|
const longPath = '/dashboard/leagues/league-123/settings/section/subsection/item/' + 'a'.repeat(100);
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: longPath,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe(longPath);
|
|
});
|
|
|
|
it('should handle returnTo with encoded characters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/leagues/league-123/settings?name=John%20Doe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/leagues/league-123/settings?name=John%20Doe');
|
|
});
|
|
|
|
it('should handle returnTo with multiple query parameters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?tab=settings&filter=active&sort=name',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?tab=settings&filter=active&sort=name');
|
|
});
|
|
|
|
it('should handle returnTo with fragment identifier', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard#section-1',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard#section-1');
|
|
});
|
|
|
|
it('should handle returnTo with multiple fragments', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard#section-1#subsection-2',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard#section-1#subsection-2');
|
|
});
|
|
|
|
it('should handle returnTo with trailing slash', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/');
|
|
});
|
|
|
|
it('should handle returnTo with leading slash', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: 'dashboard',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('dashboard');
|
|
});
|
|
|
|
it('should handle returnTo with dots', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/../login',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/../login');
|
|
});
|
|
|
|
it('should handle returnTo with double dots', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/../../login',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/../../login');
|
|
});
|
|
|
|
it('should handle returnTo with percent encoding', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/leagues/league-123/settings?name=John%20Doe&email=test%40example.com',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/leagues/league-123/settings?name=John%20Doe&email=test%40example.com');
|
|
});
|
|
|
|
it('should handle returnTo with plus signs', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?query=hello+world',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?query=hello+world');
|
|
});
|
|
|
|
it('should handle returnTo with ampersands', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?tab=settings&filter=active',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?tab=settings&filter=active');
|
|
});
|
|
|
|
it('should handle returnTo with equals signs', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?tab=settings=value',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?tab=settings=value');
|
|
});
|
|
|
|
it('should handle returnTo with multiple equals signs', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?tab=settings=value&filter=active=true',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?tab=settings=value&filter=active=true');
|
|
});
|
|
|
|
it('should handle returnTo with semicolons', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard;jsessionid=123',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard;jsessionid=123');
|
|
});
|
|
|
|
it('should handle returnTo with colons', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard:section',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard:section');
|
|
});
|
|
|
|
it('should handle returnTo with commas', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?filter=a,b,c',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?filter=a,b,c');
|
|
});
|
|
|
|
it('should handle returnTo with spaces', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John Doe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John Doe');
|
|
});
|
|
|
|
it('should handle returnTo with tabs', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\tDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\tDoe');
|
|
});
|
|
|
|
it('should handle returnTo with newlines', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\nDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\nDoe');
|
|
});
|
|
|
|
it('should handle returnTo with carriage returns', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\rDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\rDoe');
|
|
});
|
|
|
|
it('should handle returnTo with form feeds', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\fDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\fDoe');
|
|
});
|
|
|
|
it('should handle returnTo with vertical tabs', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\vDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\vDoe');
|
|
});
|
|
|
|
it('should handle returnTo with backspaces', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\bDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\bDoe');
|
|
});
|
|
|
|
it('should handle returnTo with null bytes', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\0Doe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\0Doe');
|
|
});
|
|
|
|
it('should handle returnTo with bell characters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\aDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\aDoe');
|
|
});
|
|
|
|
it('should handle returnTo with escape characters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\eDoe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\eDoe');
|
|
});
|
|
|
|
it('should handle returnTo with unicode characters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John\u00D6Doe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John\u00D6Doe');
|
|
});
|
|
|
|
it('should handle returnTo with emoji', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John😀Doe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John😀Doe');
|
|
});
|
|
|
|
it('should handle returnTo with special symbols', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard?name=John!@#$%^&*()_+-=[]{}|;:,.<>?Doe',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard?name=John!@#$%^&*()_+-=[]{}|;:,.<>?Doe');
|
|
});
|
|
|
|
it('should handle returnTo with mixed special characters', () => {
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: '/dashboard/leagues/league-123/settings?name=John%20Doe&email=test%40example.com&filter=active=true&sort=name#section-1',
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe('/dashboard/leagues/league-123/settings?name=John%20Doe&email=test%40example.com&filter=active=true&sort=name#section-1');
|
|
});
|
|
|
|
it('should handle returnTo with very long path', () => {
|
|
const longPath = '/dashboard/leagues/league-123/settings/section/subsection/item/' + 'a'.repeat(1000);
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: longPath,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe(longPath);
|
|
});
|
|
|
|
it('should handle returnTo with very long query string', () => {
|
|
const longQuery = '/dashboard?' + 'a'.repeat(1000) + '=value';
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: longQuery,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe(longQuery);
|
|
});
|
|
|
|
it('should handle returnTo with very long fragment', () => {
|
|
const longFragment = '/dashboard#' + 'a'.repeat(1000);
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: longFragment,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe(longFragment);
|
|
});
|
|
|
|
it('should handle returnTo with mixed very long components', () => {
|
|
const longPath = '/dashboard/leagues/league-123/settings/section/subsection/item/' + 'a'.repeat(500);
|
|
const longQuery = '?' + 'b'.repeat(500) + '=value';
|
|
const longFragment = '#' + 'c'.repeat(500);
|
|
const forgotPasswordViewData: ForgotPasswordViewData = {
|
|
returnTo: longPath + longQuery + longFragment,
|
|
};
|
|
|
|
const result = ForgotPasswordViewModelBuilder.build(forgotPasswordViewData);
|
|
|
|
expect(result.returnTo).toBe(longPath + longQuery + longFragment);
|
|
});
|
|
});
|
|
});
|