import { describe, it, expect } from 'vitest'; import { PageStateValidator } from '../../../../packages/domain/services/PageStateValidator'; describe('PageStateValidator', () => { const validator = new PageStateValidator(); describe('validateState', () => { it('should return valid when all required selectors are present', () => { // Arrange const actualState = (selector: string) => { return ['#add-car-button', '#cars-list'].includes(selector); }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button', '#cars-list'] }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(true); expect(value.expectedStep).toBe('cars'); expect(value.message).toContain('Page state valid'); }); it('should return invalid when required selectors are missing', () => { // Arrange const actualState = (selector: string) => { return selector === '#add-car-button'; // Only one of two selectors present }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button', '#cars-list'] }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(false); expect(value.expectedStep).toBe('cars'); expect(value.missingSelectors).toEqual(['#cars-list']); expect(value.message).toContain('missing required elements'); }); it('should return invalid when forbidden selectors are present', () => { // Arrange const actualState = (selector: string) => { return ['#add-car-button', '#set-track'].includes(selector); }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button'], forbiddenSelectors: ['#set-track'] // Should NOT be on track page yet }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(false); expect(value.expectedStep).toBe('cars'); expect(value.unexpectedSelectors).toEqual(['#set-track']); expect(value.message).toContain('unexpected elements'); }); it('should handle empty forbidden selectors array', () => { // Arrange const actualState = (selector: string) => { return selector === '#add-car-button'; }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button'], forbiddenSelectors: [] }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(true); }); it('should handle undefined forbidden selectors', () => { // Arrange const actualState = (selector: string) => { return selector === '#add-car-button'; }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button'] // forbiddenSelectors is undefined }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(true); }); it('should return error result when actualState function throws', () => { // Arrange const actualState = (selector: string) => { throw new Error('Selector evaluation failed'); }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button'] }); // Assert expect(result.isErr()).toBe(true); const error = result.unwrapErr(); expect(error.message).toContain('Selector evaluation failed'); }); it('should provide clear error messages for missing selectors', () => { // Arrange const actualState = () => false; // Nothing present // Act const result = validator.validateState(actualState, { expectedStep: 'track', requiredSelectors: ['#set-track', '#track-search'] }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(false); expect(value.message).toBe('Page state mismatch: Expected to be on "track" page but missing required elements'); expect(value.missingSelectors).toEqual(['#set-track', '#track-search']); }); it('should validate complex state with both required and forbidden selectors', () => { // Arrange - Simulate being on Cars page but Track page elements leaked through const actualState = (selector: string) => { const presentSelectors = ['#add-car-button', '#cars-list', '#set-track']; return presentSelectors.includes(selector); }; // Act const result = validator.validateState(actualState, { expectedStep: 'cars', requiredSelectors: ['#add-car-button', '#cars-list'], forbiddenSelectors: ['#set-track', '#track-search'] }); // Assert expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(value.isValid).toBe(false); // Invalid due to forbidden selector expect(value.unexpectedSelectors).toEqual(['#set-track']); expect(value.message).toContain('unexpected elements'); }); }); });