first commit
This commit is contained in:
159
libs/web/tracker/application/src/lib/facades/facades.spec.ts
Normal file
159
libs/web/tracker/application/src/lib/facades/facades.spec.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { Events } from '@ngrx/signals/events';
|
||||
import { AuthFacade } from '@task-stream/web/identity/application';
|
||||
import {
|
||||
ActivityGateway,
|
||||
CommentsGateway,
|
||||
IssuesGateway,
|
||||
IssueStatus,
|
||||
Project,
|
||||
ProjectRole,
|
||||
ProjectsGateway,
|
||||
ReleasesGateway,
|
||||
TrackerRealtimeGateway,
|
||||
UsersGateway,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { NEVER, of } from 'rxjs';
|
||||
import { boardPageEvents } from '../events/board.events';
|
||||
import { projectPageEvents } from '../events/project.events';
|
||||
import { Notifications } from '../notifications';
|
||||
import { BoardFacade, provideBoardFacade } from './board.facade';
|
||||
import { IssueFacade, provideIssueFacade } from './issue.facade';
|
||||
import { ProjectFacade, provideProjectFacade } from './project.facade';
|
||||
|
||||
const ok = { id: 'x', key: null, aggregateVersion: 1 };
|
||||
const project = { id: 'p1', key: 'WEB', members: [] } as unknown as Project;
|
||||
const issue = { id: 'i1', key: 'WEB-1', links: [] };
|
||||
|
||||
/** Every command succeeds, every query answers once, nothing arrives in real time. */
|
||||
function gateways() {
|
||||
return {
|
||||
projects: {
|
||||
project: vi.fn(() => of(project)),
|
||||
stats: vi.fn(() => NEVER),
|
||||
addMember: vi.fn(() => of(ok)),
|
||||
},
|
||||
issues: {
|
||||
board: vi.fn(() => of([])),
|
||||
issue: vi.fn(() => of(issue)),
|
||||
changeStatus: vi.fn(() => of(ok)),
|
||||
},
|
||||
list: { list: vi.fn(() => of([])) },
|
||||
realtime: {
|
||||
issueChanges: () => NEVER,
|
||||
commentChanges: () => NEVER,
|
||||
activity: () => NEVER,
|
||||
releaseChanges: () => NEVER,
|
||||
projectChanges: () => NEVER,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Facades are scoped to a route component, like in the app. */
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
provideProjectFacade(),
|
||||
provideBoardFacade(),
|
||||
provideIssueFacade(),
|
||||
],
|
||||
})
|
||||
class Host {
|
||||
readonly project = inject(ProjectFacade);
|
||||
readonly board = inject(BoardFacade);
|
||||
readonly issue = inject(IssueFacade);
|
||||
}
|
||||
|
||||
function setup() {
|
||||
const fakes = gateways();
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: ProjectsGateway, useValue: fakes.projects },
|
||||
{ provide: IssuesGateway, useValue: fakes.issues },
|
||||
{ provide: ReleasesGateway, useValue: fakes.list },
|
||||
{ provide: ActivityGateway, useValue: fakes.list },
|
||||
{ provide: CommentsGateway, useValue: fakes.list },
|
||||
{ provide: UsersGateway, useValue: { search: () => of([]) } },
|
||||
{ provide: TrackerRealtimeGateway, useValue: fakes.realtime },
|
||||
{
|
||||
provide: Notifications,
|
||||
useValue: { success: vi.fn(), error: vi.fn() },
|
||||
},
|
||||
{ provide: AuthFacade, useValue: { user: signal({ id: 'me' }) } },
|
||||
],
|
||||
});
|
||||
const host = TestBed.createComponent(Host).componentInstance;
|
||||
return { ...host, fakes };
|
||||
}
|
||||
|
||||
describe('BoardFacade', () => {
|
||||
it('opens the board for a new project and refilters the same one', () => {
|
||||
const { board, fakes } = setup();
|
||||
const seen: string[] = [];
|
||||
TestBed.inject(Events)
|
||||
.on(boardPageEvents.opened, boardPageEvents.filterChanged)
|
||||
.subscribe((event) => seen.push(event.type));
|
||||
|
||||
board.show({ projectId: 'p1' });
|
||||
board.show({ projectId: 'p1', assigneeId: 'u2' });
|
||||
board.show({ projectId: 'p2' });
|
||||
|
||||
expect(seen).toEqual([
|
||||
boardPageEvents.opened.type,
|
||||
boardPageEvents.filterChanged.type,
|
||||
boardPageEvents.opened.type,
|
||||
]);
|
||||
expect(fakes.issues.board).toHaveBeenLastCalledWith({ projectId: 'p2' });
|
||||
expect(board.filter()).toEqual({ projectId: 'p2' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProjectFacade', () => {
|
||||
it('ignores commands until a project is open, then sends its id', () => {
|
||||
const { project, fakes } = setup();
|
||||
|
||||
project.addMember('u2');
|
||||
expect(fakes.projects.addMember).not.toHaveBeenCalled();
|
||||
|
||||
project.open('WEB');
|
||||
expect(project.project()?.id).toBe('p1');
|
||||
|
||||
project.addMember('u2', ProjectRole.VIEWER);
|
||||
expect(fakes.projects.addMember).toHaveBeenCalledWith({
|
||||
projectId: 'p1',
|
||||
userId: 'u2',
|
||||
role: ProjectRole.VIEWER,
|
||||
});
|
||||
});
|
||||
|
||||
it('searches the directory from two characters', () => {
|
||||
const { project } = setup();
|
||||
const seen: string[] = [];
|
||||
TestBed.inject(Events)
|
||||
.on(projectPageEvents.userSearchRequested)
|
||||
.subscribe(({ payload }) => seen.push(payload));
|
||||
|
||||
project.searchUsers(' a ');
|
||||
project.searchUsers(' an ');
|
||||
|
||||
expect(seen).toEqual(['an']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('IssueFacade', () => {
|
||||
it('acts on the open ticket', () => {
|
||||
const { issue, fakes } = setup();
|
||||
|
||||
issue.changeStatus(IssueStatus.IN_PROGRESS);
|
||||
expect(fakes.issues.changeStatus).not.toHaveBeenCalled();
|
||||
|
||||
issue.open('WEB-1');
|
||||
issue.changeStatus(IssueStatus.QA_FAILED, 'Falla en Safari');
|
||||
expect(fakes.issues.changeStatus).toHaveBeenCalledWith({
|
||||
issueId: 'i1',
|
||||
status: IssueStatus.QA_FAILED,
|
||||
reason: 'Falla en Safari',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user