import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {RadioButtonComponent, RadioGroupComponent} from './radio-group.component';
import {Component, SimpleChange, SimpleChanges, ViewChild} from '@angular/core';
import {AngularMaterialModule} from '../modules/angular-material.module';
import {MatIconTestingModule} from '@angular/material/icon/testing';
import {FormsModule} from '@angular/forms';
import {By} from '@angular/platform-browser';
@Component({
selector: 'app-test-radio-group-component',
template: '' +
'' +
'' +
''
})
class TestRadioGroupComponent {
@ViewChild('group') group?: RadioGroupComponent;
public test: string | undefined = undefined;
public disabled = false;
}
describe('RadioGroupComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RadioGroupComponent, RadioButtonComponent, TestRadioGroupComponent ],
imports: [AngularMaterialModule, MatIconTestingModule, FormsModule]
})
.compileComponents();
});
describe('standalone behaviour', () => {
let component: RadioGroupComponent;
let fixture: ComponentFixture;
beforeEach(() => {
fixture = TestBed.createComponent(RadioGroupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('is not dirty upon creation', () => {
expect(component.isDirty()).toBe(false);
});
it('has value undefined upon creation', () => {
expect(component.value).toBe(undefined);
});
it('only registers changes if model has structural changes', () => {
const onChange = jasmine.createSpy('onChange');
component.writeValue({someProperty: 'a'});
component.registerOnChange(onChange);
component.writeValue({someProperty: 'a'});
expect(onChange).toHaveBeenCalledTimes(0);
component.writeValue({someProperty: 'b'});
expect(onChange).toHaveBeenCalledTimes(1);
});
it('updates inner value when ngModel is updated on the outside', () => {
component.writeValue('tis but a test');
expect(component.innerModel).toEqual('tis but a test');
});
it('makes ngModel undefined when set to not relevant', () => {
component.writeValue('test');
fixture.detectChanges();
component.notRelevant = true;
component.ngOnChanges({notRelevant: {currentValue: true, previousValue: undefined} as SimpleChange} as SimpleChanges);
expect(component.value).toBeUndefined();
});
it('re-fills the date when set to not relevant and back again', () => {
component.writeValue('test');
fixture.detectChanges();
component.notRelevant = true;
component.ngOnChanges({notRelevant: {currentValue: true, previousValue: undefined} as SimpleChange} as SimpleChanges);
expect(component.value).toBeUndefined();
component.notRelevant = false;
component.ngOnChanges({notRelevant: {currentValue: false, previousValue: true} as SimpleChange} as SimpleChanges);
expect(component.value).toBe('test');
});
it('does not register a change on the first value set', () => {
// This is to avoid issues where values initialized from an async call will mark the form as dirty
const mock = {
onChange: (whatever: any) => undefined
};
const onChange = spyOn(mock, 'onChange');
component.registerOnChange(onChange);
component.writeValue('whatever');
expect(onChange).not.toHaveBeenCalled();
component.writeValue('even more whatever');
expect(onChange).toHaveBeenCalledWith('even more whatever');
});
it('sets value to undefined if value is already selected on click', () => {
const clickEvent = new MouseEvent('click');
component.onClick(clickEvent, 'a');
expect(component.value).toBe('a');
component.onClick(clickEvent, 'a');
expect(component.value).toBe(undefined);
});
it('updates value if another button is active', () => {
const clickEvent = new MouseEvent('click');
component.onClick(clickEvent, 'a');
expect(component.value).toBe('a');
component.onClick(clickEvent, 'b');
expect(component.value).toBe('b');
});
it('does not change value when clicked while disabled', () => {
component.setDisabledState(true);
const clickEvent = new MouseEvent('click');
component.onClick(clickEvent, 'a');
expect(component.value).toBe(undefined);
});
it('is marked as dirty upon first click', () => {
const clickEvent = new MouseEvent('click');
component.onClick(clickEvent, 'a');
expect(component.isDirty()).toBe(true);
});
});
describe('material integration', () => {
let component: TestRadioGroupComponent;
let fixture: ComponentFixture;
beforeEach(() => {
fixture = TestBed.createComponent(TestRadioGroupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it ('creates a mat-radio-button for each app-radio-button', () => {
fixture.detectChanges();
const buttons = fixture.debugElement.queryAll(By.css('mat-radio-button'));
expect(buttons.length).toBe(2);
});
it ('disables radio-group when disabled', fakeAsync(() => {
component.disabled = true;
fixture.detectChanges();
tick();
fixture.detectChanges();
const buttons = fixture.debugElement.queryAll(By.css('mat-radio-button'));
expect(buttons[0].nativeNode.classList.contains('mat-radio-disabled')).toBeTrue();
expect(buttons[1].nativeNode.classList.contains('mat-radio-disabled')).toBeTrue();
}));
it('fills in radio-group based on ngModel value', (done) => {
component.test = 'second_test_element';
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
return fixture.whenStable();
}).then(() => {
fixture.detectChanges();
const buttons = fixture.debugElement.queryAll(By.css('mat-radio-button'));
expect(buttons[0].nativeNode.classList.contains('mat-radio-checked')).toBeFalse();
expect(buttons[1].nativeNode.classList.contains('mat-radio-checked')).toBeTrue();
done();
});
});
});
});