184 lines
5.7 KiB
TypeScript
184 lines
5.7 KiB
TypeScript
import '@angular/compiler';
|
|
import { Component, inject } from '@angular/core';
|
|
import { DOCUMENT } from '@angular/common';
|
|
import { TestBed, getTestBed } from '@angular/core/testing';
|
|
import { By } from '@angular/platform-browser';
|
|
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { MODAL_DATA, ModalRef, ModalService } from '../../../core/services/modal.service';
|
|
import { ModalHostComponent } from './modal-host.component';
|
|
|
|
@Component({
|
|
standalone: true,
|
|
template: `
|
|
<div class="modal-test-content">
|
|
<span class="modal-test-title">{{ data?.title }}</span>
|
|
<button type="button" class="modal-test-close" (click)="close()">Cerrar</button>
|
|
</div>
|
|
`,
|
|
})
|
|
class ModalContentTestComponent {
|
|
readonly data = inject<{ title: string } | null>(MODAL_DATA);
|
|
readonly modalRef = inject<ModalRef<string>>(ModalRef);
|
|
|
|
close(): void {
|
|
this.modalRef.close('accepted');
|
|
}
|
|
}
|
|
|
|
describe('ModalHostComponent', () => {
|
|
let service: ModalService;
|
|
let doc: Document;
|
|
|
|
beforeAll(() => {
|
|
try {
|
|
getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
|
|
} catch {
|
|
// Test environment may already be initialized by another setup entrypoint.
|
|
}
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await TestBed.configureTestingModule({
|
|
imports: [ModalHostComponent],
|
|
}).compileComponents();
|
|
|
|
service = TestBed.inject(ModalService);
|
|
doc = TestBed.inject(DOCUMENT);
|
|
});
|
|
|
|
afterEach(() => {
|
|
doc.body.style.overflow = '';
|
|
doc.body.style.paddingRight = '';
|
|
TestBed.resetTestingModule();
|
|
});
|
|
|
|
it('renders nothing when no modal is active', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
fixture.detectChanges();
|
|
|
|
expect(fixture.nativeElement.textContent?.trim()).toBe('');
|
|
expect(doc.body.style.overflow).toBe('');
|
|
});
|
|
|
|
it('opens and renders the requested component with its title and data', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
|
|
service.open(ModalContentTestComponent, {
|
|
title: 'Editar producto',
|
|
data: { title: 'Contenido del modal' },
|
|
});
|
|
fixture.detectChanges();
|
|
|
|
const title = fixture.nativeElement.querySelector('.modal-shell__title');
|
|
const content = fixture.nativeElement.querySelector('.modal-test-title');
|
|
|
|
expect(title?.textContent).toContain('Editar producto');
|
|
expect(content?.textContent).toContain('Contenido del modal');
|
|
expect(doc.body.style.overflow).toBe('hidden');
|
|
});
|
|
|
|
it('closes with a result from the child component', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
const ref = service.open(ModalContentTestComponent, {
|
|
data: { title: 'Cerrar' },
|
|
});
|
|
const closedSpy = vi.fn();
|
|
|
|
ref.afterClosed$.subscribe(closedSpy);
|
|
fixture.detectChanges();
|
|
|
|
const closeButton = fixture.nativeElement.querySelector(
|
|
'.modal-test-close',
|
|
) as HTMLButtonElement;
|
|
closeButton.click();
|
|
fixture.detectChanges();
|
|
|
|
expect(closedSpy).toHaveBeenCalledWith('accepted');
|
|
expect(service.activeModal()).toBeNull();
|
|
expect(doc.body.style.overflow).toBe('');
|
|
expect(doc.body.style.paddingRight).toBe('');
|
|
});
|
|
|
|
it('closes on backdrop click when enabled', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
const ref = service.open(ModalContentTestComponent, {
|
|
data: { title: 'Backdrop' },
|
|
closeOnBackdrop: true,
|
|
});
|
|
|
|
fixture.detectChanges();
|
|
|
|
const backdrop = fixture.nativeElement.querySelector('.modal-shell') as HTMLDivElement;
|
|
backdrop.click();
|
|
fixture.detectChanges();
|
|
|
|
expect(service.activeModal()).toBeNull();
|
|
expect(ref.dismissReason()).toBe('backdrop');
|
|
});
|
|
|
|
it('does not close on backdrop click when disabled', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
const ref = service.open(ModalContentTestComponent, {
|
|
data: { title: 'Persistente' },
|
|
closeOnBackdrop: false,
|
|
});
|
|
|
|
fixture.detectChanges();
|
|
|
|
const backdrop = fixture.nativeElement.querySelector('.modal-shell') as HTMLDivElement;
|
|
backdrop.click();
|
|
fixture.detectChanges();
|
|
|
|
expect(service.activeModal()?.ref).toBe(ref);
|
|
});
|
|
|
|
it('closes on Escape when enabled', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
const ref = service.open(ModalContentTestComponent, {
|
|
data: { title: 'Escape' },
|
|
closeOnEscape: true,
|
|
});
|
|
|
|
fixture.detectChanges();
|
|
doc.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
fixture.detectChanges();
|
|
|
|
expect(service.activeModal()).toBeNull();
|
|
expect(ref.dismissReason()).toBe('escape');
|
|
});
|
|
|
|
it('keeps the modal open on Escape when disabled', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
const ref = service.open(ModalContentTestComponent, {
|
|
data: { title: 'No Escape' },
|
|
closeOnEscape: false,
|
|
});
|
|
|
|
fixture.detectChanges();
|
|
doc.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
fixture.detectChanges();
|
|
|
|
expect(service.activeModal()?.ref).toBe(ref);
|
|
});
|
|
|
|
it('closes from the shell close button with a programmatic dismiss reason', () => {
|
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
|
const ref = service.open(ModalContentTestComponent, {
|
|
title: 'Con cierre',
|
|
data: { title: 'Boton' },
|
|
});
|
|
|
|
fixture.detectChanges();
|
|
|
|
const closeButton = fixture.debugElement.query(By.css('.btn-close'))
|
|
.nativeElement as HTMLButtonElement;
|
|
closeButton.click();
|
|
fixture.detectChanges();
|
|
|
|
expect(service.activeModal()).toBeNull();
|
|
expect(ref.dismissReason()).toBe('programmatic');
|
|
});
|
|
});
|