feat: CLI & MCP progress tracking for parse-prd command (#1048)
* initial cutover * update log to debug * update tracker to pass units * update test to match new base tracker format * add streamTextService mocks * remove unused imports * Ensure the CLI waits for async main() completion * refactor to reduce code duplication * update comment * reuse function * ensure targetTag is defined in streaming mode * avoid throwing inside process.exit spy * check for null * remove reference to generate * fix formatting * fix textStream assignment * ensure no division by 0 * fix jest chalk mocks * refactor for maintainability * Improve bar chart calculation logic for consistent visual representation * use custom streaming error types; fix mocks * Update streamText extraction in parse-prd.js to match actual service response * remove check - doesn't belong here * update mocks * remove streaming test that wasn't really doing anything * add comment * make parsing logic more DRY * fix formatting * Fix textStream extraction to match actual service response * fix mock * Add a cleanup method to ensure proper resource disposal and prevent memory leaks * debounce progress updates to reduce UI flicker during rapid updates * Implement timeout protection for streaming operations (60-second timeout) with automatic fallback to non-streaming mode. * clear timeout properly * Add a maximum buffer size limit (1MB) to prevent unbounded memory growth with very large streaming responses. * fix formatting * remove duplicate mock * better docs * fix formatting * sanitize the dynamic property name * Fix incorrect remaining progress calculation * Use onError callback instead of console.warn * Remove unused chalk import * Add missing custom validator in fallback parsing configuration * add custom validator parameter in fallback parsing * chore: fix package-lock.json * chore: large code refactor * chore: increase timeout from 1 minute to 3 minutes * fix: refactor and fix streaming * Merge remote-tracking branch 'origin/next' into joedanz/parse-prd-progress * fix: cleanup and fix unit tests * chore: fix unit tests * chore: fix format * chore: run format * chore: fix weird CI unit test error * chore: fix format --------- Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
This commit is contained in:
134
tests/unit/progress/base-progress-tracker.test.js
Normal file
134
tests/unit/progress/base-progress-tracker.test.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
// Mock cli-progress factory before importing BaseProgressTracker
|
||||
jest.unstable_mockModule(
|
||||
'../../../src/progress/cli-progress-factory.js',
|
||||
() => ({
|
||||
newMultiBar: jest.fn(() => ({
|
||||
create: jest.fn(() => ({
|
||||
update: jest.fn()
|
||||
})),
|
||||
stop: jest.fn()
|
||||
}))
|
||||
})
|
||||
);
|
||||
|
||||
const { newMultiBar } = await import(
|
||||
'../../../src/progress/cli-progress-factory.js'
|
||||
);
|
||||
const { BaseProgressTracker } = await import(
|
||||
'../../../src/progress/base-progress-tracker.js'
|
||||
);
|
||||
|
||||
describe('BaseProgressTracker', () => {
|
||||
let tracker;
|
||||
let mockMultiBar;
|
||||
let mockProgressBar;
|
||||
let mockTimeTokensBar;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useFakeTimers();
|
||||
|
||||
// Setup mocks
|
||||
mockProgressBar = { update: jest.fn() };
|
||||
mockTimeTokensBar = { update: jest.fn() };
|
||||
mockMultiBar = {
|
||||
create: jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(mockTimeTokensBar)
|
||||
.mockReturnValueOnce(mockProgressBar),
|
||||
stop: jest.fn()
|
||||
};
|
||||
newMultiBar.mockReturnValue(mockMultiBar);
|
||||
|
||||
tracker = new BaseProgressTracker({ numUnits: 10, unitName: 'task' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe('cleanup', () => {
|
||||
it('should stop and clear timer interval', () => {
|
||||
tracker.start();
|
||||
expect(tracker._timerInterval).toBeTruthy();
|
||||
|
||||
tracker.cleanup();
|
||||
expect(tracker._timerInterval).toBeNull();
|
||||
});
|
||||
|
||||
it('should stop and null multibar reference', () => {
|
||||
tracker.start();
|
||||
expect(tracker.multibar).toBeTruthy();
|
||||
|
||||
tracker.cleanup();
|
||||
expect(mockMultiBar.stop).toHaveBeenCalled();
|
||||
expect(tracker.multibar).toBeNull();
|
||||
});
|
||||
|
||||
it('should null progress bar references', () => {
|
||||
tracker.start();
|
||||
expect(tracker.timeTokensBar).toBeTruthy();
|
||||
expect(tracker.progressBar).toBeTruthy();
|
||||
|
||||
tracker.cleanup();
|
||||
expect(tracker.timeTokensBar).toBeNull();
|
||||
expect(tracker.progressBar).toBeNull();
|
||||
});
|
||||
|
||||
it('should set finished state', () => {
|
||||
tracker.start();
|
||||
expect(tracker.isStarted).toBe(true);
|
||||
expect(tracker.isFinished).toBe(false);
|
||||
|
||||
tracker.cleanup();
|
||||
expect(tracker.isStarted).toBe(false);
|
||||
expect(tracker.isFinished).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle cleanup when multibar.stop throws error', () => {
|
||||
tracker.start();
|
||||
mockMultiBar.stop.mockImplementation(() => {
|
||||
throw new Error('Stop failed');
|
||||
});
|
||||
|
||||
expect(() => tracker.cleanup()).not.toThrow();
|
||||
expect(tracker.multibar).toBeNull();
|
||||
});
|
||||
|
||||
it('should be safe to call multiple times', () => {
|
||||
tracker.start();
|
||||
|
||||
tracker.cleanup();
|
||||
tracker.cleanup();
|
||||
tracker.cleanup();
|
||||
|
||||
expect(mockMultiBar.stop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should be safe to call without starting', () => {
|
||||
expect(() => tracker.cleanup()).not.toThrow();
|
||||
expect(tracker.multibar).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('stop vs cleanup', () => {
|
||||
it('stop should call cleanup and null multibar reference', () => {
|
||||
tracker.start();
|
||||
tracker.stop();
|
||||
|
||||
// stop() now calls cleanup() which nulls the multibar
|
||||
expect(tracker.multibar).toBeNull();
|
||||
expect(tracker.isFinished).toBe(true);
|
||||
});
|
||||
|
||||
it('cleanup should null multibar preventing getSummary', () => {
|
||||
tracker.start();
|
||||
tracker.cleanup();
|
||||
|
||||
expect(tracker.multibar).toBeNull();
|
||||
expect(tracker.isFinished).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user