Merge pull request 'Milestone et Epic pour les tache commentaire' (#35) from feat/51-release-depuis-creation-issue-commentaire into develop
Reviewed-on: Bonsai/Bonsai-webapp#35
This commit is contained in:
@@ -4,6 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
import { afterEach, vi } from 'vitest';
|
import { afterEach, vi } from 'vitest';
|
||||||
import { IssueComments } from './issue-comments';
|
import { IssueComments } from './issue-comments';
|
||||||
import { IssueEntity, IssuesStore } from '../issues.store';
|
import { IssueEntity, IssuesStore } from '../issues.store';
|
||||||
|
import { MilestoneEntity, MilestonesStore } from '../../milestones/milestones.store';
|
||||||
|
|
||||||
const makeIssue = (overrides: Partial<IssueEntity> = {}): IssueEntity => ({
|
const makeIssue = (overrides: Partial<IssueEntity> = {}): IssueEntity => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -81,16 +82,60 @@ class FakeIssuesStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const makeMilestone = (overrides: Partial<MilestoneEntity> = {}): MilestoneEntity => ({
|
||||||
|
id: 1,
|
||||||
|
name: 'Sprint 1',
|
||||||
|
description: '',
|
||||||
|
startDate: '',
|
||||||
|
endDate: '',
|
||||||
|
dueDate: '',
|
||||||
|
issueIds: [],
|
||||||
|
dependsOnIds: [],
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
|
||||||
|
class FakeMilestonesStore {
|
||||||
|
private _data = signal<MilestoneEntity[]>([]);
|
||||||
|
|
||||||
|
readonly milestones = this._data.asReadonly();
|
||||||
|
readonly loading = signal(false);
|
||||||
|
readonly loaded = signal(true);
|
||||||
|
|
||||||
|
getById(id: number): MilestoneEntity | undefined {
|
||||||
|
return this._data().find((m) => m.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('IssueComments', () => {
|
describe('IssueComments', () => {
|
||||||
let component: IssueComments;
|
let component: IssueComments;
|
||||||
let fixture: ComponentFixture<IssueComments>;
|
let fixture: ComponentFixture<IssueComments>;
|
||||||
let store: FakeIssuesStore;
|
let store: FakeIssuesStore;
|
||||||
|
let milestonesStore: FakeMilestonesStore;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
store = new FakeIssuesStore();
|
store = new FakeIssuesStore();
|
||||||
|
milestonesStore = new FakeMilestonesStore();
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [IssueComments, RouterTestingModule],
|
imports: [IssueComments, RouterTestingModule],
|
||||||
providers: [{ provide: IssuesStore, useValue: store }],
|
providers: [
|
||||||
|
{ provide: IssuesStore, useValue: store },
|
||||||
|
{ provide: MilestonesStore, useValue: milestonesStore },
|
||||||
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(IssueComments);
|
fixture = TestBed.createComponent(IssueComments);
|
||||||
@@ -394,6 +439,46 @@ describe('IssueComments', () => {
|
|||||||
|
|
||||||
expect(store.issues().filter((i) => i.type === 'Task').length).toBe(0);
|
expect(store.issues().filter((i) => i.type === 'Task').length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('inherits the epic from the parent issue', async () => {
|
||||||
|
await store.upsert(makeIssue({ id: 1, epic: 'Mon Epic', comments: store.getById(1)!.comments }));
|
||||||
|
const commentId = store.getById(1)!.comments[0].id;
|
||||||
|
(component as any).newTaskName = 'Tâche avec epic';
|
||||||
|
await (component as any).createTaskForComment(commentId);
|
||||||
|
|
||||||
|
const task = store.issues().find((i) => i.type === 'Task');
|
||||||
|
expect(task?.epic).toBe('Mon Epic');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not set epic when parent issue has none', async () => {
|
||||||
|
const commentId = store.getById(1)!.comments[0].id;
|
||||||
|
(component as any).newTaskName = 'Tâche sans epic';
|
||||||
|
await (component as any).createTaskForComment(commentId);
|
||||||
|
|
||||||
|
const task = store.issues().find((i) => i.type === 'Task');
|
||||||
|
expect(task?.epic).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds the new task to the same milestone as the parent issue', async () => {
|
||||||
|
await milestonesStore.upsert(makeMilestone({ id: 10, name: 'Release 1', issueIds: [1] }));
|
||||||
|
const commentId = store.getById(1)!.comments[0].id;
|
||||||
|
(component as any).newTaskName = 'Tâche dans milestone';
|
||||||
|
await (component as any).createTaskForComment(commentId);
|
||||||
|
|
||||||
|
const task = store.issues().find((i) => i.type === 'Task')!;
|
||||||
|
expect(milestonesStore.getById(10)!.issueIds).toContain(task.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not modify any milestone when parent issue is not in one', async () => {
|
||||||
|
await milestonesStore.upsert(makeMilestone({ id: 10, name: 'Release 1', issueIds: [99] }));
|
||||||
|
const commentId = store.getById(1)!.comments[0].id;
|
||||||
|
(component as any).newTaskName = 'Tâche hors milestone';
|
||||||
|
await (component as any).createTaskForComment(commentId);
|
||||||
|
|
||||||
|
expect(milestonesStore.getById(10)!.issueIds).not.toContain(
|
||||||
|
store.issues().find((i) => i.type === 'Task')!.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('linkIssueToComment', () => {
|
describe('linkIssueToComment', () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { marked } from 'marked';
|
|||||||
import { handleImagePaste, insertAtSelection } from '../paste-image.util';
|
import { handleImagePaste, insertAtSelection } from '../paste-image.util';
|
||||||
import { IssueComment, IssueEntity, IssuesStore } from '../issues.store';
|
import { IssueComment, IssueEntity, IssuesStore } from '../issues.store';
|
||||||
import { StatusEntity, StatusesStore } from '../../settings/statuses/statuses.store';
|
import { StatusEntity, StatusesStore } from '../../settings/statuses/statuses.store';
|
||||||
|
import { MilestonesStore } from '../../milestones/milestones.store';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-issue-comments',
|
selector: 'app-issue-comments',
|
||||||
@@ -15,6 +16,7 @@ import { StatusEntity, StatusesStore } from '../../settings/statuses/statuses.st
|
|||||||
})
|
})
|
||||||
export class IssueComments {
|
export class IssueComments {
|
||||||
private readonly issuesStore = inject(IssuesStore);
|
private readonly issuesStore = inject(IssuesStore);
|
||||||
|
private readonly milestonesStore = inject(MilestonesStore);
|
||||||
private readonly sanitizer = inject(DomSanitizer);
|
private readonly sanitizer = inject(DomSanitizer);
|
||||||
private readonly statusesStore = inject(StatusesStore);
|
private readonly statusesStore = inject(StatusesStore);
|
||||||
|
|
||||||
@@ -169,7 +171,7 @@ export class IssueComments {
|
|||||||
type: 'Task',
|
type: 'Task',
|
||||||
name,
|
name,
|
||||||
assignee: '',
|
assignee: '',
|
||||||
epic: '',
|
epic: issue.epic,
|
||||||
startDate: '',
|
startDate: '',
|
||||||
startDateMode: 'forced',
|
startDateMode: 'forced',
|
||||||
endDate: '',
|
endDate: '',
|
||||||
@@ -189,6 +191,12 @@ export class IssueComments {
|
|||||||
return { ...c, linkedIssueIds: [...c.linkedIssueIds, created.id] };
|
return { ...c, linkedIssueIds: [...c.linkedIssueIds, created.id] };
|
||||||
});
|
});
|
||||||
await this.issuesStore.upsert({ ...issue, comments: updatedComments });
|
await this.issuesStore.upsert({ ...issue, comments: updatedComments });
|
||||||
|
|
||||||
|
const milestone = this.milestonesStore.milestones().find((m) => m.issueIds.includes(issue.id));
|
||||||
|
if (milestone) {
|
||||||
|
await this.milestonesStore.upsert({ ...milestone, issueIds: [...milestone.issueIds, created.id] });
|
||||||
|
}
|
||||||
|
|
||||||
this.creatingTaskForCommentId = null;
|
this.creatingTaskForCommentId = null;
|
||||||
this.newTaskName = '';
|
this.newTaskName = '';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user