feat(web): pantalla de permisos de menú (/admin/permisos)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d900a96740
commit
4d4df17d07
11 files changed
+496
-5
No files matched your search
@@ -0,0 +1,96 @@
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { type ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { SessionStore } from '../../core/auth/session.store';
|
||||
import { PermissionsPage } from './permissions.page';
|
||||
|
||||
describe('PermissionsPage', () => {
|
||||
let fixture: ComponentFixture<PermissionsPage>;
|
||||
let ctrl: HttpTestingController;
|
||||
const el = () => fixture.nativeElement as HTMLElement;
|
||||
const tick = async () => {
|
||||
fixture.detectChanges();
|
||||
await new Promise((r) => setTimeout(r));
|
||||
fixture.detectChanges();
|
||||
};
|
||||
const click = async (selector: string, index = 0) => {
|
||||
(el().querySelectorAll(selector)[index] as HTMLElement).click();
|
||||
await tick();
|
||||
};
|
||||
const byText = (selector: string, text: string) =>
|
||||
[...el().querySelectorAll<HTMLElement>(selector)].find((e) => e.textContent?.trim().startsWith(text))!;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PermissionsPage],
|
||||
providers: [provideHttpClient(), provideHttpClientTesting()],
|
||||
}).compileComponents();
|
||||
TestBed.inject(SessionStore); // sin sesión: solo necesitamos que exista
|
||||
ctrl = TestBed.inject(HttpTestingController);
|
||||
fixture = TestBed.createComponent(PermissionsPage);
|
||||
await tick();
|
||||
ctrl.expectOne('/api/permissions').flush([
|
||||
{ webUser: 'ana', paths: ['/ventas/*', '/socios/clientes'] },
|
||||
{ webUser: 'luis', paths: ['*'] },
|
||||
]);
|
||||
await tick();
|
||||
});
|
||||
|
||||
it('lista los usuarios con permisos', () => {
|
||||
expect([...el().querySelectorAll('.users li button')].map((b) => b.textContent?.trim())).toEqual(['ana', 'luis']);
|
||||
});
|
||||
|
||||
it('al elegir un usuario marca sus pantallas (el módulo completo por /ventas/*)', async () => {
|
||||
await click('.users li button', 0);
|
||||
const checked = [...el().querySelectorAll<HTMLInputElement>('fieldset .items input')].filter((i) => i.checked);
|
||||
expect(checked.length).toBe(7); // 6 de ventas + Clientes
|
||||
expect(el().querySelector<HTMLButtonElement>('.actions .main')!.disabled).toBe(true); // sin cambios
|
||||
});
|
||||
|
||||
it('el acceso total oculta las casillas y no ofrece Administración como casilla', async () => {
|
||||
await click('.users li button', 1);
|
||||
expect(el().querySelector('fieldset')).toBeNull();
|
||||
await click('.users li button', 0);
|
||||
expect(byText('legend label', 'Administración')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('guarda los patrones resultantes con PUT', async () => {
|
||||
await click('.users li button', 0);
|
||||
const inv = byText('legend label', 'Inventario').querySelector('input')!;
|
||||
inv.click();
|
||||
await tick();
|
||||
expect(el().querySelector<HTMLButtonElement>('.actions .main')!.disabled).toBe(false);
|
||||
await click('.actions .main');
|
||||
const req = ctrl.expectOne((r) => r.method === 'PUT');
|
||||
expect(req.request.url).toBe('/api/permissions/ana');
|
||||
expect(req.request.body).toEqual({ paths: ['/ventas/*', '/inventario/*', '/socios/clientes'] });
|
||||
req.flush({ webUser: 'ana', paths: [] });
|
||||
await tick();
|
||||
ctrl.expectOne('/api/permissions').flush([{ webUser: 'ana', paths: ['/ventas/*', '/inventario/*', '/socios/clientes'] }]);
|
||||
await tick();
|
||||
expect(el().querySelector('.msg')?.textContent).toContain('Permisos de ana guardados');
|
||||
});
|
||||
|
||||
it('muestra el error del servidor, por ejemplo al quitar el último administrador', async () => {
|
||||
await click('.users li button', 1);
|
||||
await click('.actions .danger');
|
||||
ctrl.expectOne((r) => r.method === 'DELETE' && r.url === '/api/permissions/luis').flush(
|
||||
{ message: 'No se puede quitar al último administrador' },
|
||||
{ status: 409, statusText: 'Conflict' },
|
||||
);
|
||||
await tick();
|
||||
expect(el().querySelector('.msg.err')?.textContent).toContain('último administrador');
|
||||
});
|
||||
|
||||
it('agregar un usuario lo selecciona sin guardar hasta pulsar Guardar', async () => {
|
||||
const input = el().querySelector<HTMLInputElement>('.add input')!;
|
||||
input.value = 'marta';
|
||||
input.dispatchEvent(new Event('input'));
|
||||
await tick();
|
||||
await click('.add .btn');
|
||||
expect([...el().querySelectorAll('.users li button')].map((b) => b.textContent?.trim())).toContain('marta');
|
||||
expect(el().querySelector('.detail h2')?.textContent).toBe('marta');
|
||||
ctrl.expectNone((r) => r.method === 'PUT');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user