@memberjunction/ng-tasks 5.43.0 → 5.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +22 -0
- package/package.json +8 -7
- package/src/lib/components/simple-task-viewer.component.dom.test.ts +74 -0
- package/src/lib/components/task-detail-panel.component.dom.test.ts +90 -0
- package/src/lib/components/task.component.dom.test.ts +72 -0
- package/tsconfig.spec.json +13 -0
- package/vitest.config.ts +7 -4
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.js +0 -7
- package/dist/__tests__/index.test.js.map +0 -1
- package/dist/__tests__/task-models.test.d.ts +0 -2
- package/dist/__tests__/task-models.test.d.ts.map +0 -1
- package/dist/__tests__/task-models.test.js +0 -49
- package/dist/__tests__/task-models.test.js.map +0 -1
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @memberjunction/ng-tasks
|
|
2
2
|
|
|
3
|
+
## 5.44.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [3633fbb]
|
|
8
|
+
- Updated dependencies [1367fbb]
|
|
9
|
+
- Updated dependencies [5396d90]
|
|
10
|
+
- Updated dependencies [7279819]
|
|
11
|
+
- Updated dependencies [d44e430]
|
|
12
|
+
- Updated dependencies [6f74b17]
|
|
13
|
+
- Updated dependencies [be5ab50]
|
|
14
|
+
- Updated dependencies [aa9102d]
|
|
15
|
+
- Updated dependencies [2f926df]
|
|
16
|
+
- Updated dependencies [863a10d]
|
|
17
|
+
- Updated dependencies [2f9b863]
|
|
18
|
+
- @memberjunction/ai-engine-base@5.44.0
|
|
19
|
+
- @memberjunction/ai-core-plus@5.44.0
|
|
20
|
+
- @memberjunction/core-entities@5.44.0
|
|
21
|
+
- @memberjunction/core@5.44.0
|
|
22
|
+
- @memberjunction/global@5.44.0
|
|
23
|
+
- @memberjunction/ng-shared-generic@5.44.0
|
|
24
|
+
|
|
3
25
|
## 5.43.0
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-tasks",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.44.0",
|
|
4
4
|
"description": "MemberJunction: Angular components for task visualization and management with Gantt chart support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"@angular/common": "21.1.3",
|
|
16
16
|
"@angular/core": "21.1.3",
|
|
17
17
|
"@angular/platform-browser": "21.1.3",
|
|
18
|
-
"@memberjunction/ai-core-plus": "5.
|
|
19
|
-
"@memberjunction/ai-engine-base": "5.
|
|
20
|
-
"@memberjunction/core": "5.
|
|
21
|
-
"@memberjunction/core-entities": "5.
|
|
22
|
-
"@memberjunction/global": "5.
|
|
23
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
18
|
+
"@memberjunction/ai-core-plus": "5.44.0",
|
|
19
|
+
"@memberjunction/ai-engine-base": "5.44.0",
|
|
20
|
+
"@memberjunction/core": "5.44.0",
|
|
21
|
+
"@memberjunction/core-entities": "5.44.0",
|
|
22
|
+
"@memberjunction/global": "5.44.0",
|
|
23
|
+
"@memberjunction/ng-shared-generic": "5.44.0",
|
|
24
24
|
"dhtmlx-gantt": "^9.1.1",
|
|
25
25
|
"rxjs": "^7.8.2"
|
|
26
26
|
},
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"url": "https://github.com/MemberJunction/MJ"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
+
"@memberjunction/ng-test-utils": "5.44.0",
|
|
38
39
|
"vitest": "^4.0.18"
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderComponentFixture, query, queryAll, text, hasClass, click, capture } from '@memberjunction/ng-test-utils';
|
|
3
|
+
import { SimpleTaskViewerComponent } from './simple-task-viewer.component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* DOM-level spec for <mj-simple-task-viewer>. tasks/@Input is MJTaskEntity[]; the template
|
|
7
|
+
* only reads plain fields, so plain objects via `inputs` suffice.
|
|
8
|
+
*
|
|
9
|
+
* Clicking a task sets selectedTask and renders the child <mj-task-detail-panel>. That child's
|
|
10
|
+
* ngOnInit only touches AIEngineBase when task.AgentID is set, so all task fixtures here OMIT
|
|
11
|
+
* AgentID — no engine/backend call. selectedTask is set via a real (click) DOM event, which
|
|
12
|
+
* marks the view dirty the zoneless-correct way (guide §5), so the follow-up render is clean.
|
|
13
|
+
*/
|
|
14
|
+
describe('SimpleTaskViewerComponent (DOM)', () => {
|
|
15
|
+
const tasks = [
|
|
16
|
+
{ ID: 'a1b2c3d4-0000-0000-0000-000000000001', Name: 'First task', Status: 'In Progress', PercentComplete: 30 },
|
|
17
|
+
{ ID: 'a1b2c3d4-0000-0000-0000-000000000002', Name: 'Second task', Status: 'Complete', PercentComplete: 100 },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
it('renders one row per task with its title', () => {
|
|
21
|
+
const f = renderComponentFixture(SimpleTaskViewerComponent, { inputs: { tasks } });
|
|
22
|
+
const items = queryAll(f, '.task-item');
|
|
23
|
+
expect(items.length).toBe(2);
|
|
24
|
+
const titles = queryAll(f, '.task-title').map((el) => el.textContent?.trim());
|
|
25
|
+
expect(titles[0]).toContain('First task');
|
|
26
|
+
expect(titles[1]).toContain('Second task');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('marks the completed task with the completed class and check icon', () => {
|
|
30
|
+
const f = renderComponentFixture(SimpleTaskViewerComponent, { inputs: { tasks } });
|
|
31
|
+
const items = queryAll(f, '.task-item');
|
|
32
|
+
expect(items[0].classList.contains('completed')).toBe(false);
|
|
33
|
+
expect(items[1].classList.contains('completed')).toBe(true);
|
|
34
|
+
// completed-check icon only renders for the Complete task
|
|
35
|
+
expect(items[0].querySelector('.completed-check')).toBeNull();
|
|
36
|
+
expect(items[1].querySelector('.completed-check')).not.toBeNull();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders the compact progress fill width from PercentComplete', () => {
|
|
40
|
+
const f = renderComponentFixture(SimpleTaskViewerComponent, { inputs: { tasks } });
|
|
41
|
+
const fills = queryAll(f, '.progress-fill-compact') as HTMLElement[];
|
|
42
|
+
expect(fills[0].style.width).toBe('30%');
|
|
43
|
+
expect(fills[1].style.width).toBe('100%');
|
|
44
|
+
// the completed task's fill carries the .complete modifier
|
|
45
|
+
expect(fills[1].classList.contains('complete')).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('shows the empty state when there are no tasks', () => {
|
|
49
|
+
const f = renderComponentFixture(SimpleTaskViewerComponent, { inputs: { tasks: [] } });
|
|
50
|
+
expect(query(f, '.no-tasks')).not.toBeNull();
|
|
51
|
+
expect(text(f, '.no-tasks p')).toBe('No tasks to display');
|
|
52
|
+
expect(queryAll(f, '.task-item').length).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('emits taskClicked and selects the row when a task is clicked', () => {
|
|
56
|
+
const f = renderComponentFixture(SimpleTaskViewerComponent, { inputs: { tasks } });
|
|
57
|
+
const clicked = capture(f.componentInstance.taskClicked);
|
|
58
|
+
|
|
59
|
+
click(f, '.task-item'); // first row
|
|
60
|
+
f.detectChanges(); // re-render after click set selectedTask (view already marked dirty by the event)
|
|
61
|
+
|
|
62
|
+
expect(clicked).toHaveLength(1);
|
|
63
|
+
expect(clicked[0].Name).toBe('First task');
|
|
64
|
+
// selecting a task adds .selected to the first row and reveals the detail panel
|
|
65
|
+
expect(queryAll(f, '.task-item')[0].classList.contains('selected')).toBe(true);
|
|
66
|
+
expect(query(f, 'mj-task-detail-panel')).not.toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('does not render the detail panel before any task is selected', () => {
|
|
70
|
+
const f = renderComponentFixture(SimpleTaskViewerComponent, { inputs: { tasks } });
|
|
71
|
+
expect(query(f, 'mj-task-detail-panel')).toBeNull();
|
|
72
|
+
expect(hasClass(f, '.task-list', 'with-detail')).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderComponentFixture, query, queryAll, text, click, capture } from '@memberjunction/ng-test-utils';
|
|
3
|
+
import { TaskDetailPanelComponent } from './task-detail-panel.component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* DOM-level spec for <mj-task-detail-panel>. The @Input() task is typed as MJTaskEntity,
|
|
7
|
+
* but the template only reads plain fields (Name, Description, Status, PercentComplete,
|
|
8
|
+
* dates, User) — a plain object via `inputs` is enough (see slot specs in conversations).
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: ngOnInit/ngOnChanges call loadAgentInfo(), which only touches AIEngineBase
|
|
11
|
+
* when task.AgentID is set. Every task fixture here OMITS AgentID, so loadAgentInfo
|
|
12
|
+
* returns early and no engine/backend call happens — the agent section stays hidden.
|
|
13
|
+
*/
|
|
14
|
+
describe('TaskDetailPanelComponent (DOM)', () => {
|
|
15
|
+
const baseTask = { Name: 'Build report', Status: 'In Progress' };
|
|
16
|
+
|
|
17
|
+
it('renders the task name and status', () => {
|
|
18
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, { inputs: { task: baseTask } });
|
|
19
|
+
expect(text(f, '.detail-header h3')).toBe('Build report');
|
|
20
|
+
// Status is always shown (no @if gate); find the field whose label is "Status".
|
|
21
|
+
const fields = queryAll(f, '.detail-field');
|
|
22
|
+
const statusField = fields.find((el) => el.querySelector('label')?.textContent?.trim() === 'Status');
|
|
23
|
+
expect(statusField?.querySelector('p')?.textContent?.trim()).toBe('In Progress');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('hides the Description field when task has no description', () => {
|
|
27
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, { inputs: { task: baseTask } });
|
|
28
|
+
const labels = queryAll(f, '.detail-field label').map((el) => el.textContent?.trim());
|
|
29
|
+
expect(labels).not.toContain('Description');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('shows the Description field when task has a description', () => {
|
|
33
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, {
|
|
34
|
+
inputs: { task: { ...baseTask, Description: 'Quarterly numbers' } },
|
|
35
|
+
});
|
|
36
|
+
const labels = queryAll(f, '.detail-field label').map((el) => el.textContent?.trim());
|
|
37
|
+
expect(labels).toContain('Description');
|
|
38
|
+
const descField = queryAll(f, '.detail-field').find((el) => el.querySelector('label')?.textContent?.trim() === 'Description');
|
|
39
|
+
expect(descField?.querySelector('p')?.textContent?.trim()).toBe('Quarterly numbers');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders the progress fill width from PercentComplete', () => {
|
|
43
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, {
|
|
44
|
+
inputs: { task: { ...baseTask, PercentComplete: 42 } },
|
|
45
|
+
});
|
|
46
|
+
const fill = query(f, '.progress-fill-detail') as HTMLElement;
|
|
47
|
+
expect(fill).not.toBeNull();
|
|
48
|
+
expect(fill.style.width).toBe('42%');
|
|
49
|
+
expect(text(f, '.detail-progress span')).toBe('42%');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('hides the progress field when PercentComplete is null', () => {
|
|
53
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, {
|
|
54
|
+
inputs: { task: { ...baseTask, PercentComplete: null } },
|
|
55
|
+
});
|
|
56
|
+
expect(query(f, '.detail-progress')).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('does not render the agent section when there is no agent', () => {
|
|
60
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, { inputs: { task: baseTask } });
|
|
61
|
+
expect(query(f, '.agent-info')).toBeNull();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('shows the agent-run link when agentRunId is set, and emits openEntityRecord on click', () => {
|
|
65
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, {
|
|
66
|
+
inputs: { task: baseTask, agentRunId: 'run-123' },
|
|
67
|
+
});
|
|
68
|
+
const link = query(f, '.agent-run-link');
|
|
69
|
+
expect(link).not.toBeNull();
|
|
70
|
+
|
|
71
|
+
const emitted = capture(f.componentInstance.openEntityRecord);
|
|
72
|
+
click(f, '.agent-run-link');
|
|
73
|
+
expect(emitted).toHaveLength(1);
|
|
74
|
+
expect(emitted[0]).toEqual({ entityName: 'MJ: AI Agent Runs', recordId: 'run-123' });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('hides the agent-run link when agentRunId is null', () => {
|
|
78
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, {
|
|
79
|
+
inputs: { task: baseTask, agentRunId: null },
|
|
80
|
+
});
|
|
81
|
+
expect(query(f, '.agent-run-link')).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('emits closePanel when the close button is clicked', () => {
|
|
85
|
+
const f = renderComponentFixture(TaskDetailPanelComponent, { inputs: { task: baseTask } });
|
|
86
|
+
const closed = capture(f.componentInstance.closePanel);
|
|
87
|
+
click(f, '.close-detail-btn');
|
|
88
|
+
expect(closed).toHaveLength(1);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderComponentFixture, query, queryAll, text, capture } from '@memberjunction/ng-test-utils';
|
|
3
|
+
import { TaskComponent } from './task.component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* DOM-level spec for <mj-task> — the compound component that hosts the header/view-toggle
|
|
7
|
+
* and switches between the Simple and Gantt viewers.
|
|
8
|
+
*
|
|
9
|
+
* We test ONLY in the default 'simple' viewMode. Switching to 'gantt' renders
|
|
10
|
+
* <mj-gantt-task-viewer>, which boots the dhtmlx-gantt canvas library in ngAfterViewInit
|
|
11
|
+
* (not jsdom-friendly) — that path is deferred. So we assert the toggle's wiring via the
|
|
12
|
+
* "List" button (which keeps viewMode === 'simple') and the @Output, never by rendering gantt.
|
|
13
|
+
*/
|
|
14
|
+
describe('TaskComponent (DOM)', () => {
|
|
15
|
+
it('renders the header with title and description when showHeader is true', () => {
|
|
16
|
+
const f = renderComponentFixture(TaskComponent, {
|
|
17
|
+
inputs: { tasks: [], title: 'My Work', description: 'Things to do' },
|
|
18
|
+
});
|
|
19
|
+
expect(query(f, '.task-header')).not.toBeNull();
|
|
20
|
+
expect(text(f, '.task-title')).toBe('My Work');
|
|
21
|
+
expect(text(f, '.task-description')).toBe('Things to do');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('hides the header entirely when showHeader is false', () => {
|
|
25
|
+
const f = renderComponentFixture(TaskComponent, {
|
|
26
|
+
inputs: { tasks: [], showHeader: false, title: 'Hidden' },
|
|
27
|
+
});
|
|
28
|
+
expect(query(f, '.task-header')).toBeNull();
|
|
29
|
+
expect(query(f, '.task-title')).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('omits the title element when no title is provided', () => {
|
|
33
|
+
const f = renderComponentFixture(TaskComponent, { inputs: { tasks: [] } });
|
|
34
|
+
expect(query(f, '.task-header')).not.toBeNull();
|
|
35
|
+
expect(query(f, '.task-title')).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders the view toggle by default and marks the List button active in simple mode', () => {
|
|
39
|
+
const f = renderComponentFixture(TaskComponent, { inputs: { tasks: [] } });
|
|
40
|
+
const buttons = queryAll(f, '.toggle-btn');
|
|
41
|
+
expect(buttons.length).toBe(2);
|
|
42
|
+
const [ganttBtn, listBtn] = buttons;
|
|
43
|
+
// default viewMode === 'simple' → List active, Gantt not
|
|
44
|
+
expect(listBtn.classList.contains('active')).toBe(true);
|
|
45
|
+
expect(ganttBtn.classList.contains('active')).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('hides the view toggle when showViewToggle is false', () => {
|
|
49
|
+
const f = renderComponentFixture(TaskComponent, { inputs: { tasks: [], showViewToggle: false } });
|
|
50
|
+
expect(query(f, '.view-toggle')).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders the simple viewer in the default simple mode', () => {
|
|
54
|
+
const f = renderComponentFixture(TaskComponent, { inputs: { tasks: [] } });
|
|
55
|
+
expect(query(f, 'mj-simple-task-viewer')).not.toBeNull();
|
|
56
|
+
expect(query(f, 'mj-gantt-task-viewer')).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('emits viewModeChanged when the (already-active) List toggle is clicked, staying in simple mode', () => {
|
|
60
|
+
const f = renderComponentFixture(TaskComponent, { inputs: { tasks: [] } });
|
|
61
|
+
const changed = capture(f.componentInstance.viewModeChanged);
|
|
62
|
+
|
|
63
|
+
// second toggle button is "List" → setViewMode('simple'); keeps us out of the gantt render path
|
|
64
|
+
const listBtn = queryAll(f, '.toggle-btn')[1] as HTMLButtonElement;
|
|
65
|
+
listBtn.click();
|
|
66
|
+
f.detectChanges();
|
|
67
|
+
|
|
68
|
+
expect(changed).toHaveLength(1);
|
|
69
|
+
expect(changed[0]).toBe('simple');
|
|
70
|
+
expect(query(f, 'mj-gantt-task-viewer')).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
});
|
package/vitest.config.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { defineProject, mergeConfig } from 'vitest/config';
|
|
2
|
-
import
|
|
2
|
+
import domSharedConfig from '../../../../vitest.dom.shared';
|
|
3
3
|
|
|
4
|
+
// Single DOM preset: this package has no class-level spec that vi.mock('@angular/core'),
|
|
5
|
+
// so the existing node/logic specs (index.test.ts, task-models.test.ts) run fine under
|
|
6
|
+
// jsdom alongside the DOM component specs. See guides/ANGULAR_TESTING_GUIDE.md §3b.
|
|
4
7
|
export default mergeConfig(
|
|
5
|
-
|
|
8
|
+
domSharedConfig,
|
|
6
9
|
defineProject({
|
|
7
10
|
test: {
|
|
8
|
-
|
|
11
|
+
name: '@memberjunction/ng-tasks',
|
|
9
12
|
},
|
|
10
|
-
})
|
|
13
|
+
}),
|
|
11
14
|
);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\n\ndescribe('@memberjunction/ng-tasks', () => {\n it('should have a passing test', () => {\n expect(true).toBe(true);\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"task-models.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/task-models.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
describe('TaskViewMode', () => {
|
|
3
|
-
it('should accept simple mode', () => {
|
|
4
|
-
const mode = 'simple';
|
|
5
|
-
expect(mode).toBe('simple');
|
|
6
|
-
});
|
|
7
|
-
it('should accept gantt mode', () => {
|
|
8
|
-
const mode = 'gantt';
|
|
9
|
-
expect(mode).toBe('gantt');
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
describe('GanttTask', () => {
|
|
13
|
-
it('should construct a basic gantt task', () => {
|
|
14
|
-
const task = {
|
|
15
|
-
id: 'task-1',
|
|
16
|
-
name: 'Design Phase',
|
|
17
|
-
start: '2026-01-01',
|
|
18
|
-
end: '2026-01-15',
|
|
19
|
-
progress: 75
|
|
20
|
-
};
|
|
21
|
-
expect(task.id).toBe('task-1');
|
|
22
|
-
expect(task.progress).toBe(75);
|
|
23
|
-
expect(task.dependencies).toBeUndefined();
|
|
24
|
-
});
|
|
25
|
-
it('should support dependencies and custom class', () => {
|
|
26
|
-
const task = {
|
|
27
|
-
id: 'task-2',
|
|
28
|
-
name: 'Implementation',
|
|
29
|
-
start: '2026-01-16',
|
|
30
|
-
end: '2026-02-15',
|
|
31
|
-
progress: 30,
|
|
32
|
-
dependencies: 'task-1',
|
|
33
|
-
custom_class: 'critical-task'
|
|
34
|
-
};
|
|
35
|
-
expect(task.dependencies).toBe('task-1');
|
|
36
|
-
expect(task.custom_class).toBe('critical-task');
|
|
37
|
-
});
|
|
38
|
-
it('should handle zero progress', () => {
|
|
39
|
-
const task = {
|
|
40
|
-
id: 'task-3',
|
|
41
|
-
name: 'Future Task',
|
|
42
|
-
start: '2026-03-01',
|
|
43
|
-
end: '2026-03-30',
|
|
44
|
-
progress: 0
|
|
45
|
-
};
|
|
46
|
-
expect(task.progress).toBe(0);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
//# sourceMappingURL=task-models.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"task-models.test.js","sourceRoot":"","sources":["../../src/__tests__/task-models.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG9C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,IAAI,GAAiB,QAAQ,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAiB,OAAO,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,IAAI,GAAc;YACtB,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,YAAY;YACnB,GAAG,EAAE,YAAY;YACjB,QAAQ,EAAE,EAAE;SACb,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,IAAI,GAAc;YACtB,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,YAAY;YACnB,GAAG,EAAE,YAAY;YACjB,QAAQ,EAAE,EAAE;YACZ,YAAY,EAAE,QAAQ;YACtB,YAAY,EAAE,eAAe;SAC9B,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,IAAI,GAAc;YACtB,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,YAAY;YACnB,GAAG,EAAE,YAAY;YACjB,QAAQ,EAAE,CAAC;SACZ,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport type { TaskViewMode, GanttTask } from '../lib/models/task-view.models';\n\ndescribe('TaskViewMode', () => {\n it('should accept simple mode', () => {\n const mode: TaskViewMode = 'simple';\n expect(mode).toBe('simple');\n });\n\n it('should accept gantt mode', () => {\n const mode: TaskViewMode = 'gantt';\n expect(mode).toBe('gantt');\n });\n});\n\ndescribe('GanttTask', () => {\n it('should construct a basic gantt task', () => {\n const task: GanttTask = {\n id: 'task-1',\n name: 'Design Phase',\n start: '2026-01-01',\n end: '2026-01-15',\n progress: 75\n };\n expect(task.id).toBe('task-1');\n expect(task.progress).toBe(75);\n expect(task.dependencies).toBeUndefined();\n });\n\n it('should support dependencies and custom class', () => {\n const task: GanttTask = {\n id: 'task-2',\n name: 'Implementation',\n start: '2026-01-16',\n end: '2026-02-15',\n progress: 30,\n dependencies: 'task-1',\n custom_class: 'critical-task'\n };\n expect(task.dependencies).toBe('task-1');\n expect(task.custom_class).toBe('critical-task');\n });\n\n it('should handle zero progress', () => {\n const task: GanttTask = {\n id: 'task-3',\n name: 'Future Task',\n start: '2026-03-01',\n end: '2026-03-30',\n progress: 0\n };\n expect(task.progress).toBe(0);\n });\n});\n"]}
|