import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { MultipleChoiceResponseComponent } from './multiple-choice-response.component';
import { MultipleChoiceQuestion } from '@ask-me-mcp/askme-shared';
describe('MultipleChoiceResponseComponent', () => {
let component: MultipleChoiceResponseComponent;
let fixture: ComponentFixture<MultipleChoiceResponseComponent>;
const mockQuestions: MultipleChoiceQuestion[] = [
{
id: 'q1',
text: 'Which features should be included?',
options: [
{ id: 'q1-opt1', text: 'Feature A', selected: false, comment: '' },
{ id: 'q1-opt2', text: 'Feature B', selected: false, comment: '' },
{ id: 'q1-opt3', text: 'Feature C', selected: false, comment: '' }
]
},
{
id: 'q2',
text: 'Which environments to deploy?',
options: [
{ id: 'q2-opt1', text: 'Development', selected: false, comment: '' },
{ id: 'q2-opt2', text: 'Staging', selected: false, comment: '' }
]
}
];
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MultipleChoiceResponseComponent, FormsModule]
}).compileComponents();
fixture = TestBed.createComponent(MultipleChoiceResponseComponent);
component = fixture.componentInstance;
component.requestId = 'test-request-123';
component.questionsInput = mockQuestions;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display all questions and options', () => {
const questionElements = fixture.nativeElement.querySelectorAll('.question-card');
expect(questionElements.length).toBe(2);
const firstQuestionText = questionElements[0].querySelector('.question-text').textContent.trim();
expect(firstQuestionText).toBe('Which features should be included?');
const firstQuestionOptions = questionElements[0].querySelectorAll('.option-item');
expect(firstQuestionOptions.length).toBe(3);
});
it('should toggle option selection', () => {
const optionCheckboxes = fixture.nativeElement.querySelectorAll('.option-label input[type="checkbox"]');
const firstOptionCheckbox = optionCheckboxes[0];
expect(component.questions()[0].options[0].selected).toBe(false);
firstOptionCheckbox.click();
fixture.detectChanges();
expect(component.questions()[0].options[0].selected).toBe(true);
});
it('should update selected count when options are toggled', () => {
expect(component.selectedCount()).toBe(0);
component.toggleOption('q1', 'q1-opt1');
component.toggleOption('q1', 'q1-opt2');
expect(component.selectedCount()).toBe(2);
});
it('should calculate total options correctly', () => {
expect(component.totalOptions()).toBe(5); // 3 + 2 options
});
it('should clear all selections', () => {
// Select some options first
component.toggleOption('q1', 'q1-opt1');
component.toggleOption('q2', 'q2-opt1');
expect(component.selectedCount()).toBe(2);
component.clearAll();
expect(component.selectedCount()).toBe(0);
expect(component.questions()[0].options[0].selected).toBe(false);
expect(component.questions()[1].options[0].selected).toBe(false);
});
it('should emit response when submitted', () => {
jest.spyOn(component.responseSubmitted, 'emit');
// Select some options
component.toggleOption('q1', 'q1-opt1');
component.submitResponse();
expect(component.responseSubmitted.emit).toHaveBeenCalledWith({
requestId: 'test-request-123',
type: 'multiple-choice',
questions: component.questions()
});
});
it('should disable submit button while submitting', () => {
component.isSubmitting.set(true);
fixture.detectChanges();
const submitButton = fixture.nativeElement.querySelector('.btn-primary');
expect(submitButton.disabled).toBe(true);
});
it('should show selection summary', () => {
component.toggleOption('q1', 'q1-opt1');
fixture.detectChanges();
const summary = fixture.nativeElement.querySelector('.selection-summary');
expect(summary.textContent.trim()).toContain('Selected: 1 / 5');
});
it('should handle comment input', () => {
const questions = component.questions();
questions[0].options[0].comment = 'Test comment';
component.questions.set([...questions]);
fixture.detectChanges();
expect(component.questions()[0].options[0].comment).toBe('Test comment');
});
it('should disable clear button when no selections', () => {
fixture.detectChanges();
const clearButton = fixture.nativeElement.querySelector('.btn-secondary');
expect(clearButton.disabled).toBe(true);
});
it('should enable clear button when selections exist', () => {
component.toggleOption('q1', 'q1-opt1');
fixture.detectChanges();
const clearButton = fixture.nativeElement.querySelector('.btn-secondary');
expect(clearButton.disabled).toBe(false);
});
it('should set priority level for selected options', () => {
component.toggleOption('q1', 'q1-opt1'); // First select an option
component.setPriority('q1', 'q1-opt1', 2);
const option = component.questions()[0].options[0];
expect(option.priority).toBe(2);
});
it('should display priority arrows when option is selected', () => {
component.toggleOption('q1', 'q1-opt1');
component.setPriority('q1', 'q1-opt1', 1);
fixture.detectChanges();
const prioritySection = fixture.nativeElement.querySelector('.priority-section');
expect(prioritySection).toBeTruthy();
const selectedButton = fixture.nativeElement.querySelector('.priority-button.selected');
expect(selectedButton).toBeTruthy();
expect(selectedButton.textContent.trim()).toBe('↑');
const priorityText = fixture.nativeElement.querySelector('.priority-text');
expect(priorityText.textContent.trim()).toBe('High');
});
it('should show correct arrow and text for different priority levels', () => {
expect(component.getPriorityArrow(-3)).toBe('↓↓↓');
expect(component.getPriorityArrow(-2)).toBe('↓↓');
expect(component.getPriorityArrow(-1)).toBe('↓');
expect(component.getPriorityArrow(0)).toBe('→');
expect(component.getPriorityArrow(1)).toBe('↑');
expect(component.getPriorityArrow(2)).toBe('↑↑');
expect(component.getPriorityArrow(3)).toBe('↑↑↑');
expect(component.getPriorityText(-3)).toBe('Lowest');
expect(component.getPriorityText(0)).toBe('Neutral');
expect(component.getPriorityText(3)).toBe('Highest');
});
it('should show correct tooltips for priority levels', () => {
expect(component.getPriorityTooltip(-3)).toBe('Lowest priority - least important');
expect(component.getPriorityTooltip(0)).toBe('Neutral priority - standard importance');
expect(component.getPriorityTooltip(3)).toBe('Highest priority - most important');
});
it('should toggle won\'t answer for a question', () => {
expect(component.hasWontAnswer('q1')).toBe(false);
component.toggleWontAnswer('q1');
expect(component.hasWontAnswer('q1')).toBe(true);
expect(component.questions()[0].options.every(opt => opt.wontAnswer)).toBe(true);
});
it('should show why field when won\'t answer is selected', () => {
component.toggleWontAnswer('q1');
fixture.detectChanges();
const questionCards = fixture.nativeElement.querySelectorAll('.question-card');
const firstQuestionCard = questionCards[0];
expect(firstQuestionCard.classList).toContain('grayed-out');
const whySection = firstQuestionCard.querySelector('.why-section');
expect(whySection).toBeTruthy();
const whyInput = firstQuestionCard.querySelector('.why-input');
expect(whyInput).toBeTruthy();
// Options grid should not be visible for the first question
const firstQuestionOptionsGrid = firstQuestionCard.querySelector('.options-grid');
expect(firstQuestionOptionsGrid).toBeFalsy();
// But second question should still show options
const secondQuestionCard = questionCards[1];
const secondQuestionOptionsGrid = secondQuestionCard.querySelector('.options-grid');
expect(secondQuestionOptionsGrid).toBeTruthy();
});
it('should clear selections when won\'t answer is toggled on', () => {
// First select an option and set priority
component.toggleOption('q1', 'q1-opt1');
component.setPriority('q1', 'q1-opt1', 2);
expect(component.questions()[0].options[0].selected).toBe(true);
expect(component.questions()[0].options[0].priority).toBe(2);
// Toggle won't answer
component.toggleWontAnswer('q1');
expect(component.questions()[0].options[0].selected).toBe(false);
expect(component.questions()[0].options[0].priority).toBeUndefined();
expect(component.questions()[0].options[0].wontAnswer).toBe(true);
});
it('should restore selections when won\'t answer is toggled off', () => {
// Set won't answer first
component.toggleWontAnswer('q1');
expect(component.hasWontAnswer('q1')).toBe(true);
// Toggle it off
component.toggleWontAnswer('q1');
expect(component.hasWontAnswer('q1')).toBe(false);
expect(component.questions()[0].options.every(opt => opt.wontAnswer === false)).toBe(true);
});
it('should handle why field for questions', () => {
component.setQuestionWhy('q1', 'Privacy concerns');
expect(component.getQuestionWhy('q1')).toBe('Privacy concerns');
component.setQuestionWhy('q1', 'Updated reason');
expect(component.getQuestionWhy('q1')).toBe('Updated reason');
});
it('should include priority, wontAnswer and why in clear all', () => {
// Set up some state
component.toggleOption('q1', 'q1-opt1');
component.setPriority('q1', 'q1-opt1', 3);
component.toggleWontAnswer('q2');
component.setQuestionWhy('q2', 'Cannot answer due to confidentiality');
component.clearAll();
component.questions().forEach(question => {
question.options.forEach(option => {
expect(option.selected).toBe(false);
expect(option.comment).toBe('');
expect(option.priority).toBeUndefined();
expect(option.wontAnswer).toBe(false);
});
expect(question.whyNotAnswering).toBeUndefined();
});
});
});