import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideMarkdown } from 'ngx-markdown';
import { RequestDisplayComponent } from './request-display.component';
import { QueuedRequest } from '@ask-me-mcp/askme-shared';
describe('RequestDisplayComponent', () => {
let component: RequestDisplayComponent;
let fixture: ComponentFixture<RequestDisplayComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RequestDisplayComponent],
providers: [
provideHttpClient(),
provideMarkdown()
]
}).compileComponents();
fixture = TestBed.createComponent(RequestDisplayComponent);
component = fixture.componentInstance;
});
it('should create', () => {
component.request = createMockRequest();
fixture.detectChanges();
expect(component).toBeTruthy();
});
describe('hasContext', () => {
it('should return true when context has properties', () => {
component.request = createMockRequest({
context: { foo: 'bar' }
});
expect(component.hasContext).toBe(true);
});
it('should return false when context is empty', () => {
component.request = createMockRequest({
context: {}
});
expect(component.hasContext).toBe(false);
});
it('should return false when context is undefined', () => {
component.request = createMockRequest({
context: undefined
});
expect(component.hasContext).toBe(false);
});
});
describe('formatTime', () => {
beforeEach(() => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01T12:00:00Z'));
});
afterEach(() => {
jest.useRealTimers();
});
it('should format as "just now" for recent times', () => {
const timestamp = new Date('2024-01-01T11:59:30Z');
expect(component.formatTime(timestamp)).toBe('just now');
});
it('should format as minutes ago', () => {
const timestamp = new Date('2024-01-01T11:45:00Z');
expect(component.formatTime(timestamp)).toBe('15m ago');
});
it('should format as hours ago', () => {
const timestamp = new Date('2024-01-01T09:00:00Z');
expect(component.formatTime(timestamp)).toBe('3h ago');
});
it('should format as date for old timestamps', () => {
const timestamp = new Date('2023-12-25T12:00:00Z');
expect(component.formatTime(timestamp)).toBe('12/25/2023');
});
it('should handle string timestamps', () => {
const timestamp = '2024-01-01T11:59:30Z';
expect(component.formatTime(timestamp)).toBe('just now');
});
});
describe('formatContext', () => {
it('should format context as JSON', () => {
const context = {
foo: 'bar',
num: 42,
nested: { key: 'value' }
};
const formatted = component.formatContext(context);
expect(formatted).toContain('"foo": "bar"');
expect(formatted).toContain('"num": 42');
expect(formatted).toContain('"nested"');
});
});
describe('template', () => {
beforeEach(() => {
component.request = createMockRequest({
id: 'req-123',
question: 'Should we deploy?',
status: 'active',
timestamp: new Date('2024-01-01T12:00:00Z')
});
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01T12:05:00Z'));
fixture.detectChanges();
});
afterEach(() => {
jest.useRealTimers();
});
it('should display request ID', () => {
const idElement = fixture.nativeElement.querySelector('.request-id');
expect(idElement.textContent).toBe('req-123');
});
it('should display question', () => {
const questionElement = fixture.nativeElement.querySelector('.request-question');
expect(questionElement.textContent).toBe('Should we deploy?');
});
it('should display formatted time', () => {
const timeElement = fixture.nativeElement.querySelector('.request-time');
expect(timeElement.textContent.trim()).toBe('5m ago');
});
it('should display status', () => {
const statusElement = fixture.nativeElement.querySelector('.request-status');
expect(statusElement.textContent.trim()).toBe('active');
expect(statusElement.classList.contains('status-active')).toBe(true);
});
it('should add active class for active requests', () => {
const card = fixture.nativeElement.querySelector('.request-card');
expect(card.classList.contains('active')).toBe(true);
});
it('should show context when available', () => {
component.request = createMockRequest({
context: { environment: 'production' }
});
fixture.detectChanges();
const contextElement = fixture.nativeElement.querySelector('.request-context');
expect(contextElement).toBeTruthy();
const summary = contextElement.querySelector('summary');
expect(summary.textContent).toBe('Context');
});
it('should hide context when not available', () => {
component.request = createMockRequest({
context: {}
});
fixture.detectChanges();
const contextElement = fixture.nativeElement.querySelector('.request-context');
expect(contextElement).toBeFalsy();
});
});
describe('isMarkdownQuestion', () => {
it('should detect headers', () => {
component.request = createMockRequest({ question: '# Main Title\n\nSome content' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect bold text', () => {
component.request = createMockRequest({ question: 'This is **bold** text' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect italic text', () => {
component.request = createMockRequest({ question: 'This is *italic* text' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect inline code', () => {
component.request = createMockRequest({ question: 'Use `console.log()` for debugging' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect code blocks', () => {
component.request = createMockRequest({ question: '```javascript\\nconsole.log("hello");\\n```' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect lists', () => {
component.request = createMockRequest({ question: '* Item 1\\n* Item 2' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect ordered lists', () => {
component.request = createMockRequest({ question: '1. First item\\n2. Second item' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect blockquotes', () => {
component.request = createMockRequest({ question: '> This is a quote' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should detect links', () => {
component.request = createMockRequest({ question: 'Visit [Google](https://google.com)' });
expect(component.isMarkdownQuestion()).toBe(true);
});
it('should return false for plain text', () => {
component.request = createMockRequest({ question: 'This is just plain text without any markdown.' });
expect(component.isMarkdownQuestion()).toBe(false);
});
it('should return false for empty question', () => {
component.request = createMockRequest({ question: '' });
expect(component.isMarkdownQuestion()).toBe(false);
});
});
});
function createMockRequest(overrides: Partial<QueuedRequest> = {}): QueuedRequest {
return {
id: 'req-1',
sessionId: 'session-1',
question: 'Test question',
status: 'pending',
timestamp: new Date(),
...overrides
};
}