milestone dans tableau issue
This commit is contained in:
@@ -5,6 +5,7 @@ import { provideRouter } from '@angular/router';
|
||||
import { vi } from 'vitest';
|
||||
import { Issues } from './issues';
|
||||
import { IssueEntity, IssuesStore } from './issues.store';
|
||||
import { MilestoneEntity, MilestonesStore } from '../milestones/milestones.store';
|
||||
|
||||
const makeIssue = (overrides: Partial<IssueEntity> = {}): IssueEntity => ({
|
||||
id: 99,
|
||||
@@ -88,19 +89,63 @@ class FakeIssuesStore {
|
||||
}
|
||||
}
|
||||
|
||||
const makeMilestone = (overrides: Partial<MilestoneEntity> = {}): MilestoneEntity => ({
|
||||
id: 1,
|
||||
name: 'Sprint 1',
|
||||
description: '',
|
||||
dueDate: '',
|
||||
issueIds: [],
|
||||
...overrides,
|
||||
});
|
||||
|
||||
class FakeMilestonesStore {
|
||||
private _data = signal<MilestoneEntity[]>([]);
|
||||
|
||||
readonly milestones = this._data.asReadonly();
|
||||
readonly loading = signal(false);
|
||||
readonly loaded = signal(true);
|
||||
|
||||
seed(milestones: MilestoneEntity[]): void {
|
||||
this._data.set(milestones);
|
||||
}
|
||||
|
||||
load(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
upsert(milestone: MilestoneEntity): Promise<MilestoneEntity> {
|
||||
this._data.update((list) => {
|
||||
const idx = list.findIndex((m) => m.id === milestone.id);
|
||||
if (idx === -1) return [...list, milestone];
|
||||
const copy = [...list];
|
||||
copy[idx] = milestone;
|
||||
return copy;
|
||||
});
|
||||
return Promise.resolve(milestone);
|
||||
}
|
||||
|
||||
deleteById(id: number): Promise<void> {
|
||||
this._data.update((list) => list.filter((m) => m.id !== id));
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
describe('Issues', () => {
|
||||
let component: Issues;
|
||||
let fixture: ComponentFixture<Issues>;
|
||||
let store: FakeIssuesStore;
|
||||
let milestonesStore: FakeMilestonesStore;
|
||||
let router: Router;
|
||||
|
||||
beforeEach(async () => {
|
||||
store = new FakeIssuesStore();
|
||||
milestonesStore = new FakeMilestonesStore();
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Issues],
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: IssuesStore, useValue: store },
|
||||
{ provide: MilestonesStore, useValue: milestonesStore },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
@@ -259,6 +304,138 @@ describe('Issues', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMilestoneForIssue', () => {
|
||||
it('returns the milestone that contains the issue', () => {
|
||||
milestonesStore.seed([makeMilestone({ id: 10, name: 'Sprint A', issueIds: [1] })]);
|
||||
const m = (component as any).getMilestoneForIssue(1);
|
||||
expect(m?.id).toBe(10);
|
||||
});
|
||||
|
||||
it('returns undefined when no milestone contains the issue', () => {
|
||||
milestonesStore.seed([makeMilestone({ id: 10, name: 'Sprint A', issueIds: [99] })]);
|
||||
expect((component as any).getMilestoneForIssue(1)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('filteredIssues — milestone filter', () => {
|
||||
beforeEach(() => {
|
||||
milestonesStore.seed([
|
||||
makeMilestone({ id: 10, name: 'Sprint A', issueIds: [1] }),
|
||||
makeMilestone({ id: 20, name: 'Sprint B', issueIds: [2] }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('shows all issues when no milestone filter is active', () => {
|
||||
expect((component as any).filteredIssues.length).toBe(3);
|
||||
});
|
||||
|
||||
it('shows only issues of the selected milestone', () => {
|
||||
(component as any).selectedMilestoneIds = new Set([10]);
|
||||
const filtered: IssueEntity[] = (component as any).filteredIssues;
|
||||
expect(filtered.length).toBe(1);
|
||||
expect(filtered[0].id).toBe(1);
|
||||
});
|
||||
|
||||
it('shows issues from multiple selected milestones', () => {
|
||||
(component as any).selectedMilestoneIds = new Set([10, 20]);
|
||||
const filtered: IssueEntity[] = (component as any).filteredIssues;
|
||||
expect(filtered.map((i) => i.id).sort()).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('shows only issues without milestone when showNoMilestone is true', () => {
|
||||
(component as any).showNoMilestone = true;
|
||||
const filtered: IssueEntity[] = (component as any).filteredIssues;
|
||||
expect(filtered.length).toBe(1);
|
||||
expect(filtered[0].id).toBe(3);
|
||||
});
|
||||
|
||||
it('combines milestone selection and no-milestone option as OR', () => {
|
||||
(component as any).selectedMilestoneIds = new Set([10]);
|
||||
(component as any).showNoMilestone = true;
|
||||
const filtered: IssueEntity[] = (component as any).filteredIssues;
|
||||
expect(filtered.map((i) => i.id).sort()).toEqual([1, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleMilestone', () => {
|
||||
it('adds a milestone id when not already selected', () => {
|
||||
(component as any).toggleMilestone(10, mockEvent);
|
||||
expect((component as any).selectedMilestoneIds.has(10)).toBe(true);
|
||||
});
|
||||
|
||||
it('removes a milestone id when already selected', () => {
|
||||
(component as any).selectedMilestoneIds = new Set([10]);
|
||||
(component as any).toggleMilestone(10, mockEvent);
|
||||
expect((component as any).selectedMilestoneIds.has(10)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleNoMilestone', () => {
|
||||
it('sets showNoMilestone to true when false', () => {
|
||||
(component as any).showNoMilestone = false;
|
||||
(component as any).toggleNoMilestone(mockEvent);
|
||||
expect((component as any).showNoMilestone).toBe(true);
|
||||
});
|
||||
|
||||
it('sets showNoMilestone to false when true', () => {
|
||||
(component as any).showNoMilestone = true;
|
||||
(component as any).toggleNoMilestone(mockEvent);
|
||||
expect((component as any).showNoMilestone).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearMilestones', () => {
|
||||
it('clears selected milestone ids and showNoMilestone', () => {
|
||||
(component as any).selectedMilestoneIds = new Set([10, 20]);
|
||||
(component as any).showNoMilestone = true;
|
||||
(component as any).clearMilestones(mockEvent);
|
||||
expect((component as any).selectedMilestoneIds.size).toBe(0);
|
||||
expect((component as any).showNoMilestone).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('milestoneDropdownLabel', () => {
|
||||
it('returns "Milestone" when nothing is selected', () => {
|
||||
expect((component as any).milestoneDropdownLabel()).toBe('Milestone');
|
||||
});
|
||||
|
||||
it('returns "Sans milestone" when only showNoMilestone is true', () => {
|
||||
(component as any).showNoMilestone = true;
|
||||
expect((component as any).milestoneDropdownLabel()).toBe('Sans milestone');
|
||||
});
|
||||
|
||||
it('returns the milestone name when exactly one milestone is selected', () => {
|
||||
milestonesStore.seed([makeMilestone({ id: 10, name: 'Sprint A', issueIds: [] })]);
|
||||
(component as any).selectedMilestoneIds = new Set([10]);
|
||||
expect((component as any).milestoneDropdownLabel()).toBe('Sprint A');
|
||||
});
|
||||
|
||||
it('returns a count when multiple filters are active', () => {
|
||||
milestonesStore.seed([
|
||||
makeMilestone({ id: 10, name: 'Sprint A', issueIds: [] }),
|
||||
makeMilestone({ id: 20, name: 'Sprint B', issueIds: [] }),
|
||||
]);
|
||||
(component as any).selectedMilestoneIds = new Set([10, 20]);
|
||||
expect((component as any).milestoneDropdownLabel()).toBe('Milestone (2)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('milestoneFilterActive', () => {
|
||||
it('is false when nothing is selected', () => {
|
||||
expect((component as any).milestoneFilterActive).toBe(false);
|
||||
});
|
||||
|
||||
it('is true when a milestone id is selected', () => {
|
||||
(component as any).selectedMilestoneIds = new Set([10]);
|
||||
expect((component as any).milestoneFilterActive).toBe(true);
|
||||
});
|
||||
|
||||
it('is true when showNoMilestone is true', () => {
|
||||
(component as any).showNoMilestone = true;
|
||||
expect((component as any).milestoneFilterActive).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('typeBadgeClass', () => {
|
||||
it('maps Bug to text-bg-danger', () => {
|
||||
expect((component as any).typeBadgeClass('Bug')).toBe('text-bg-danger');
|
||||
|
||||
Reference in New Issue
Block a user