277 lines
7.6 KiB
TypeScript
277 lines
7.6 KiB
TypeScript
import '@angular/compiler';
|
|
import { signal } from '@angular/core';
|
|
import { TestBed, getTestBed } from '@angular/core/testing';
|
|
import { By } from '@angular/platform-browser';
|
|
import {
|
|
BrowserTestingModule,
|
|
platformBrowserTesting
|
|
} from '@angular/platform-browser/testing';
|
|
import { of, throwError } from 'rxjs';
|
|
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { CartService } from '../../../core/services/cart/cart.service';
|
|
import { ModalService } from '../../../core/services/modal.service';
|
|
import { ToastService } from '../../../core/services/toast.service';
|
|
import { CartComponent } from './cart.component';
|
|
|
|
describe('CartComponent', () => {
|
|
beforeAll(() => {
|
|
try {
|
|
getTestBed().initTestEnvironment(
|
|
BrowserTestingModule,
|
|
platformBrowserTesting()
|
|
);
|
|
} catch {
|
|
// Test environment may already be initialized by another setup entrypoint.
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
TestBed.resetTestingModule();
|
|
});
|
|
|
|
it('opens a confirm delete modal before removing an item', async () => {
|
|
const removeItem = vi.fn().mockReturnValue(of({ message: 'Producto eliminado.' }));
|
|
const openConfirmDelete = vi.fn().mockReturnValue(of(true));
|
|
|
|
await TestBed.configureTestingModule({
|
|
imports: [CartComponent],
|
|
providers: [
|
|
{
|
|
provide: CartService,
|
|
useValue: {
|
|
cart: signal(null).asReadonly(),
|
|
updateItemQuantity: vi.fn(),
|
|
removeItem
|
|
}
|
|
},
|
|
{
|
|
provide: ModalService,
|
|
useValue: {
|
|
openConfirmDelete
|
|
}
|
|
},
|
|
{
|
|
provide: ToastService,
|
|
useValue: {
|
|
success: vi.fn(),
|
|
info: vi.fn(),
|
|
danger: vi.fn()
|
|
}
|
|
}
|
|
]
|
|
}).compileComponents();
|
|
|
|
const fixture = TestBed.createComponent(CartComponent);
|
|
fixture.componentRef.setInput('items', [
|
|
{
|
|
productVariantId: 10,
|
|
imageUrl: null,
|
|
product: 'Producto de prueba',
|
|
originalPrice: null,
|
|
discountedPrice: 1000,
|
|
discountPercentage: null,
|
|
attributes: [],
|
|
quantity: 1
|
|
}
|
|
]);
|
|
fixture.detectChanges();
|
|
|
|
fixture.debugElement.query(By.css('app-cart-item')).triggerEventHandler('remove');
|
|
|
|
expect(openConfirmDelete).toHaveBeenCalledWith({
|
|
title: 'Eliminar producto',
|
|
content:
|
|
'Se eliminara "Producto de prueba" del carrito. Esta accion no se puede deshacer.',
|
|
confirmLabel: 'Eliminar',
|
|
cancelLabel: 'Cancelar'
|
|
});
|
|
expect(removeItem).toHaveBeenCalledWith(10);
|
|
});
|
|
|
|
it('does not remove the item when the delete confirmation is cancelled', async () => {
|
|
const removeItem = vi.fn().mockReturnValue(of({}));
|
|
const openConfirmDelete = vi.fn().mockReturnValue(of(false));
|
|
|
|
await TestBed.configureTestingModule({
|
|
imports: [CartComponent],
|
|
providers: [
|
|
{
|
|
provide: CartService,
|
|
useValue: {
|
|
cart: signal(null).asReadonly(),
|
|
updateItemQuantity: vi.fn(),
|
|
removeItem
|
|
}
|
|
},
|
|
{
|
|
provide: ModalService,
|
|
useValue: {
|
|
openConfirmDelete
|
|
}
|
|
},
|
|
{
|
|
provide: ToastService,
|
|
useValue: {
|
|
success: vi.fn(),
|
|
info: vi.fn(),
|
|
danger: vi.fn()
|
|
}
|
|
}
|
|
]
|
|
}).compileComponents();
|
|
|
|
const fixture = TestBed.createComponent(CartComponent);
|
|
fixture.componentRef.setInput('items', [
|
|
{
|
|
productVariantId: 10,
|
|
imageUrl: null,
|
|
product: 'Producto de prueba',
|
|
originalPrice: null,
|
|
discountedPrice: 1000,
|
|
discountPercentage: null,
|
|
attributes: [],
|
|
quantity: 1
|
|
}
|
|
]);
|
|
fixture.detectChanges();
|
|
|
|
fixture.debugElement.query(By.css('app-cart-item')).triggerEventHandler('remove');
|
|
|
|
expect(openConfirmDelete).toHaveBeenCalled();
|
|
expect(removeItem).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('optimistically updates quantity and rolls back on error', async () => {
|
|
vi.useFakeTimers();
|
|
const updateItemQuantity = vi.fn().mockReturnValue(throwError(() => new Error('Error')));
|
|
const danger = vi.fn();
|
|
|
|
await TestBed.configureTestingModule({
|
|
imports: [CartComponent],
|
|
providers: [
|
|
{
|
|
provide: CartService,
|
|
useValue: {
|
|
cart: signal(null).asReadonly(),
|
|
updateItemQuantity,
|
|
removeItem: vi.fn()
|
|
}
|
|
},
|
|
{
|
|
provide: ModalService,
|
|
useValue: {}
|
|
},
|
|
{
|
|
provide: ToastService,
|
|
useValue: {
|
|
success: vi.fn(),
|
|
info: vi.fn(),
|
|
danger
|
|
}
|
|
}
|
|
]
|
|
}).compileComponents();
|
|
|
|
const fixture = TestBed.createComponent(CartComponent);
|
|
const component = fixture.componentInstance;
|
|
|
|
const item = {
|
|
productVariantId: 10,
|
|
imageUrl: null,
|
|
product: 'Producto de prueba',
|
|
originalPrice: null,
|
|
discountedPrice: 1000,
|
|
discountPercentage: null,
|
|
attributes: [],
|
|
quantity: 1
|
|
};
|
|
|
|
fixture.componentRef.setInput('items', [item]);
|
|
fixture.detectChanges();
|
|
|
|
// Trigger quantity change to 3
|
|
fixture.debugElement.query(By.css('app-cart-item')).triggerEventHandler('quantityChange', 3);
|
|
fixture.detectChanges();
|
|
|
|
// Optimistic update should be active immediately in local getter
|
|
expect((component as any).getItemQuantity(item)).toBe(3);
|
|
|
|
// Wait for the debounce time (1000ms)
|
|
vi.advanceTimersByTime(1000);
|
|
fixture.detectChanges();
|
|
|
|
// After failure, it should roll back to original quantity (1)
|
|
expect((component as any).getItemQuantity(item)).toBe(1);
|
|
expect(danger).toHaveBeenCalled();
|
|
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it('optimistically updates quantity and clears override on success', async () => {
|
|
vi.useFakeTimers();
|
|
const updateItemQuantity = vi.fn().mockReturnValue(of({ message: 'Success', data: {} }));
|
|
const success = vi.fn();
|
|
|
|
await TestBed.configureTestingModule({
|
|
imports: [CartComponent],
|
|
providers: [
|
|
{
|
|
provide: CartService,
|
|
useValue: {
|
|
cart: signal(null).asReadonly(),
|
|
updateItemQuantity,
|
|
removeItem: vi.fn()
|
|
}
|
|
},
|
|
{
|
|
provide: ModalService,
|
|
useValue: {}
|
|
},
|
|
{
|
|
provide: ToastService,
|
|
useValue: {
|
|
success,
|
|
info: vi.fn(),
|
|
danger: vi.fn()
|
|
}
|
|
}
|
|
]
|
|
}).compileComponents();
|
|
|
|
const fixture = TestBed.createComponent(CartComponent);
|
|
const component = fixture.componentInstance;
|
|
|
|
const item = {
|
|
productVariantId: 10,
|
|
imageUrl: null,
|
|
product: 'Producto de prueba',
|
|
originalPrice: null,
|
|
discountedPrice: 1000,
|
|
discountPercentage: null,
|
|
attributes: [],
|
|
quantity: 1
|
|
};
|
|
|
|
fixture.componentRef.setInput('items', [item]);
|
|
fixture.detectChanges();
|
|
|
|
// Trigger quantity change to 3
|
|
fixture.debugElement.query(By.css('app-cart-item')).triggerEventHandler('quantityChange', 3);
|
|
fixture.detectChanges();
|
|
|
|
// Optimistic update should be active immediately in local getter
|
|
expect((component as any).getItemQuantity(item)).toBe(3);
|
|
|
|
// Wait for the debounce time (1000ms)
|
|
vi.advanceTimersByTime(1000);
|
|
fixture.detectChanges();
|
|
|
|
// After success, it should clear override and use input quantity (which is 1 since we didn't update items input here)
|
|
expect((component as any).getItemQuantity(item)).toBe(1);
|
|
expect(success).toHaveBeenCalled();
|
|
|
|
vi.useRealTimers();
|
|
});
|
|
});
|