igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
715 lines (588 loc) • 26 kB
text/typescript
import { Component, ViewChild } from '@angular/core';
import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { UIInteractions } from '../test-utils/ui-interactions.spec';
import { IDialogCancellableEventArgs, IDialogEventArgs, IgxDialogComponent } from './dialog.component';
import { configureTestSuite } from '../test-utils/configure-suite';
import { useAnimation } from '@angular/animations';
import { PositionSettings, HorizontalAlignment, VerticalAlignment } from '../services/overlay/utilities';
import { slideOutBottom, slideInTop } from '../animations/main';
import { IgxToggleDirective } from '../directives/toggle/toggle.directive';
import { IgxDialogActionsDirective, IgxDialogTitleDirective } from './dialog.directives';
const OVERLAY_MAIN_CLASS = 'igx-overlay';
const OVERLAY_WRAPPER_CLASS = `${OVERLAY_MAIN_CLASS}__wrapper--flex`;
const OVERLAY_MODAL_WRAPPER_CLASS = `${OVERLAY_MAIN_CLASS}__wrapper--modal`;
const CLASS_OVERLAY_CONTENT_MODAL = `${OVERLAY_MAIN_CLASS}__content--modal`;
describe('Dialog', () => {
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
AlertComponent,
DialogComponent,
CustomDialogComponent,
NestedDialogsComponent,
CustomTemplates1DialogComponent,
CustomTemplates2DialogComponent,
DialogSampleComponent,
PositionSettingsDialogComponent,
DialogTwoWayDataBindingComponent
]
}).compileComponents();
}));
afterEach(() => {
UIInteractions.clearOverlay();
});
it('Initialize a datepicker component with id', () => {
const fixture = TestBed.createComponent(AlertComponent);
fixture.detectChanges();
const dialog = fixture.componentInstance.dialog;
const domDialog = fixture.debugElement.query(By.css('igx-dialog')).nativeElement;
expect(dialog.id).toContain('igx-dialog-');
expect(domDialog.id).toContain('igx-dialog-');
dialog.id = 'customDialog';
fixture.detectChanges();
expect(dialog.id).toBe('customDialog');
expect(domDialog.id).toBe('customDialog');
});
it('Should set dialog title.', () => {
const fixture = TestBed.createComponent(AlertComponent);
const dialog = fixture.componentInstance.dialog;
const expectedTitle = 'alert';
dialog.open();
fixture.detectChanges();
expect(dialog.title).toEqual(expectedTitle);
const titleDebugElement = fixture.debugElement.query(By.css('.igx-dialog__window-title'));
expect(titleDebugElement.nativeElement.textContent.trim()).toEqual(expectedTitle);
dialog.close();
});
it('Should set dialog message.', () => {
const fixture = TestBed.createComponent(AlertComponent);
const dialog = fixture.componentInstance.dialog;
const expectedMessage = 'message';
dialog.open();
fixture.detectChanges();
expect(dialog.message).toEqual(expectedMessage);
const messageDebugElement = fixture.debugElement.query(By.css('.igx-dialog__window-content'));
expect(messageDebugElement.nativeElement.textContent.trim()).toEqual(expectedMessage);
});
it('Should focus focusable elements in dialog on Tab key pressed', () => {
const fix = TestBed.createComponent(DialogComponent);
fix.detectChanges();
const dialog = fix.componentInstance.dialog;
dialog.open();
fix.detectChanges();
const buttons = fix.debugElement.queryAll(By.css('button'));
const toggle = fix.debugElement.query(By.directive(IgxToggleDirective));
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle);
fix.detectChanges();
expect(document.activeElement).toEqual(buttons[0].nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle);
fix.detectChanges();
expect(document.activeElement).toEqual(buttons[1].nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle);
fix.detectChanges();
expect(document.activeElement).toEqual(buttons[0].nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle, false, true);
fix.detectChanges();
expect(document.activeElement).toEqual(buttons[1].nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle, false, true);
fix.detectChanges();
expect(document.activeElement).toEqual(buttons[0].nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle, false, true);
fix.detectChanges();
expect(document.activeElement).toEqual(buttons[1].nativeElement);
});
it('should trap focus on dialog modal with non-focusable elements', () => {
const fix = TestBed.createComponent(AlertComponent);
fix.detectChanges();
const dialog = fix.componentInstance.dialog;
dialog.leftButtonLabel = '';
fix.detectChanges();
dialog.open();
fix.detectChanges();
const toggle = fix.debugElement.query(By.directive(IgxToggleDirective));
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle);
fix.detectChanges();
expect(document.activeElement).toEqual(toggle.nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle, false, true);
fix.detectChanges();
expect(document.activeElement).toEqual(toggle.nativeElement);
UIInteractions.triggerEventHandlerKeyDown('Tab', toggle);
fix.detectChanges();
expect(document.activeElement).toEqual(toggle.nativeElement);
});
it('Should open and close dialog when set values to IsOpen', fakeAsync(() => {
const fixture = TestBed.createComponent(AlertComponent);
const dialog = fixture.componentInstance.dialog;
dialog.isOpen = true;
tick(100);
fixture.detectChanges();
expect(dialog.isOpen).toEqual(true);
dialog.close();
tick();
fixture.detectChanges();
expect(dialog.isOpen).toEqual(false);
}));
it('Should open and close dialog with isOpen two way data binding', fakeAsync(() => {
const fixture = TestBed.createComponent(DialogTwoWayDataBindingComponent);
const dialog = fixture.componentInstance.dialog;
fixture.detectChanges();
fixture.componentInstance.myDialog = true;
fixture.detectChanges();
tick(100);
expect(dialog.isOpen).toEqual(true);
fixture.componentInstance.myDialog = false;
fixture.detectChanges();
tick();
expect(dialog.isOpen).toEqual(false);
}));
it('Should set custom modal message.', () => {
const fixture = TestBed.createComponent(CustomDialogComponent);
const dialog = fixture.componentInstance.dialog;
dialog.open();
fixture.detectChanges();
const dialogWindow = fixture.debugElement.query(By.css('.igx-dialog__window'));
const customContent = fixture.debugElement.query(By.css('.custom-dialog__content'));
expect(customContent).toBeTruthy();
expect(dialogWindow.children.length).toEqual(2);
expect(customContent.children.length).toEqual(1);
});
it('Should set left and right button properties.', () => {
const fixture = TestBed.createComponent(DialogComponent);
const dialog = fixture.componentInstance.dialog;
fixture.detectChanges();
dialog.open();
expect(dialog.leftButtonLabel).toEqual('left button');
expect(dialog.leftButtonType).toEqual('raised');
expect(dialog.leftButtonColor).toEqual('black');
expect(dialog.leftButtonBackgroundColor).toEqual('darkblue');
expect(dialog.leftButtonRipple).toEqual('pink');
expect(dialog.rightButtonLabel).toEqual('right button');
expect(dialog.rightButtonType).toEqual('raised');
expect(dialog.rightButtonColor).toEqual('orange');
expect(dialog.rightButtonBackgroundColor).toEqual('lightblue');
expect(dialog.rightButtonRipple).toEqual('white');
});
it('Should execute open/close methods.', fakeAsync(() => {
const fixture = TestBed.createComponent(AlertComponent);
const dialog = fixture.componentInstance.dialog;
fixture.detectChanges();
expect(dialog.isOpen).toEqual(false);
dialog.open();
fixture.detectChanges();
tick();
expect(dialog.isOpen).toEqual(true);
dialog.close();
tick();
fixture.detectChanges();
expect(dialog.isOpen).toEqual(false);
}));
it('Should set closeOnOutsideSelect.', fakeAsync(() => {
const fixture = TestBed.createComponent(AlertComponent);
fixture.detectChanges();
const dialog = fixture.componentInstance.dialog;
dialog.open();
tick();
fixture.detectChanges();
const dialogElem = fixture.debugElement.query(By.css('.igx-dialog')).nativeElement;
dialogElem.click();
tick();
fixture.detectChanges();
expect(dialog.isOpen).toEqual(false);
dialog.closeOnOutsideSelect = false;
dialog.open();
tick();
fixture.detectChanges();
dialogElem.click();
tick();
fixture.detectChanges();
expect(dialog.isOpen).toEqual(true);
}));
it('Should test events.', fakeAsync(() => {
const fixture = TestBed.createComponent(DialogSampleComponent);
const dialog = fixture.componentInstance.dialog;
const args: IDialogEventArgs = {
dialog,
event: undefined,
};
let cancellableArgs: IDialogCancellableEventArgs = {
dialog,
event: null,
cancel: false
};
spyOn(dialog.opening, 'emit');
spyOn(dialog.opened, 'emit');
spyOn(dialog.isOpenChange, 'emit');
spyOn(dialog.closing, 'emit');
spyOn(dialog.closed, 'emit');
dialog.open();
tick();
fixture.detectChanges();
expect(dialog.opening.emit).toHaveBeenCalledWith(cancellableArgs);
expect(dialog.isOpenChange.emit).toHaveBeenCalledWith(true);
// expect(dialog.onOpened.emit).toHaveBeenCalled();
dialog.close();
tick();
fixture.detectChanges();
cancellableArgs = { dialog, event: undefined, cancel: false };
expect(dialog.closing.emit).toHaveBeenCalledWith(cancellableArgs);
expect(dialog.closed.emit).toHaveBeenCalledWith(args);
expect(dialog.isOpenChange.emit).toHaveBeenCalledWith(false);
dialog.open();
tick();
fixture.detectChanges();
const buttons = document.querySelectorAll('button');
const leftButton = buttons[0];
const rightButton = buttons[1];
spyOn(dialog.leftButtonSelect, 'emit');
dispatchEvent(leftButton, 'click');
expect(dialog.leftButtonSelect.emit).toHaveBeenCalled();
spyOn(dialog.rightButtonSelect, 'emit');
dispatchEvent(rightButton, 'click');
tick();
expect(dialog.rightButtonSelect.emit).toHaveBeenCalled();
}));
it('Should set ARIA attributes.', () => {
const alertFixture = TestBed.createComponent(AlertComponent);
const alert = alertFixture.componentInstance.dialog;
alert.open();
alertFixture.detectChanges();
expect(alert.role).toEqual('alertdialog');
const dialogFixture = TestBed.createComponent(DialogComponent);
const dialog = dialogFixture.componentInstance.dialog;
dialog.open();
dialogFixture.detectChanges();
expect(dialog.role).toEqual('dialog');
const titleWrapper = dialogFixture.debugElement.query(By.css('.igx-dialog__window-title'));
const dialogWindow = dialogFixture.debugElement.query(By.css('.igx-dialog__window'));
expect(titleWrapper.attributes.id).toEqual(dialogWindow.attributes['aria-labelledby']);
});
it('Should close only inner dialog on closeOnOutsideSelect.', fakeAsync(() => {
const fixture = TestBed.createComponent(NestedDialogsComponent);
fixture.detectChanges();
const mainDialog = fixture.componentInstance.main;
const childDialog = fixture.componentInstance.child;
mainDialog.open();
tick();
childDialog.open();
tick();
fixture.detectChanges();
const dialogs = fixture.debugElement.queryAll(By.css('.igx-dialog'));
const maindDialogElem = dialogs[0].nativeElement;
const childDialogElem = dialogs[1].nativeElement;
childDialogElem.click();
tick();
fixture.detectChanges();
expect(mainDialog.isOpen).toEqual(true);
expect(childDialog.isOpen).toEqual(false);
maindDialogElem.click();
tick();
fixture.detectChanges();
expect(mainDialog.isOpen).toEqual(false);
expect(childDialog.isOpen).toEqual(false);
}));
it('Should initialize igx-dialog custom title and actions', () => {
const data = [{
component: CustomTemplates1DialogComponent
}, {
component: CustomTemplates2DialogComponent
}];
data.forEach((item) => {
const fixture = TestBed.createComponent(item.component);
const dialog = fixture.componentInstance.dialog;
dialog.open();
fixture.detectChanges();
const dialogWindow = fixture.debugElement.query(By.css('.igx-dialog__window'));
expect(dialogWindow.children.length).toEqual(3);
expect(dialogWindow.children[0].nativeElement.innerText.toString()).toContain('TITLE');
expect(dialogWindow.children[2].nativeElement.innerText.toString()).toContain('BUTTONS');
dialog.close();
});
});
it('When modal mode is changed, overlay should be informed', fakeAsync(() => {
const fix = TestBed.createComponent(AlertComponent);
fix.detectChanges();
const dialog = fix.componentInstance.dialog;
dialog.open();
tick();
fix.detectChanges();
let overlaydiv = document.getElementsByClassName(OVERLAY_MAIN_CLASS)[0];
let overlayWrapper = overlaydiv.children[0];
expect(overlayWrapper.classList.contains(OVERLAY_WRAPPER_CLASS)).toBe(true);
expect(overlayWrapper.classList.contains(OVERLAY_MODAL_WRAPPER_CLASS)).toBe(false);
dialog.close();
tick();
fix.detectChanges();
fix.componentInstance.isModal = true;
fix.detectChanges();
dialog.open();
tick(16);
fix.detectChanges();
overlaydiv = document.getElementsByClassName(OVERLAY_MAIN_CLASS)[0];
overlayWrapper = overlaydiv.children[0];
expect(overlayWrapper.classList.contains(OVERLAY_MODAL_WRAPPER_CLASS)).toBe(true);
expect(overlayWrapper.classList.contains(OVERLAY_WRAPPER_CLASS)).toBe(true);
}));
it('Default button of the dialog is focused after opening the dialog and can be closed with keyboard.', fakeAsync(() => {
const fix = TestBed.createComponent(DialogComponent);
fix.detectChanges();
const dialog: IgxDialogComponent = fix.componentInstance.dialog as IgxDialogComponent;
dialog.open();
tick(100);
fix.detectChanges();
tick(100);
// Verify dialog is opened and its default right button is focused
const dialogDOM = fix.debugElement.query(By.css('.igx-dialog'));
const rightButton = dialogDOM.queryAll(By.css('button')).filter((b) => b.nativeElement.innerText === 'right button')[0];
expect(document.activeElement).toBe(rightButton.nativeElement);
expect(dialog.isOpen).toEqual(true);
// Press 'escape' key
UIInteractions.triggerKeyDownEvtUponElem('Escape', document.activeElement);
tick(100);
fix.detectChanges();
// Verify dialog is closed and its default right button is no longer focused
expect(document.activeElement).not.toBe(rightButton.nativeElement);
expect(dialog.isOpen).toEqual(false);
}));
describe('Position settings', () => {
let fix;
let dialog;
beforeEach(fakeAsync(() => {
fix = TestBed.createComponent(PositionSettingsDialogComponent);
fix.detectChanges();
dialog = fix.componentInstance.dialog;
}));
it('Define different position settings ', fakeAsync(() => {
const currentElement = fix.componentInstance;
dialog.open();
tick(16);
fix.detectChanges();
expect(dialog.isOpen).toEqual(true);
const firstContentRect = document.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0].getBoundingClientRect();
const middleDialogPosition = document.documentElement.offsetHeight / 2 - firstContentRect.height / 2;
expect(firstContentRect.left).toEqual(0, 'OffsetLeft position check');
expect(firstContentRect.top).toBeGreaterThanOrEqual(middleDialogPosition - 2, 'OffsetTop position check');
expect(firstContentRect.top).toBeLessThanOrEqual(middleDialogPosition + 2, 'OffsetTop position check');
dialog.close();
tick(16);
fix.detectChanges();
expect(dialog.isOpen).toEqual(false);
dialog.positionSettings = currentElement.newPositionSettings;
tick(16);
fix.detectChanges();
dialog.open();
tick(16);
fix.detectChanges();
expect(dialog.isOpen).toEqual(true);
const secondContentRect = document.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0].getBoundingClientRect();
const topDialogPosition = document.documentElement.offsetWidth / 2 - secondContentRect.width / 2;
expect(secondContentRect.top).toEqual(0, 'OffsetTop position check');
expect(secondContentRect.left).toBeGreaterThanOrEqual(topDialogPosition - 2, 'OffsetLeft position check');
expect(secondContentRect.left).toBeLessThanOrEqual(topDialogPosition + 2, 'OffsetLeft position check');
dialog.close();
tick(16);
fix.detectChanges();
expect(dialog.isOpen).toEqual(false);
}));
it('Set animation settings', () => {
const currentElement = fix.componentInstance;
// Check initial animation settings
expect(dialog.positionSettings.openAnimation.animation.type).toEqual(8, 'Animation type is set');
expect(dialog.positionSettings.openAnimation.options.params.duration).toEqual('200ms', 'Animation duration is set to 200ms');
expect(dialog.positionSettings.closeAnimation.animation.type).toEqual(8, 'Animation type is set');
expect(dialog.positionSettings.closeAnimation.options.params.duration).toEqual('200ms', 'Animation duration is set to 200ms');
dialog.positionSettings = currentElement.animationSettings;
fix.detectChanges();
// Check the new animation settings
expect(dialog.positionSettings.openAnimation.options.params.duration).toEqual('800ms', 'Animation duration is set to 800ms');
expect(dialog.positionSettings.closeAnimation.options.params.duration).toEqual('700ms', 'Animation duration is set to 700ms');
});
});
const dispatchEvent = (element: HTMLElement, eventType: string) => {
const event = new Event(eventType);
element.dispatchEvent(event);
};
});
@Component({
template: `
<div #wrapper>
<igx-dialog #dialog
title="alert"
message="message"
closeOnOutsideSelect="true"
leftButtonLabel="OK"
[isModal]="isModal">
</igx-dialog>
</div>`,
standalone: true,
imports: [IgxDialogComponent]
})
class AlertComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
public isModal = false;
}
@Component({
template: `
<div #wrapper>
<igx-dialog #dialog title="dialog" message="message"
leftButtonLabel="left button"
leftButtonType="raised"
leftButtonColor="black"
leftButtonBackgroundColor="darkblue"
leftButtonRipple="pink"
rightButtonLabel="right button"
rightButtonType="raised"
rightButtonColor="orange"
rightButtonBackgroundColor="lightblue"
rightButtonRipple="white">
</igx-dialog>
</div>`,
standalone: true,
imports: [IgxDialogComponent]
})
class DialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
}
@Component({
template: `
<div #wrapper>
<igx-dialog #dialog title="dialog" message="message"
[(isOpen)]="myDialog"
leftButtonLabel="left button"
leftButtonType="raised"
leftButtonColor="black"
leftButtonBackgroundColor="darkblue"
leftButtonRipple="pink"
rightButtonLabel="right button"
rightButtonType="raised"
rightButtonColor="orange"
rightButtonBackgroundColor="lightblue"
rightButtonRipple="white">
</igx-dialog>
</div>`,
standalone: true,
imports: [IgxDialogComponent]
})
class DialogTwoWayDataBindingComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
public myDialog = false;
}
@Component({
template: `
<div #wrapper>
<igx-dialog #dialog
leftButtonLabel="left button"
leftButtonType="raised"
leftButtonColor="black"
leftButtonBackgroundColor="darkblue"
leftButtonRipple="pink"
rightButtonLabel="right button"
rightButtonType="raised"
rightButtonColor="orange"
rightButtonBackgroundColor="lightblue"
rightButtonRipple="white">
<div class="custom-sample">
<h2>Custom Sample</h2>
</div>
</igx-dialog>
</div>`,
standalone: true,
imports: [IgxDialogComponent]
})
class DialogSampleComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
}
@Component({
template: `
<div #wrapper>
<igx-dialog #dialog title="custom-dialog">
<div class="custom-dialog__content">
<input class="custom-dialog__content-input" type="text" />
</div>
</igx-dialog>
<div>`,
standalone: true,
imports: [IgxDialogComponent]
})
class CustomDialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
}
@Component({
template: `
<igx-dialog #main
title="Main Dialog"
leftButtonLabel="Cancel"
rightButtonLabel="Sign In"
[closeOnOutsideSelect]="true">
<igx-dialog #child
title="Child Dialog"
leftButtonLabel="Cancel"
rightButtonLabel="Sign In"
[closeOnOutsideSelect]="true">
</igx-dialog>
</igx-dialog>`,
standalone: true,
imports: [IgxDialogComponent]
})
class NestedDialogsComponent {
@ViewChild('child', { static: true }) public child: IgxDialogComponent;
@ViewChild('main', { static: true }) public main: IgxDialogComponent;
}
@Component({
template: `
<igx-dialog #dialog>
<igx-dialog-title>
<div>TITLE 1</div>
</igx-dialog-title>
<igx-dialog-actions>
<div>BUTTONS 1</div>
</igx-dialog-actions>
</igx-dialog>`,
standalone: true,
imports: [IgxDialogComponent, IgxDialogTitleDirective, IgxDialogActionsDirective]
})
class CustomTemplates1DialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
}
@Component({
template: `
<igx-dialog #dialog>
<div igxDialogTitle>TITLE 2</div>
<div igxDialogActions>BUTTONS 2</div>
</igx-dialog>`,
standalone: true,
imports: [IgxDialogComponent, IgxDialogTitleDirective, IgxDialogActionsDirective]
})
class CustomTemplates2DialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
}
@Component({
template: `
<igx-dialog #dialog title="Notification" message="Your email has been sent successfully!" leftButtonLabel="OK"
[positionSettings]="positionSettings" >
</igx-dialog>`,
standalone: true,
imports: [IgxDialogComponent]
})
class PositionSettingsDialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
public positionSettings: PositionSettings = {
horizontalDirection: HorizontalAlignment.Left,
verticalDirection: VerticalAlignment.Middle,
horizontalStartPoint: HorizontalAlignment.Left,
verticalStartPoint: VerticalAlignment.Middle,
openAnimation: useAnimation(slideInTop, { params: { duration: '200ms' } }),
closeAnimation: useAnimation(slideOutBottom, { params: { duration: '200ms' } })
};
public newPositionSettings: PositionSettings = {
horizontalDirection: HorizontalAlignment.Center,
verticalDirection: VerticalAlignment.Top
};
public animationSettings: PositionSettings = {
openAnimation: useAnimation(slideInTop, { params: { duration: '800ms' } }),
closeAnimation: useAnimation(slideOutBottom, { params: { duration: '700ms' } })
};
}