feat: add BlockedTriageModal component with tests
- Create BlockedTriageModal component at src/components/shared/blocked-triage-modal.tsx - Implements modal with blocked task triage functionality - Uses deriveBlockedIds and buildBlockedByTree from kanban lib - Each row shows blocker chain and has inline archetype picker - Modal is scrollable and closes via Escape/close button - Add corresponding tests at tests/components/blocked-triage-modal.test.tsx - Register test in package.json test script
This commit is contained in:
parent
c71e3742d9
commit
29eefaf7ec
3 changed files with 263 additions and 1 deletions
73
tests/components/blocked-triage-modal.test.tsx
Normal file
73
tests/components/blocked-triage-modal.test.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
test('BlockedTriageModal - file exists and exports', async () => {
|
||||
const filePath = path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx');
|
||||
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||
assert.ok(fileContent.includes('export function BlockedTriageModal'), 'Should export BlockedTriageModal function');
|
||||
assert.ok(fileContent.includes('export interface BlockedTriageModalProps'), 'Should export BlockedTriageModalProps interface');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - imports Dialog from shadcn', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('from "@/components/ui/dialog"') || fileContent.includes("from '@/components/ui/dialog'"), 'Should import Dialog components from shadcn');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - imports deriveBlockedIds and buildBlockedByTree from kanban', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('deriveBlockedIds'), 'Should import deriveBlockedIds');
|
||||
assert.ok(fileContent.includes('buildBlockedByTree'), 'Should import buildBlockedByTree');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - imports useArchetypePicker hook', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('useArchetypePicker'), 'Should import useArchetypePicker hook');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - imports BeadIssue type', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('BeadIssue'), 'Should import BeadIssue type');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - accepts isOpen, onClose, issues, projectRoot props', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('isOpen:'), 'Should have isOpen prop');
|
||||
assert.ok(fileContent.includes('onClose:'), 'Should have onClose prop');
|
||||
assert.ok(fileContent.includes('issues:'), 'Should have issues prop');
|
||||
assert.ok(fileContent.includes('projectRoot:'), 'Should have projectRoot prop');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - uses useMemo for blockedIds computation', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('useMemo'), 'Should use useMemo for computing blockedIds');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - computes blocked tasks using status=blocked AND deriveBlockedIds', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes("status === 'blocked'"), 'Should check explicit blocked status');
|
||||
assert.ok(fileContent.includes('deriveBlockedIds'), 'Should use deriveBlockedIds');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - uses buildBlockedByTree for blocker chain display', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('buildBlockedByTree'), 'Should use buildBlockedByTree for blocker chains');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - has local state for showing/hiding archetype picker per row', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('useState'), 'Should use useState');
|
||||
assert.ok(fileContent.includes('showPicker') || fileContent.includes('pickerOpen') || fileContent.includes('expandedRow'), 'Should have state for row picker visibility');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - modal content is scrollable', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
assert.ok(fileContent.includes('overflow-y-auto') || fileContent.includes('overflowAuto') || fileContent.includes('scrollable'), 'Should have scrollable content');
|
||||
});
|
||||
|
||||
test('BlockedTriageModal - reuses useArchetypePicker for row assignments', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/blocked-triage-modal.tsx'), 'utf-8');
|
||||
const useArchetypePickerCount = (fileContent.match(/useArchetypePicker/g) || []).length;
|
||||
assert.ok(useArchetypePickerCount >= 2, 'Should use useArchetypePicker hook');
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue