sass-references/angular-material/material/progress-bar/progress-bar.spec.ts

350 lines
13 KiB
TypeScript
Raw Normal View History

2024-12-06 10:42:08 +08:00
import {dispatchFakeEvent} from '@angular/cdk/testing/private';
import {
ApplicationRef,
Component,
DebugElement,
EnvironmentProviders,
Provider,
Type,
} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {MAT_PROGRESS_BAR_DEFAULT_OPTIONS, MatProgressBarModule} from './index';
import {MatProgressBar} from './progress-bar';
describe('MatProgressBar', () => {
function createComponent<T>(
componentType: Type<T>,
providers: (Provider | EnvironmentProviders)[] = [],
): ComponentFixture<T> {
TestBed.configureTestingModule({
imports: [MatProgressBarModule, componentType],
providers,
});
return TestBed.createComponent<T>(componentType);
}
// All children need to be hidden for screen readers in order to support ChromeVox.
// More context in the issue: https://github.com/angular/components/issues/22165.
it('should have elements wrapped in aria-hidden div', () => {
const fixture = createComponent(BasicProgressBar);
const host = fixture.nativeElement as Element;
const element = host.children[0];
const children = element.children;
expect(children.length).toBe(3);
const ariaHidden = Array.from(children).map(child => child.getAttribute('aria-hidden'));
expect(ariaHidden).toEqual(['true', 'true', 'true']);
});
describe('with animation', () => {
describe('basic progress-bar', () => {
it('should apply a mode of "determinate" if no mode is provided.', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
expect(progressElement.componentInstance.mode).toBe('determinate');
});
it('should define default values for value and bufferValue attributes', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
expect(progressElement.componentInstance.value).toBe(0);
expect(progressElement.componentInstance.bufferValue).toBe(0);
});
it('should clamp value and bufferValue between 0 and 100', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
const progressComponent = progressElement.componentInstance;
progressComponent.value = 50;
expect(progressComponent.value).toBe(50);
progressComponent.value = 999;
expect(progressComponent.value).toBe(100);
progressComponent.value = -10;
expect(progressComponent.value).toBe(0);
progressComponent.bufferValue = -29;
expect(progressComponent.bufferValue).toBe(0);
progressComponent.bufferValue = 9;
expect(progressComponent.bufferValue).toBe(9);
progressComponent.bufferValue = 1320;
expect(progressComponent.bufferValue).toBe(100);
});
it('should set the proper transform based on the current value', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
const progressComponent = progressElement.componentInstance;
const primaryStyles = progressElement.nativeElement.querySelector(
'.mdc-linear-progress__primary-bar',
).style;
const bufferStyles = progressElement.nativeElement.querySelector(
'.mdc-linear-progress__buffer-bar',
).style;
// Parse out and round the value since different
// browsers return the value with a different precision.
const getBufferValue = () => Math.floor(parseInt(bufferStyles.flexBasis));
expect(primaryStyles.transform).toBe('scaleX(0)');
expect(getBufferValue()).toBe(100);
progressComponent.value = 40;
fixture.detectChanges();
expect(primaryStyles.transform).toBe('scaleX(0.4)');
expect(getBufferValue()).toBe(100);
progressComponent.value = 35;
progressComponent.bufferValue = 55;
fixture.detectChanges();
expect(primaryStyles.transform).toBe('scaleX(0.35)');
expect(getBufferValue()).toBe(100);
progressComponent.mode = 'buffer';
fixture.detectChanges();
expect(primaryStyles.transform).toBe('scaleX(0.35)');
expect(getBufferValue()).toEqual(55);
progressComponent.value = 60;
progressComponent.bufferValue = 60;
fixture.detectChanges();
expect(primaryStyles.transform).toBe('scaleX(0.6)');
expect(getBufferValue()).toEqual(60);
});
it('should remove the `aria-valuenow` attribute in indeterminate mode', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
const progressComponent = progressElement.componentInstance;
progressComponent.mode = 'determinate';
progressComponent.value = 50;
fixture.detectChanges();
expect(progressElement.nativeElement.getAttribute('aria-valuenow'))
.withContext('Expected aria-valuenow to be set in determinate mode.')
.toBe('50');
progressComponent.mode = 'indeterminate';
fixture.detectChanges();
expect(progressElement.nativeElement.hasAttribute('aria-valuenow'))
.withContext('Expect aria-valuenow to be cleared in indeterminate mode.')
.toBe(false);
});
it('should remove the `aria-valuenow` attribute in query mode', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
const progressComponent = progressElement.componentInstance;
progressComponent.mode = 'determinate';
progressComponent.value = 50;
fixture.detectChanges();
expect(progressElement.nativeElement.getAttribute('aria-valuenow'))
.withContext('Expected aria-valuenow to be set in determinate mode.')
.toBe('50');
progressComponent.mode = 'query';
fixture.detectChanges();
expect(progressElement.nativeElement.hasAttribute('aria-valuenow'))
.withContext('Expect aria-valuenow to be cleared in query mode.')
.toBe(false);
});
it('should be able to configure the default progress bar options via DI', () => {
const fixture = createComponent(BasicProgressBar, [
{
provide: MAT_PROGRESS_BAR_DEFAULT_OPTIONS,
useValue: {
mode: 'buffer',
color: 'warn',
},
},
]);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
expect(progressElement.componentInstance.mode).toBe('buffer');
expect(progressElement.componentInstance.color).toBe('warn');
});
it('should update the DOM transform when the value has changed', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
const progressComponent = progressElement.componentInstance;
const primaryBar = progressElement.nativeElement.querySelector(
'.mdc-linear-progress__primary-bar',
);
expect(primaryBar.style.transform).toBe('scaleX(0)');
progressComponent.value = 40;
fixture.detectChanges();
expect(primaryBar.style.transform).toBe('scaleX(0.4)');
});
it('should update the DOM transform when the bufferValue has changed', () => {
const fixture = createComponent(BasicProgressBar);
fixture.detectChanges();
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
const progressComponent = progressElement.componentInstance;
const bufferBar = progressElement.nativeElement.querySelector(
'.mdc-linear-progress__buffer-bar',
);
progressComponent.mode = 'buffer';
fixture.detectChanges();
expect(bufferBar.style.flexBasis).toBe('0%');
progressComponent.bufferValue = 40;
fixture.detectChanges();
expect(bufferBar.style.flexBasis).toBe('40%');
});
});
describe('animation trigger on determinate setting', () => {
let fixture: ComponentFixture<BasicProgressBar>;
let progressComponent: MatProgressBar;
let primaryValueBar: DebugElement;
beforeEach(() => {
fixture = createComponent(BasicProgressBar);
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
progressComponent = progressElement.componentInstance;
primaryValueBar = progressElement.query(By.css('.mdc-linear-progress__primary-bar'))!;
});
it('should trigger output event on primary value bar animation end', () => {
fixture.detectChanges();
const animationEndSpy = jasmine.createSpy();
progressComponent.animationEnd.subscribe(animationEndSpy);
progressComponent.value = 40;
expect(animationEndSpy).not.toHaveBeenCalled();
// On animation end, output should be emitted.
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend', true);
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
});
});
describe('animation trigger on buffer setting', () => {
let fixture: ComponentFixture<BufferProgressBar>;
let progressElement: DebugElement;
let progressComponent: MatProgressBar;
let primaryValueBar: DebugElement;
beforeEach(() => {
fixture = createComponent(BufferProgressBar);
progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
progressComponent = progressElement.componentInstance;
primaryValueBar = progressElement.query(By.css('.mdc-linear-progress__primary-bar'))!;
});
it('should bind on transitionend eventListener on primaryBarValue', () => {
spyOn(progressElement.nativeElement, 'addEventListener');
fixture.detectChanges();
expect(progressElement.nativeElement.addEventListener).toHaveBeenCalled();
expect(progressElement.nativeElement.addEventListener.calls.mostRecent().args[0]).toBe(
'transitionend',
);
});
it('should trigger output event on primary value bar animation end', () => {
fixture.detectChanges();
const animationEndSpy = jasmine.createSpy();
progressComponent.animationEnd.subscribe(animationEndSpy);
progressComponent.value = 40;
expect(animationEndSpy).not.toHaveBeenCalled();
// On animation end, output should be emitted.
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend', true);
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
});
it('should trigger output event with value not bufferValue', () => {
fixture.detectChanges();
const animationEndSpy = jasmine.createSpy();
progressComponent.animationEnd.subscribe(animationEndSpy);
progressComponent.value = 40;
progressComponent.bufferValue = 70;
expect(animationEndSpy).not.toHaveBeenCalled();
// On animation end, output should be emitted.
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend', true);
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
});
it('should not run change detection if there are no `animationEnd` observers', () => {
fixture.detectChanges();
const animationEndSpy = jasmine.createSpy();
const appRef = TestBed.inject(ApplicationRef);
spyOn(appRef, 'tick');
progressComponent.value = 30;
progressComponent.bufferValue = 60;
// On animation end, output should be emitted.
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend', true);
expect(appRef.tick).not.toHaveBeenCalled();
progressComponent.animationEnd.subscribe(animationEndSpy);
progressComponent.value = 40;
progressComponent.bufferValue = 70;
// On animation end, output should be emitted.
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend', true);
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
});
});
});
});
@Component({
template: '<mat-progress-bar></mat-progress-bar>',
standalone: true,
imports: [MatProgressBar],
})
class BasicProgressBar {}
@Component({
template: '<mat-progress-bar mode="buffer"></mat-progress-bar>',
standalone: true,
imports: [MatProgressBar],
})
class BufferProgressBar {}