/// /// Copyright 2015-2026 Micro Focus or one of its affiliates. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RadioButtonComponent } from './radiobutton.component'; import { RadioButtonModule } from './radiobutton.module'; describe('Radio Button Component', () => { let fixture: ComponentFixture; let component: RadioButtonComponent; let nativeElement: HTMLElement; let valueChangeSpy: jasmine.Spy; let changeCallbackSpy: jasmine.Spy; let touchedCallbackSpy: jasmine.Spy; beforeEach(() => { TestBed.configureTestingModule({ imports: [RadioButtonModule], }) .overrideComponent(RadioButtonComponent, { set: { changeDetection: ChangeDetectionStrategy.Default }, }) .compileComponents(); fixture = TestBed.createComponent(RadioButtonComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; valueChangeSpy = spyOn(component.valueChange, 'emit'); changeCallbackSpy = spyOn(component, 'onChangeCallback'); touchedCallbackSpy = spyOn(component, 'onTouchedCallback'); component.option = true; fixture.detectChanges(); }); it('should initialise with the correct values', () => { expect(component).toBeTruthy(); expect(component.value).toBeFalsy(); }); it('should not emit valueChange initially', () => { expect(valueChangeSpy).not.toHaveBeenCalled(); expect(changeCallbackSpy).not.toHaveBeenCalled(); expect(touchedCallbackSpy).not.toHaveBeenCalled(); }); it('should not emit valueChange when the value input changes', async () => { component.value = true; fixture.detectChanges(); await fixture.whenStable(); expect(valueChangeSpy).not.toHaveBeenCalled(); expect(changeCallbackSpy).not.toHaveBeenCalled(); expect(touchedCallbackSpy).not.toHaveBeenCalled(); }); it('should not emit valueChange when the value input changes via ngModel or form control', async () => { component.writeValue(true); fixture.detectChanges(); await fixture.whenStable(); expect(valueChangeSpy).not.toHaveBeenCalled(); expect(changeCallbackSpy).not.toHaveBeenCalled(); expect(touchedCallbackSpy).not.toHaveBeenCalled(); }); it('should emit valueChange whenever the user toggle the input via clicking', async () => { expect(component.value).toBeFalsy(); await toggle(); expect(component.value).toBeTruthy(); expect(valueChangeSpy).toHaveBeenCalledWith(true); expect(valueChangeSpy).toHaveBeenCalledTimes(1); expect(changeCallbackSpy).toHaveBeenCalledWith(true); expect(changeCallbackSpy).toHaveBeenCalledTimes(1); expect(touchedCallbackSpy).toHaveBeenCalled(); expect(touchedCallbackSpy).toHaveBeenCalledTimes(1); // clicking again should not toggle and should now emit a second time await toggle(); expect(component.value).toBeTruthy(); expect(valueChangeSpy).toHaveBeenCalledTimes(1); expect(changeCallbackSpy).toHaveBeenCalledTimes(1); expect(touchedCallbackSpy).toHaveBeenCalledTimes(1); }); it('should not allow toggling whenever the radio button is disabled via disabled input', async () => { component.disabled = true; await toggle(); fixture.detectChanges(); await fixture.whenStable(); expect(component.value).toBeFalsy(); expect(valueChangeSpy).not.toHaveBeenCalled(); expect(changeCallbackSpy).not.toHaveBeenCalled(); expect(touchedCallbackSpy).not.toHaveBeenCalled(); }); it('should not allow toggling whenever the radio button is disabled via Angular forms', async () => { component.setDisabledState(true); await toggle(); fixture.detectChanges(); await fixture.whenStable(); expect(component.value).toBeFalsy(); expect(valueChangeSpy).not.toHaveBeenCalled(); expect(changeCallbackSpy).not.toHaveBeenCalled(); expect(touchedCallbackSpy).not.toHaveBeenCalled(); }); it('should not allow toggling whenever the radio button is marked as not clickable', async () => { component.clickable = false; await toggle(); fixture.detectChanges(); await fixture.whenStable(); expect(component.value).toBeFalsy(); expect(valueChangeSpy).not.toHaveBeenCalled(); expect(changeCallbackSpy).not.toHaveBeenCalled(); expect(touchedCallbackSpy).not.toHaveBeenCalled(); }); function getInput(): HTMLInputElement { return nativeElement.querySelector('input'); } async function toggle(): Promise { getInput().click(); fixture.detectChanges(); await fixture.whenStable(); } }); @Component({ selector: 'app-radio-button-value-test', template: `
Option1 Option2 Option3
`, imports: [RadioButtonModule], }) export class RadioButtonValueTestComponent { selected: number | string | object = 100; radioOptions = { option1: 100, option2: 'string', option3: { test: 1, }, }; disabled: boolean = false; simplified: boolean = false; clickable: boolean = true; // eslint-disable-next-line @typescript-eslint/no-empty-function onButtonClick(): void {} } describe('Radio Button Component - Value', () => { let component: RadioButtonValueTestComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [RadioButtonModule, RadioButtonValueTestComponent], }).compileComponents(); fixture = TestBed.createComponent(RadioButtonValueTestComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); expect(getRadioButtonContent(0, nativeElement)).toBe('Option1'); expect(getRadioButtonContent(1, nativeElement)).toBe('Option2'); expect(getRadioButtonContent(2, nativeElement)).toBe('Option3'); }); it('should have initially selected option', () => { expect(getNativeRadioButton(0, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual(100); }); it('should select an option when clicked', () => { // click on the second radio button getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual('string'); }); it('should disable an option when the [disabled] input changes', () => { component.disabled = true; fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).disabled).toBeTruthy(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); }); it('should simplify an option when the [simplified] input changes', () => { component.simplified = true; fixture.detectChanges(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); }); it('should not allow an option 2 to be clicked when [clickable] input changes', () => { expect(component.selected).toEqual(100); component.clickable = false; fixture.detectChanges(); getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(component.selected).toEqual(100); }); it('should emit event on radio button click', () => { spyOn(component, 'onButtonClick'); getNativeRadioButton(1, nativeElement).click(); expect(component.onButtonClick).toHaveBeenCalled(); }); }); @Component({ selector: 'app-radio-button-value-test', template: `
Option1 Option2 Option3
`, imports: [RadioButtonModule], }) export class RadioButtonTestValueGroupComponent { selected: number | string | object = 100; radioOptions = { option1: 100, option2: 'string', option3: { test: 1, }, }; disabled: boolean = false; simplified: boolean = false; clickable: boolean = true; // eslint-disable-next-line @typescript-eslint/no-empty-function onButtonClick(): void {} } describe('Radio Button Component - Value with uxRadioButtonGroup', () => { let component: RadioButtonTestValueGroupComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [RadioButtonModule, RadioButtonTestValueGroupComponent], }).compileComponents(); fixture = TestBed.createComponent(RadioButtonTestValueGroupComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); expect(getRadioButtonContent(0, nativeElement)).toBe('Option1'); expect(getRadioButtonContent(1, nativeElement)).toBe('Option2'); expect(getRadioButtonContent(2, nativeElement)).toBe('Option3'); }); it('should have initially selected option', () => { expect(getNativeRadioButton(0, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual(100); }); it('should select an option when clicked', () => { // click on the second radio button getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual('string'); }); it('should disable an option when the [disabled] input changes', () => { component.disabled = true; fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).disabled).toBeTruthy(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); }); it('should simplify an option when the [simplified] input changes', () => { component.simplified = true; fixture.detectChanges(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); }); it('should not allow an option 2 to be clicked when [clickable] input changes', () => { expect(component.selected).toEqual(100); component.clickable = false; fixture.detectChanges(); getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(component.selected).toEqual(100); }); it('should emit event on radio button click', () => { spyOn(component, 'onButtonClick'); getNativeRadioButton(1, nativeElement).click(); expect(component.onButtonClick).toHaveBeenCalled(); }); }); @Component({ selector: 'app-radio-button-value-test', template: `
Option1 Option2 Option3
`, imports: [RadioButtonModule, FormsModule], }) export class RadioButtonTestNgModelComponent { selected: number | string | object = 100; radioOptions = { option1: 100, option2: 'string', option3: { test: 1, }, }; disabled: boolean = false; simplified: boolean = false; clickable: boolean = true; // eslint-disable-next-line @typescript-eslint/no-empty-function onButtonClick(): void {} } describe('Radio Button Component - NgModel', () => { let component: RadioButtonTestNgModelComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [RadioButtonModule, FormsModule, RadioButtonTestNgModelComponent], }).compileComponents(); fixture = TestBed.createComponent(RadioButtonTestNgModelComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); expect(getRadioButtonContent(0, nativeElement)).toBe('Option1'); expect(getRadioButtonContent(1, nativeElement)).toBe('Option2'); expect(getRadioButtonContent(2, nativeElement)).toBe('Option3'); }); it('should have initially selected option', () => { expect(getNativeRadioButton(0, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual(100); }); it('should select an option when clicked', () => { // click on the second radio button getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual('string'); }); it('should disable an option when the [disabled] input changes', () => { component.disabled = true; fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).disabled).toBeTruthy(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); }); it('should simplify an option when the [simplified] input changes', () => { component.simplified = true; fixture.detectChanges(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); }); it('should not allow an option 2 to be clicked when [clickable] input changes', () => { expect(component.selected).toEqual(100); component.clickable = false; fixture.detectChanges(); getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(component.selected).toEqual(100); }); it('should emit event on radio button click', () => { spyOn(component, 'onButtonClick'); getNativeRadioButton(1, nativeElement).click(); expect(component.onButtonClick).toHaveBeenCalled(); }); }); @Component({ selector: 'app-radio-button-value-test', template: `
Option1 Option2 Option3
`, imports: [RadioButtonModule, FormsModule], }) export class RadioButtonTestNgModelGroupComponent { selected: number | string | object = 100; radioOptions = { option1: 100, option2: 'string', option3: { test: 1, }, }; disabled: boolean = false; simplified: boolean = false; clickable: boolean = true; // eslint-disable-next-line @typescript-eslint/no-empty-function onButtonClick(): void {} } describe('Radio Button Component - NgModel', () => { let component: RadioButtonTestNgModelGroupComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [RadioButtonModule, FormsModule, RadioButtonTestNgModelGroupComponent], }).compileComponents(); fixture = TestBed.createComponent(RadioButtonTestNgModelGroupComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); expect(getRadioButtonContent(0, nativeElement)).toBe('Option1'); expect(getRadioButtonContent(1, nativeElement)).toBe('Option2'); expect(getRadioButtonContent(2, nativeElement)).toBe('Option3'); }); it('should have initially selected option', () => { expect(getNativeRadioButton(0, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual(100); }); it('should select an option when clicked', () => { // click on the second radio button getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(2, nativeElement).checked).toBeFalsy(); expect(component.selected).toEqual('string'); }); it('should disable an option when the [disabled] input changes', () => { component.disabled = true; fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).disabled).toBeTruthy(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); expect(getNativeRadioButton(2, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); }); it('should simplify an option when the [simplified] input changes', () => { component.simplified = true; fixture.detectChanges(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(2, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); }); it('should not allow an option 2 to be clicked when [clickable] input changes', () => { expect(component.selected).toEqual(100); component.clickable = false; fixture.detectChanges(); getNativeRadioButton(1, nativeElement).click(); fixture.detectChanges(); expect(component.selected).toEqual(100); }); it('should emit event on radio button click', () => { spyOn(component, 'onButtonClick'); getNativeRadioButton(1, nativeElement).click(); expect(component.onButtonClick).toHaveBeenCalled(); }); }); @Component({ selector: 'app-radio-button-value-test', template: `
Option 1 Option 2
`, imports: [RadioButtonModule, ReactiveFormsModule], }) export class RadioButtonTestReactiveFormComponent { form = new FormGroup({ option: new FormControl(2), }); required: boolean = false; disabled: boolean = false; simplified: boolean = false; clickable: boolean = true; // eslint-disable-next-line @typescript-eslint/no-empty-function onButtonClick(): void {} } describe('Radio Button Component - Reactive Form', () => { let component: RadioButtonTestReactiveFormComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [RadioButtonModule, ReactiveFormsModule, RadioButtonTestReactiveFormComponent], }).compileComponents(); fixture = TestBed.createComponent(RadioButtonTestReactiveFormComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); expect(getRadioButtonContent(0, nativeElement)).toBe('Option 1'); expect(getRadioButtonContent(1, nativeElement)).toBe('Option 2'); }); it('should have initially selected option', () => { expect(getNativeRadioButton(0, nativeElement).checked).toBeFalsy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeTruthy(); expect(component.form.value.option).toEqual(2); }); it('should select an option when clicked', () => { // click on the first radio button getNativeRadioButton(0, nativeElement).click(); fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).checked).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).checked).toBeFalsy(); expect(component.form.value.option).toEqual(1); }); it('should disable an option when the [disabled] input changes', () => { component.disabled = true; fixture.detectChanges(); expect(getNativeRadioButton(0, nativeElement).disabled).toBeTruthy(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeTruthy(); expect(getNativeRadioButton(1, nativeElement).disabled).toBeFalsy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-disabled') ).toBeFalsy(); }); it('should simplify an option when the [simplified] input changes', () => { component.simplified = true; fixture.detectChanges(); expect( getRadioButtonLabel(0, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); expect( getRadioButtonLabel(1, nativeElement).classList.contains('ux-radio-button-simplified') ).toBeTruthy(); }); it('should not allow an option 1 to be clicked when [clickable] input changes', () => { expect(component.form.value.option).toEqual(2); component.clickable = false; fixture.detectChanges(); getNativeRadioButton(0, nativeElement).click(); fixture.detectChanges(); expect(component.form.value.option).toEqual(2); }); it('should emit event on radio button click', () => { spyOn(component, 'onButtonClick'); getNativeRadioButton(0, nativeElement).click(); fixture.detectChanges(); expect(component.onButtonClick).toHaveBeenCalled(); }); it('should add a required attribute to the input when required is true', () => { component.required = true; fixture.detectChanges(); const inputElementEmpty = nativeElement.querySelectorAll( 'input.ux-radio-button-input' ); const attributeRequired = inputElementEmpty[1].hasAttribute('required'); expect(attributeRequired).toBe(true); }); }); function getRadioButton(index: number, nativeElement: HTMLElement): HTMLElement { return nativeElement.querySelectorAll('ux-radio-button').item(index); } function getNativeRadioButton(index: number, nativeElement: HTMLElement): HTMLInputElement { return getRadioButton(index, nativeElement).querySelector('input'); } function getRadioButtonLabel(index: number, nativeElement: HTMLElement): HTMLLabelElement { return getRadioButton(index, nativeElement).querySelector('label'); } function getRadioButtonContent(index: number, nativeElement: HTMLElement): string { return getRadioButton(index, nativeElement).querySelector( '.ux-radio-button-label' ).innerText; }