mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
1. Reward Shopping Cart Implementation - New shopping cart with quantity control and availability checking - Responsive shopping cart item component with improved CSS styling - Shipping address integration in cart - Customer reward card and billing/shipping address components 2. Navigation State Management Library (@isa/core/navigation) - New library with type-safe navigation context service (373 lines) - Navigation state service (287 lines) for temporary state between routes - Comprehensive test coverage (668 + 227 lines of tests) - Documentation (792 lines in README.md) - Replaces query parameters for passing temporary navigation context 3. CRM Shipping Address Services - New ShippingAddressService with fetching and validation - CustomerShippingAddressResource and CustomerShippingAddressesResource - Zod schemas for data validation 4. Additional Improvements - Enhanced searchbox accessibility with ARIA support - Availability data access rework for better fetching/mapping - Storybook tooltip variant support - Vitest JUnit and Cobertura reporting configuration Related work items: #5382, #5383, #5384
228 lines
7.5 KiB
TypeScript
228 lines
7.5 KiB
TypeScript
import { TestBed } from '@angular/core/testing';
|
|
import { Location } from '@angular/common';
|
|
import { Router } from '@angular/router';
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { NavigationStateService } from './navigation-state.service';
|
|
import { NavigationContextService } from './navigation-context.service';
|
|
import { ReturnUrlContext } from './navigation-context.types';
|
|
|
|
describe('NavigationStateService', () => {
|
|
let service: NavigationStateService;
|
|
let locationMock: { getState: ReturnType<typeof vi.fn> };
|
|
let routerMock: { navigate: ReturnType<typeof vi.fn> };
|
|
let contextServiceMock: {
|
|
setContext: ReturnType<typeof vi.fn>;
|
|
getContext: ReturnType<typeof vi.fn>;
|
|
getAndClearContext: ReturnType<typeof vi.fn>;
|
|
clearContext: ReturnType<typeof vi.fn>;
|
|
hasContext: ReturnType<typeof vi.fn>;
|
|
clearScope: ReturnType<typeof vi.fn>;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
locationMock = {
|
|
getState: vi.fn(),
|
|
};
|
|
|
|
routerMock = {
|
|
navigate: vi.fn(),
|
|
};
|
|
|
|
contextServiceMock = {
|
|
setContext: vi.fn().mockResolvedValue(undefined),
|
|
getContext: vi.fn().mockResolvedValue(null),
|
|
getAndClearContext: vi.fn().mockResolvedValue(null),
|
|
clearContext: vi.fn().mockResolvedValue(false),
|
|
hasContext: vi.fn().mockResolvedValue(false),
|
|
clearScope: vi.fn().mockResolvedValue(0),
|
|
};
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [
|
|
NavigationStateService,
|
|
{ provide: Location, useValue: locationMock },
|
|
{ provide: Router, useValue: routerMock },
|
|
{ provide: NavigationContextService, useValue: contextServiceMock },
|
|
],
|
|
});
|
|
|
|
service = TestBed.inject(NavigationStateService);
|
|
});
|
|
|
|
it('should be created', () => {
|
|
expect(service).toBeTruthy();
|
|
});
|
|
|
|
// Context Preservation Methods Tests
|
|
|
|
describe('preserveContext', () => {
|
|
it('should call contextService.setContext with correct parameters', async () => {
|
|
const data: ReturnUrlContext = { returnUrl: '/test-page' };
|
|
const scopeKey = 'process-123';
|
|
|
|
await service.preserveContext(data, scopeKey);
|
|
|
|
expect(contextServiceMock.setContext).toHaveBeenCalledWith(data, scopeKey);
|
|
});
|
|
|
|
it('should work without scope key', async () => {
|
|
const data: ReturnUrlContext = { returnUrl: '/test-page' };
|
|
|
|
await service.preserveContext(data);
|
|
|
|
expect(contextServiceMock.setContext).toHaveBeenCalledWith(data, undefined);
|
|
});
|
|
});
|
|
|
|
describe('restoreContext', () => {
|
|
it('should call contextService.getContext with correct parameters', async () => {
|
|
const expectedData: ReturnUrlContext = { returnUrl: '/test-page' };
|
|
contextServiceMock.getContext.mockResolvedValue(expectedData);
|
|
|
|
const result = await service.restoreContext<ReturnUrlContext>('scope-123');
|
|
|
|
expect(contextServiceMock.getContext).toHaveBeenCalledWith('scope-123');
|
|
expect(result).toEqual(expectedData);
|
|
});
|
|
|
|
it('should return null when context not found', async () => {
|
|
contextServiceMock.getContext.mockResolvedValue(null);
|
|
|
|
const result = await service.restoreContext();
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should work without parameters', async () => {
|
|
const expectedData: ReturnUrlContext = { returnUrl: '/test-page' };
|
|
contextServiceMock.getContext.mockResolvedValue(expectedData);
|
|
|
|
const result = await service.restoreContext<ReturnUrlContext>();
|
|
|
|
expect(contextServiceMock.getContext).toHaveBeenCalledWith(undefined);
|
|
expect(result).toEqual(expectedData);
|
|
});
|
|
});
|
|
|
|
describe('restoreAndClearContext', () => {
|
|
it('should call contextService.getAndClearContext with correct parameters', async () => {
|
|
const expectedData: ReturnUrlContext = { returnUrl: '/test-page' };
|
|
contextServiceMock.getAndClearContext.mockResolvedValue(expectedData);
|
|
|
|
const result = await service.restoreAndClearContext<ReturnUrlContext>('scope-123');
|
|
|
|
expect(contextServiceMock.getAndClearContext).toHaveBeenCalledWith('scope-123');
|
|
expect(result).toEqual(expectedData);
|
|
});
|
|
|
|
it('should return null when context not found', async () => {
|
|
contextServiceMock.getAndClearContext.mockResolvedValue(null);
|
|
|
|
const result = await service.restoreAndClearContext();
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('clearPreservedContext', () => {
|
|
it('should call contextService.clearContext and return result', async () => {
|
|
contextServiceMock.clearContext.mockResolvedValue(true);
|
|
|
|
const result = await service.clearPreservedContext('scope-123');
|
|
|
|
expect(contextServiceMock.clearContext).toHaveBeenCalledWith('scope-123');
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('should return false when context not found', async () => {
|
|
contextServiceMock.clearContext.mockResolvedValue(false);
|
|
|
|
const result = await service.clearPreservedContext();
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('hasPreservedContext', () => {
|
|
it('should call contextService.hasContext and return result', async () => {
|
|
contextServiceMock.hasContext.mockResolvedValue(true);
|
|
|
|
const result = await service.hasPreservedContext('scope-123');
|
|
|
|
expect(contextServiceMock.hasContext).toHaveBeenCalledWith('scope-123');
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('should return false when context not found', async () => {
|
|
contextServiceMock.hasContext.mockResolvedValue(false);
|
|
|
|
const result = await service.hasPreservedContext();
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('navigateWithPreservedContext', () => {
|
|
it('should preserve context and navigate', async () => {
|
|
const data: ReturnUrlContext = { returnUrl: '/reward/cart', customerId: 123 };
|
|
const commands = ['/customer/search'];
|
|
const scopeKey = 'process-123';
|
|
|
|
routerMock.navigate.mockResolvedValue(true);
|
|
|
|
const result = await service.navigateWithPreservedContext(commands, data, scopeKey);
|
|
|
|
expect(contextServiceMock.setContext).toHaveBeenCalledWith(data, scopeKey);
|
|
expect(routerMock.navigate).toHaveBeenCalledWith(commands, {
|
|
state: data,
|
|
});
|
|
expect(result).toEqual({ success: true });
|
|
});
|
|
|
|
it('should merge navigation extras', async () => {
|
|
const data: ReturnUrlContext = { returnUrl: '/test' };
|
|
const commands = ['/page'];
|
|
const extras = { queryParams: { foo: 'bar' } };
|
|
|
|
routerMock.navigate.mockResolvedValue(true);
|
|
|
|
await service.navigateWithPreservedContext(commands, data, undefined, extras);
|
|
|
|
expect(routerMock.navigate).toHaveBeenCalledWith(commands, {
|
|
queryParams: { foo: 'bar' },
|
|
state: data,
|
|
});
|
|
});
|
|
|
|
it('should return false when navigation fails', async () => {
|
|
const data: ReturnUrlContext = { returnUrl: '/test' };
|
|
const commands = ['/page'];
|
|
|
|
routerMock.navigate.mockResolvedValue(false);
|
|
|
|
const result = await service.navigateWithPreservedContext(commands, data);
|
|
|
|
expect(result).toEqual({ success: false });
|
|
});
|
|
});
|
|
|
|
describe('clearScopeContexts', () => {
|
|
it('should call contextService.clearScope and return count', async () => {
|
|
contextServiceMock.clearScope.mockResolvedValue(3);
|
|
|
|
const result = await service.clearScopeContexts();
|
|
|
|
expect(contextServiceMock.clearScope).toHaveBeenCalled();
|
|
expect(result).toBe(3);
|
|
});
|
|
|
|
it('should return 0 when no contexts cleared', async () => {
|
|
contextServiceMock.clearScope.mockResolvedValue(0);
|
|
|
|
const result = await service.clearScopeContexts();
|
|
|
|
expect(result).toBe(0);
|
|
});
|
|
});
|
|
});
|