() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([ManySelects])));

        let fixture: ComponentFixture<ManySelects>;
        let triggers: DebugElement[];
        let options: NodeListOf<HTMLElement>;

        beforeEach(fakeAsync(() => {
            fixture = TestBed.createComponent(ManySelects);
            fixture.detectChanges();
            triggers = fixture.debugElement.queryAll(By.css('.mc-select__trigger'));

            triggers[0].nativeElement.click();
            fixture.detectChanges();
            flush();

            options = overlayContainerElement.querySelectorAll('mc-option');
        }));

        it('should set the option id properly', fakeAsync(() => {
            const firstOptionID = options[0].id;

            expect(options[0].id)
                .toContain('mc-option', `Expected option ID to have the correct prefix.`);
            expect(options[0].id).not.toEqual(options[1].id, `Expected option IDs to be unique.`);

            document.body.click();
            fixture.detectChanges();
            flush();

            triggers[1].nativeElement.click();
            fixture.detectChanges();
            flush();

            options =
                overlayContainerElement.querySelectorAll('mc-option');
            expect(options[0].id)
                .toContain('mc-option', `Expected option ID to have the correct prefix.`);
            expect(options[0].id).not.toEqual(firstOptionID, `Expected option IDs to be unique.`);
            expect(options[0].id).not.toEqual(options[1].id, `Expected option IDs to be unique.`);
        }));
    }


() => configureMcSelectTestingModule([ManySelects])


() => {
            fixture = TestBed.createComponent(ManySelects);
            fixture.detectChanges();
            triggers = fixture.debugElement.queryAll(By.css('.mc-select__trigger'));

            triggers[0].nativeElement.click();
            fixture.detectChanges();
            flush();

            options = overlayContainerElement.querySelectorAll('mc-option');
        }


() => {
            const firstOptionID = options[0].id;

            expect(options[0].id)
                .toContain('mc-option', `Expected option ID to have the correct prefix.`);
            expect(options[0].id).not.toEqual(options[1].id, `Expected option IDs to be unique.`);

            document.body.click();
            fixture.detectChanges();
            flush();

            triggers[1].nativeElement.click();
            fixture.detectChanges();
            flush();

            options =
                overlayContainerElement.querySelectorAll('mc-option');
            expect(options[0].id)
                .toContain('mc-option', `Expected option ID to have the correct prefix.`);
            expect(options[0].id).not.toEqual(firstOptionID, `Expected option IDs to be unique.`);
            expect(options[0].id).not.toEqual(options[1].id, `Expected option IDs to be unique.`);
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([
            SelectWithErrorSibling,
            ThrowsErrorOnInit
        ])));

        it('should not crash the browser when a sibling throws an error on init', fakeAsync(() => {
            // Note that this test can be considered successful if the error being thrown didn't
            // end up crashing the testing setup altogether.
            expect(() => {
                TestBed.createComponent(SelectWithErrorSibling).detectChanges();
            }).toThrowError(new RegExp('Oh no!', 'g'));
        }));
    }


() => configureMcSelectTestingModule([
            SelectWithErrorSibling,
            ThrowsErrorOnInit
        ])


() => {
            // Note that this test can be considered successful if the error being thrown didn't
            // end up crashing the testing setup altogether.
            expect(() => {
                TestBed.createComponent(SelectWithErrorSibling).detectChanges();
            }).toThrowError(new RegExp('Oh no!', 'g'));
        }


() => {
                TestBed.createComponent(SelectWithErrorSibling).detectChanges();
            }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([SelectWithPlainTabindex])));

        it('should complete the stateChanges stream on destroy', () => {
            const fixture = TestBed.createComponent(SelectWithPlainTabindex);
            fixture.detectChanges();

            const debugElement = fixture.debugElement.query(By.directive(McSelect));
            const select = debugElement.componentInstance;

            const spy = jasmine.createSpy('stateChanges complete');
            const subscription = select.stateChanges.subscribe(undefined, undefined, spy);

            fixture.destroy();
            expect(spy).toHaveBeenCalled();
            subscription.unsubscribe();
        });
    }


() => configureMcSelectTestingModule([SelectWithPlainTabindex])


() => {
            const fixture = TestBed.createComponent(SelectWithPlainTabindex);
            fixture.detectChanges();

            const debugElement = fixture.debugElement.query(By.directive(McSelect));
            const select = debugElement.componentInstance;

            const spy = jasmine.createSpy('stateChanges complete');
            const subscription = select.stateChanges.subscribe(undefined, undefined, spy);

            fixture.destroy();
            expect(spy).toHaveBeenCalled();
            subscription.unsubscribe();
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([BasicSelectInitiallyHidden])));

        it('should set the width of the overlay if the element was hidden initially', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectInitiallyHidden);
            fixture.detectChanges();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            trigger.style.width = '200px';
            fixture.componentInstance.isVisible = true;
            fixture.detectChanges();

            trigger.click();
            fixture.detectChanges();
            flush();

            const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
            expect(pane.style.minWidth).toBe('200px');
        }));
    }


() => configureMcSelectTestingModule([BasicSelectInitiallyHidden])


() => {
            const fixture = TestBed.createComponent(BasicSelectInitiallyHidden);
            fixture.detectChanges();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            trigger.style.width = '200px';
            fixture.componentInstance.isVisible = true;
            fixture.detectChanges();

            trigger.click();
            fixture.detectChanges();
            flush();

            const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
            expect(pane.style.minWidth).toBe('200px');
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([BasicSelectNoPlaceholder])));

        it('should set the width of the overlay if there is no placeholder', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectNoPlaceholder);

            fixture.detectChanges();
            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
            expect(parseInt(pane.style.minWidth as string)).toBeGreaterThan(0);
        }));
    }


() => configureMcSelectTestingModule([BasicSelectNoPlaceholder])


() => {
            const fixture = TestBed.createComponent(BasicSelectNoPlaceholder);

            fixture.detectChanges();
            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
            expect(parseInt(pane.style.minWidth as string)).toBeGreaterThan(0);
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([BasicSelectWithTheming])));

        let fixture: ComponentFixture<BasicSelectWithTheming>;

        beforeEach(fakeAsync(() => {
            fixture = TestBed.createComponent(BasicSelectWithTheming);
            fixture.detectChanges();
        }));

        it('should transfer the theme to the select panel', fakeAsync(() => {
            fixture.componentInstance.theme = 'error';
            fixture.componentInstance.select.open();
            fixture.detectChanges();
            flush();

            const panel = overlayContainerElement.querySelector('.mc-select__panel') as HTMLElement;
            expect(panel.classList).toContain('mc-error');
        }));
    }


() => configureMcSelectTestingModule([BasicSelectWithTheming])


() => {
            fixture = TestBed.createComponent(BasicSelectWithTheming);
            fixture.detectChanges();
        }


() => {
            fixture.componentInstance.theme = 'error';
            fixture.componentInstance.select.open();
            fixture.detectChanges();
            flush();

            const panel = overlayContainerElement.querySelector('.mc-select__panel') as HTMLElement;
            expect(panel.classList).toContain('mc-error');
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([InvalidSelectInForm])));

        it('should not throw SelectionModel errors in addition to ngModel errors', fakeAsync(() => {
            const fixture = TestBed.createComponent(InvalidSelectInForm);

            // The first change detection run will throw the "ngModel is missing a name" error.
            expect(() => fixture.detectChanges()).toThrowError(/the name attribute must be set/g);

            // The second run shouldn't throw selection-model related errors.
            expect(() => fixture.detectChanges()).not.toThrow();
        }));
    }


() => configureMcSelectTestingModule([InvalidSelectInForm])


() => {
            const fixture = TestBed.createComponent(InvalidSelectInForm);

            // The first change detection run will throw the "ngModel is missing a name" error.
            expect(() => fixture.detectChanges()).toThrowError(/the name attribute must be set/g);

            // The second run shouldn't throw selection-model related errors.
            expect(() => fixture.detectChanges()).not.toThrow();
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([NgModelCompareWithSelect])));

        let fixture: ComponentFixture<NgModelCompareWithSelect>;
        let instance: NgModelCompareWithSelect;

        beforeEach(fakeAsync(() => {
            fixture = TestBed.createComponent(NgModelCompareWithSelect);
            instance = fixture.componentInstance;
            fixture.detectChanges();
        }));

        describe('comparing by value', () => {

            it('should have a selection', fakeAsync(() => {
                const selectedOption = instance.select.selected as McOption;
                expect(selectedOption.value.value).toEqual('pizza-1');
            }));

            it('should update when making a new selection', fakeAsync(() => {
                instance.options.last.selectViaInteraction();
                fixture.detectChanges();
                flush();

                const selectedOption = instance.select.selected as McOption;
                expect(instance.selectedFood.value).toEqual('tacos-2');
                expect(selectedOption.value.value).toEqual('tacos-2');
            }));
        });

        describe('comparing by reference', () => {
            beforeEach(fakeAsync(() => {
                spyOn(instance, 'compareByReference').and.callThrough();
                instance.useCompareByReference();
                fixture.detectChanges();
                flush();
            }));

            it('should use the comparator', fakeAsync(() => {
                expect(instance.compareByReference).toHaveBeenCalled();
            }));

            it('should initialize with no selection despite having a value', fakeAsync(() => {
                expect(instance.selectedFood.value).toBe('pizza-1');
                expect(instance.select.selected).toBeUndefined();
            }));

            it('should not update the selection if value is copied on change', fakeAsync(() => {
                instance.options.first.selectViaInteraction();
                fixture.detectChanges();
                flush();

                expect(instance.selectedFood.value).toEqual('steak-0');
                expect(instance.select.selected).toBeUndefined();
            }));

            it('should throw an error when using a non-function comparator', fakeAsync(() => {
                instance.useNullComparator();

                expect(() => {
                    fixture.detectChanges();
                }).toThrowError(wrappedErrorMessage(getMcSelectNonFunctionValueError()));
            }));
        });
    }


() => configureMcSelectTestingModule([NgModelCompareWithSelect])


() => {
            fixture = TestBed.createComponent(NgModelCompareWithSelect);
            instance = fixture.componentInstance;
            fixture.detectChanges();
        }


() => {

            it('should have a selection', fakeAsync(() => {
                const selectedOption = instance.select.selected as McOption;
                expect(selectedOption.value.value).toEqual('pizza-1');
            }));

            it('should update when making a new selection', fakeAsync(() => {
                instance.options.last.selectViaInteraction();
                fixture.detectChanges();
                flush();

                const selectedOption = instance.select.selected as McOption;
                expect(instance.selectedFood.value).toEqual('tacos-2');
                expect(selectedOption.value.value).toEqual('tacos-2');
            }));
        }


() => {
                const selectedOption = instance.select.selected as McOption;
                expect(selectedOption.value.value).toEqual('pizza-1');
            }


() => {
                instance.options.last.selectViaInteraction();
                fixture.detectChanges();
                flush();

                const selectedOption = instance.select.selected as McOption;
                expect(instance.selectedFood.value).toEqual('tacos-2');
                expect(selectedOption.value.value).toEqual('tacos-2');
            }


() => {
            beforeEach(fakeAsync(() => {
                spyOn(instance, 'compareByReference').and.callThrough();
                instance.useCompareByReference();
                fixture.detectChanges();
                flush();
            }));

            it('should use the comparator', fakeAsync(() => {
                expect(instance.compareByReference).toHaveBeenCalled();
            }));

            it('should initialize with no selection despite having a value', fakeAsync(() => {
                expect(instance.selectedFood.value).toBe('pizza-1');
                expect(instance.select.selected).toBeUndefined();
            }));

            it('should not update the selection if value is copied on change', fakeAsync(() => {
                instance.options.first.selectViaInteraction();
                fixture.detectChanges();
                flush();

                expect(instance.selectedFood.value).toEqual('steak-0');
                expect(instance.select.selected).toBeUndefined();
            }));

            it('should throw an error when using a non-function comparator', fakeAsync(() => {
                instance.useNullComparator();

                expect(() => {
                    fixture.detectChanges();
                }).toThrowError(wrappedErrorMessage(getMcSelectNonFunctionValueError()));
            }));
        }


() => {
                spyOn(instance, 'compareByReference').and.callThrough();
                instance.useCompareByReference();
                fixture.detectChanges();
                flush();
            }


() => {
                expect(instance.compareByReference).toHaveBeenCalled();
            }


() => {
                expect(instance.selectedFood.value).toBe('pizza-1');
                expect(instance.select.selected).toBeUndefined();
            }


() => {
                instance.options.first.selectViaInteraction();
                fixture.detectChanges();
                flush();

                expect(instance.selectedFood.value).toEqual('steak-0');
                expect(instance.select.selected).toBeUndefined();
            }


() => {
                instance.useNullComparator();

                expect(() => {
                    fixture.detectChanges();
                }).toThrowError(wrappedErrorMessage(getMcSelectNonFunctionValueError()));
            }


() => {
                    fixture.detectChanges();
                }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([SelectEarlyAccessSibling])));

        it('should not throw when trying to access the selected value on init', fakeAsync(() => {
            expect(() => {
                TestBed.createComponent(SelectEarlyAccessSibling).detectChanges();
            }).not.toThrow();
        }));
    }


() => configureMcSelectTestingModule([SelectEarlyAccessSibling])


() => {
            expect(() => {
                TestBed.createComponent(SelectEarlyAccessSibling).detectChanges();
            }).not.toThrow();
        }


() => {
                TestBed.createComponent(SelectEarlyAccessSibling).detectChanges();
            }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([SelectInsideFormGroup])));

        let fixture: ComponentFixture<SelectInsideFormGroup>;
        let testComponent: SelectInsideFormGroup;
        let select: HTMLElement;

        beforeEach(fakeAsync(() => {
            fixture = TestBed.createComponent(SelectInsideFormGroup);
            fixture.detectChanges();
            testComponent = fixture.componentInstance;
            select = fixture.debugElement.query(By.css('mc-select')).nativeElement;
        }));

        it('should not set the invalid class on a clean select', fakeAsync(() => {
            expect(testComponent.formGroup.untouched).toBe(true, 'Expected the form to be untouched.');
            expect(testComponent.formControl.invalid).toBe(false, 'Expected form control to be invalid.');
            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');
        }));

        it('should not appear as invalid if it becomes touched', fakeAsync(() => {
            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');

            testComponent.formControl.markAsTouched();
            fixture.detectChanges();

            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select to appear invalid.');
        }));

        it('should not have the invalid class when the select becomes valid', fakeAsync(() => {
            testComponent.formControl.markAsTouched();
            fixture.detectChanges();

            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select to appear invalid.');

            testComponent.formControl.setValue('pizza-1');
            fixture.detectChanges();
            flush();

            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');
        }));

        it('should appear as invalid when the parent form group is submitted', fakeAsync(() => {
            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');

            dispatchFakeEvent(fixture.debugElement.query(By.css('form')).nativeElement, 'submit');
            fixture.detectChanges();

            expect(select.classList)
                .toContain('mc-invalid', 'Expected select to appear invalid.');
        }));

        xit('should render the error messages when the parent form is submitted', fakeAsync(() => {
            const debugEl = fixture.debugElement.nativeElement;

            expect(debugEl.querySelectorAll('mc-error').length).toBe(0, 'Expected no error messages');

            dispatchFakeEvent(fixture.debugElement.query(By.css('form')).nativeElement, 'submit');
            fixture.detectChanges();

            expect(debugEl.querySelectorAll('mc-error').length).toBe(1, 'Expected one error message');
        }));

        it('should override error matching behavior via injection token', fakeAsync(() => {
            const errorStateMatcher: ErrorStateMatcher = {
                isErrorState: jasmine.createSpy('error state matcher').and.returnValue(true)
            };

            fixture.destroy();

            TestBed.resetTestingModule().configureTestingModule({
                imports: [McSelectModule, ReactiveFormsModule, FormsModule, NoopAnimationsModule],
                declarations: [SelectInsideFormGroup],
                providers: [{ provide: ErrorStateMatcher, useValue: errorStateMatcher }]
            });

            const errorFixture = TestBed.createComponent(SelectInsideFormGroup);
            const component = errorFixture.componentInstance;

            errorFixture.detectChanges();

            expect(component.select.errorState).toBe(true);
            expect(errorStateMatcher.isErrorState).toHaveBeenCalled();
        }));
    }


() => configureMcSelectTestingModule([SelectInsideFormGroup])


() => {
            fixture = TestBed.createComponent(SelectInsideFormGroup);
            fixture.detectChanges();
            testComponent = fixture.componentInstance;
            select = fixture.debugElement.query(By.css('mc-select')).nativeElement;
        }


() => {
            expect(testComponent.formGroup.untouched).toBe(true, 'Expected the form to be untouched.');
            expect(testComponent.formControl.invalid).toBe(false, 'Expected form control to be invalid.');
            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');
        }


() => {
            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');

            testComponent.formControl.markAsTouched();
            fixture.detectChanges();

            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select to appear invalid.');
        }


() => {
            testComponent.formControl.markAsTouched();
            fixture.detectChanges();

            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select to appear invalid.');

            testComponent.formControl.setValue('pizza-1');
            fixture.detectChanges();
            flush();

            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');
        }


() => {
            expect(select.classList)
                .not.toContain('mc-invalid', 'Expected select not to appear invalid.');

            dispatchFakeEvent(fixture.debugElement.query(By.css('form')).nativeElement, 'submit');
            fixture.detectChanges();

            expect(select.classList)
                .toContain('mc-invalid', 'Expected select to appear invalid.');
        }


() => {
            const debugEl = fixture.debugElement.nativeElement;

            expect(debugEl.querySelectorAll('mc-error').length).toBe(0, 'Expected no error messages');

            dispatchFakeEvent(fixture.debugElement.query(By.css('form')).nativeElement, 'submit');
            fixture.detectChanges();

            expect(debugEl.querySelectorAll('mc-error').length).toBe(1, 'Expected one error message');
        }


() => {
            const errorStateMatcher: ErrorStateMatcher = {
                isErrorState: jasmine.createSpy('error state matcher').and.returnValue(true)
            };

            fixture.destroy();

            TestBed.resetTestingModule().configureTestingModule({
                imports: [McSelectModule, ReactiveFormsModule, FormsModule, NoopAnimationsModule],
                declarations: [SelectInsideFormGroup],
                providers: [{ provide: ErrorStateMatcher, useValue: errorStateMatcher }]
            });

            const errorFixture = TestBed.createComponent(SelectInsideFormGroup);
            const component = errorFixture.componentInstance;

            errorFixture.detectChanges();

            expect(component.select.errorState).toBe(true);
            expect(errorStateMatcher.isErrorState).toHaveBeenCalled();
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([CustomErrorBehaviorSelect])));

        it('should be able to override the error matching behavior via an @Input', fakeAsync(() => {
            const fixture = TestBed.createComponent(CustomErrorBehaviorSelect);
            const component = fixture.componentInstance;
            const matcher = jasmine.createSpy('error state matcher').and.returnValue(true);

            fixture.detectChanges();

            expect(component.control.invalid).toBe(false);
            expect(component.select.errorState).toBe(false);

            fixture.componentInstance.errorStateMatcher = { isErrorState: matcher };
            fixture.detectChanges();

            expect(component.select.errorState).toBe(true);
            expect(matcher).toHaveBeenCalled();
        }));
    }


() => configureMcSelectTestingModule([CustomErrorBehaviorSelect])


() => {
            const fixture = TestBed.createComponent(CustomErrorBehaviorSelect);
            const component = fixture.componentInstance;
            const matcher = jasmine.createSpy('error state matcher').and.returnValue(true);

            fixture.detectChanges();

            expect(component.control.invalid).toBe(false);
            expect(component.select.errorState).toBe(false);

            fixture.componentInstance.errorStateMatcher = { isErrorState: matcher };
            fixture.detectChanges();

            expect(component.select.errorState).toBe(true);
            expect(matcher).toHaveBeenCalled();
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([
            SingleSelectWithPreselectedArrayValues
        ])));

        xit('should be able to preselect an array value in single-selection mode', fakeAsync(() => {
            const fixture = TestBed.createComponent(SingleSelectWithPreselectedArrayValues);
            fixture.detectChanges();
            flush();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            expect(trigger.textContent).toContain('Pizza');
            expect(fixture.componentInstance.options.toArray()[1].selected).toBe(true);
        }));
    }


() => configureMcSelectTestingModule([
            SingleSelectWithPreselectedArrayValues
        ])


() => {
            const fixture = TestBed.createComponent(SingleSelectWithPreselectedArrayValues);
            fixture.detectChanges();
            flush();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            expect(trigger.textContent).toContain('Pizza');
            expect(fixture.componentInstance.options.toArray()[1].selected).toBe(true);
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([
            CompWithCustomSelect,
            CustomSelectAccessor
        ])));

        it('should support use inside a custom value accessor', fakeAsync(() => {
            const fixture = TestBed.createComponent(CompWithCustomSelect);
            spyOn(fixture.componentInstance.customAccessor, 'writeValue');
            fixture.detectChanges();

            expect(fixture.componentInstance.customAccessor.select.ngControl)
                .toBeFalsy('Expected mc-select NOT to inherit control from parent value accessor.');
            expect(fixture.componentInstance.customAccessor.writeValue).toHaveBeenCalled();
        }));
    }


() => configureMcSelectTestingModule([
            CompWithCustomSelect,
            CustomSelectAccessor
        ])


() => {
            const fixture = TestBed.createComponent(CompWithCustomSelect);
            spyOn(fixture.componentInstance.customAccessor, 'writeValue');
            fixture.detectChanges();

            expect(fixture.componentInstance.customAccessor.select.ngControl)
                .toBeFalsy('Expected mc-select NOT to inherit control from parent value accessor.');
            expect(fixture.componentInstance.customAccessor.writeValue).toHaveBeenCalled();
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([FalsyValueSelect])));

        it('should be able to programmatically select a falsy option', fakeAsync(() => {
            const fixture = TestBed.createComponent(FalsyValueSelect);

            fixture.detectChanges();
            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.componentInstance.control.setValue(0);
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.options.first.selected)
                .toBe(true, 'Expected first option to be selected');
            expect(overlayContainerElement.querySelectorAll('mc-option')[0].classList)
                .toContain('mc-selected', 'Expected first option to be selected');
        }));
    }


() => configureMcSelectTestingModule([FalsyValueSelect])


() => {
            const fixture = TestBed.createComponent(FalsyValueSelect);

            fixture.detectChanges();
            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.componentInstance.control.setValue(0);
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.options.first.selected)
                .toBe(true, 'Expected first option to be selected');
            expect(overlayContainerElement.querySelectorAll('mc-option')[0].classList)
                .toContain('mc-selected', 'Expected first option to be selected');
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([
            BasicSelectOnPush,
            BasicSelectOnPushPreselected
        ])));

        xit('should set the trigger text based on the value when initialized', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectOnPushPreselected);

            fixture.detectChanges();
            flush();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            fixture.detectChanges();

            expect(trigger.textContent).toContain('Pizza');
        }));

        xit('should update the trigger based on the value', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectOnPush);
            fixture.detectChanges();
            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            fixture.componentInstance.control.setValue('pizza-1');
            fixture.detectChanges();

            expect(trigger.textContent).toContain('Pizza');

            fixture.componentInstance.control.reset();
            fixture.detectChanges();

            expect(trigger.textContent).not.toContain('Pizza');
        }));
    }


() => configureMcSelectTestingModule([
            BasicSelectOnPush,
            BasicSelectOnPushPreselected
        ])


() => {
            const fixture = TestBed.createComponent(BasicSelectOnPushPreselected);

            fixture.detectChanges();
            flush();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            fixture.detectChanges();

            expect(trigger.textContent).toContain('Pizza');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectOnPush);
            fixture.detectChanges();
            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            fixture.componentInstance.control.setValue('pizza-1');
            fixture.detectChanges();

            expect(trigger.textContent).toContain('Pizza');

            fixture.componentInstance.control.reset();
            fixture.detectChanges();

            expect(trigger.textContent).not.toContain('Pizza');
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([SelectWithCustomTrigger])));

        xit('should allow the user to customize the label', fakeAsync(() => {
            const fixture = TestBed.createComponent(SelectWithCustomTrigger);
            fixture.detectChanges();

            fixture.componentInstance.control.setValue('pizza-1');
            fixture.detectChanges();

            const label = fixture.debugElement.query(By.css('.mc-select__matcher')).nativeElement;

            expect(label.textContent).toContain('azziP',
                'Expected the displayed text to be "Pizza" in reverse.');
        }));
    }


() => configureMcSelectTestingModule([SelectWithCustomTrigger])


() => {
            const fixture = TestBed.createComponent(SelectWithCustomTrigger);
            fixture.detectChanges();

            fixture.componentInstance.control.setValue('pizza-1');
            fixture.detectChanges();

            const label = fixture.debugElement.query(By.css('.mc-select__matcher')).nativeElement;

            expect(label.textContent).toContain('azziP',
                'Expected the displayed text to be "Pizza" in reverse.');
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([ResetValuesSelect])));

        let fixture: ComponentFixture<ResetValuesSelect>;
        let trigger: HTMLElement;
        let formField: HTMLElement;
        let options: NodeListOf<HTMLElement>;

        beforeEach(fakeAsync(() => {
            fixture = TestBed.createComponent(ResetValuesSelect);
            fixture.detectChanges();
            trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            formField = fixture.debugElement.query(By.css('.mc-form-field')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            options = overlayContainerElement.querySelectorAll('mc-option');
            options[0].click();
            fixture.detectChanges();
            flush();
        }));

        it('should reset when an option with an undefined value is selected', fakeAsync(() => {
            options[4].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBeUndefined();
            expect(fixture.componentInstance.select.selected).toBeFalsy();
            expect(formField.classList).not.toContain('mc-form-field-should-float');
            expect(trigger.textContent).not.toContain('Undefined');
        }));

        it('should reset when an option with a null value is selected', fakeAsync(() => {
            options[5].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBeNull();
            expect(fixture.componentInstance.select.selected).toBeFalsy();
            expect(formField.classList).not.toContain('mc-form-field-should-float');
            expect(trigger.textContent).not.toContain('Null');
        }));

        it('should reset when a blank option is selected', fakeAsync(() => {
            options[6].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBeUndefined();
            expect(fixture.componentInstance.select.selected).toBeFalsy();
            expect(formField.classList).not.toContain('mc-form-field-should-float');
            expect(trigger.textContent).not.toContain('None');
        }));

        it('should not mark the reset option as selected ', fakeAsync(() => {
            options[5].click();
            fixture.detectChanges();
            flush();

            fixture.componentInstance.select.open();
            fixture.detectChanges();
            flush();

            expect(options[5].classList).not.toContain('mc-selected');
        }));

        it('should not reset when any other falsy option is selected', fakeAsync(() => {
            options[3].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBe(false);
            expect(fixture.componentInstance.select.selected).toBeTruthy();
            expect(trigger.textContent).toContain('Falsy');
        }));

        xit('should not consider the reset values as selected when resetting the form control',
            fakeAsync(() => {
                fixture.componentInstance.control.reset();
                fixture.detectChanges();

                expect(fixture.componentInstance.control.value).toBeNull();
                expect(fixture.componentInstance.select.selected).toBeFalsy();
                expect(trigger.textContent).not.toContain('Null');
                expect(trigger.textContent).not.toContain('Undefined');
        }));
    }


() => configureMcSelectTestingModule([ResetValuesSelect])


() => {
            fixture = TestBed.createComponent(ResetValuesSelect);
            fixture.detectChanges();
            trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            formField = fixture.debugElement.query(By.css('.mc-form-field')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            options = overlayContainerElement.querySelectorAll('mc-option');
            options[0].click();
            fixture.detectChanges();
            flush();
        }


() => {
            options[4].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBeUndefined();
            expect(fixture.componentInstance.select.selected).toBeFalsy();
            expect(formField.classList).not.toContain('mc-form-field-should-float');
            expect(trigger.textContent).not.toContain('Undefined');
        }


() => {
            options[5].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBeNull();
            expect(fixture.componentInstance.select.selected).toBeFalsy();
            expect(formField.classList).not.toContain('mc-form-field-should-float');
            expect(trigger.textContent).not.toContain('Null');
        }


() => {
            options[6].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBeUndefined();
            expect(fixture.componentInstance.select.selected).toBeFalsy();
            expect(formField.classList).not.toContain('mc-form-field-should-float');
            expect(trigger.textContent).not.toContain('None');
        }


() => {
            options[5].click();
            fixture.detectChanges();
            flush();

            fixture.componentInstance.select.open();
            fixture.detectChanges();
            flush();

            expect(options[5].classList).not.toContain('mc-selected');
        }


() => {
            options[3].click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.control.value).toBe(false);
            expect(fixture.componentInstance.select.selected).toBeTruthy();
            expect(trigger.textContent).toContain('Falsy');
        }


() => {
                fixture.componentInstance.control.reset();
                fixture.detectChanges();

                expect(fixture.componentInstance.control.value).toBeNull();
                expect(fixture.componentInstance.select.selected).toBeFalsy();
                expect(trigger.textContent).not.toContain('Null');
                expect(trigger.textContent).not.toContain('Undefined');
        }


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([
            BasicSelectWithoutForms,
            BasicSelectWithoutFormsPreselected,
            BasicSelectWithoutFormsMultiple
        ])));

        it('should set the value when options are clicked', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            expect(fixture.componentInstance.selectedFood).toBeFalsy();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.selectedFood).toBe('steak-0');
            expect(fixture.componentInstance.select.value).toBe('steak-0');
            expect(trigger.textContent).toContain('Steak');

            trigger.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelectorAll('mc-option')[2] as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.selectedFood).toBe('sandwich-2');
            expect(fixture.componentInstance.select.value).toBe('sandwich-2');
            expect(trigger.textContent).toContain('Sandwich');
        }));

        xit('should mark options as selected when the value is set', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            fixture.componentInstance.selectedFood = 'sandwich-2';
            fixture.detectChanges();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            expect(trigger.textContent).toContain('Sandwich');

            trigger.click();
            fixture.detectChanges();

            const option = overlayContainerElement.querySelectorAll('mc-option')[2];

            expect(option.classList).toContain('mc-selected');
            expect(fixture.componentInstance.select.value).toBe('sandwich-2');
        }));

        xit('should reset the label when a null value is set', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            expect(fixture.componentInstance.selectedFood).toBeFalsy();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.selectedFood).toBe('steak-0');
            expect(fixture.componentInstance.select.value).toBe('steak-0');
            expect(trigger.textContent).toContain('Steak');

            fixture.componentInstance.selectedFood = null;
            fixture.detectChanges();

            expect(fixture.componentInstance.select.value).toBeNull();
            expect(trigger.textContent).not.toContain('Steak');
        }));

        xit('should reflect the preselected value', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutFormsPreselected);

            fixture.detectChanges();
            flush();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            fixture.detectChanges();
            expect(trigger.textContent).toContain('Pizza');

            trigger.click();
            fixture.detectChanges();

            const option = overlayContainerElement.querySelectorAll('mc-option')[1];

            expect(option.classList).toContain('mc-selected');
            expect(fixture.componentInstance.select.value).toBe('pizza-1');
        }));

        xit('should be able to select multiple values', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutFormsMultiple);

            fixture.detectChanges();
            expect(fixture.componentInstance.selectedFoods).toBeFalsy();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();

            const options: NodeListOf<HTMLElement> = overlayContainerElement.querySelectorAll('mc-option');

            options[0].click();
            fixture.detectChanges();

            expect(fixture.componentInstance.selectedFoods).toEqual(['steak-0']);
            expect(fixture.componentInstance.select.value).toEqual(['steak-0']);
            expect(trigger.textContent).toContain('Steak');

            options[2].click();
            fixture.detectChanges();

            expect(fixture.componentInstance.selectedFoods).toEqual(['steak-0', 'sandwich-2']);
            expect(fixture.componentInstance.select.value).toEqual(['steak-0', 'sandwich-2']);
            expect(trigger.textContent).toContain('Steak, Sandwich');

            options[1].click();
            fixture.detectChanges();

            expect(fixture.componentInstance.selectedFoods).toEqual(['steak-0', 'pizza-1', 'sandwich-2']);
            expect(fixture.componentInstance.select.value).toEqual(['steak-0', 'pizza-1', 'sandwich-2']);
            expect(trigger.textContent).toContain('Steak, Pizza, Sandwich');
        }));

        it('should restore focus to the host element', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            const select = fixture.debugElement.nativeElement.querySelector('mc-select');

            expect(document.activeElement).toBe(select, 'Expected trigger to be focused.');
        }));

        it('should not restore focus to the host element when clicking outside', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);
            const select = fixture.debugElement.nativeElement.querySelector('mc-select');

            fixture.detectChanges();
            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.detectChanges();
            flush();

            expect(document.activeElement?.classList).toContain('mc-option', 'Expected option to be focused.');

            select.blur(); // Blur manually since the programmatic click might not do it.
            document.body.click();
            fixture.detectChanges();
            flush();

            expect(document.activeElement).not.toBe(select, 'Expected trigger not to be focused.');
        }));

        it('should update the data binding before emitting the change event', fakeAsync(() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);
            const instance = fixture.componentInstance;
            const spy = jasmine.createSpy('change spy');

            fixture.detectChanges();
            instance.select.selectionChange.subscribe(() => spy(instance.selectedFood));

            expect(instance.selectedFood).toBeFalsy();

            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(instance.selectedFood).toBe('steak-0');
            expect(spy).toHaveBeenCalledWith('steak-0');
        }));

    }


() => configureMcSelectTestingModule([
            BasicSelectWithoutForms,
            BasicSelectWithoutFormsPreselected,
            BasicSelectWithoutFormsMultiple
        ])


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            expect(fixture.componentInstance.selectedFood).toBeFalsy();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.selectedFood).toBe('steak-0');
            expect(fixture.componentInstance.select.value).toBe('steak-0');
            expect(trigger.textContent).toContain('Steak');

            trigger.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelectorAll('mc-option')[2] as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.selectedFood).toBe('sandwich-2');
            expect(fixture.componentInstance.select.value).toBe('sandwich-2');
            expect(trigger.textContent).toContain('Sandwich');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            fixture.componentInstance.selectedFood = 'sandwich-2';
            fixture.detectChanges();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            expect(trigger.textContent).toContain('Sandwich');

            trigger.click();
            fixture.detectChanges();

            const option = overlayContainerElement.querySelectorAll('mc-option')[2];

            expect(option.classList).toContain('mc-selected');
            expect(fixture.componentInstance.select.value).toBe('sandwich-2');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            expect(fixture.componentInstance.selectedFood).toBeFalsy();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(fixture.componentInstance.selectedFood).toBe('steak-0');
            expect(fixture.componentInstance.select.value).toBe('steak-0');
            expect(trigger.textContent).toContain('Steak');

            fixture.componentInstance.selectedFood = null;
            fixture.detectChanges();

            expect(fixture.componentInstance.select.value).toBeNull();
            expect(trigger.textContent).not.toContain('Steak');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutFormsPreselected);

            fixture.detectChanges();
            flush();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            fixture.detectChanges();
            expect(trigger.textContent).toContain('Pizza');

            trigger.click();
            fixture.detectChanges();

            const option = overlayContainerElement.querySelectorAll('mc-option')[1];

            expect(option.classList).toContain('mc-selected');
            expect(fixture.componentInstance.select.value).toBe('pizza-1');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutFormsMultiple);

            fixture.detectChanges();
            expect(fixture.componentInstance.selectedFoods).toBeFalsy();

            const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;

            trigger.click();
            fixture.detectChanges();

            const options: NodeListOf<HTMLElement> = overlayContainerElement.querySelectorAll('mc-option');

            options[0].click();
            fixture.detectChanges();

            expect(fixture.componentInstance.selectedFoods).toEqual(['steak-0']);
            expect(fixture.componentInstance.select.value).toEqual(['steak-0']);
            expect(trigger.textContent).toContain('Steak');

            options[2].click();
            fixture.detectChanges();

            expect(fixture.componentInstance.selectedFoods).toEqual(['steak-0', 'sandwich-2']);
            expect(fixture.componentInstance.select.value).toEqual(['steak-0', 'sandwich-2']);
            expect(trigger.textContent).toContain('Steak, Sandwich');

            options[1].click();
            fixture.detectChanges();

            expect(fixture.componentInstance.selectedFoods).toEqual(['steak-0', 'pizza-1', 'sandwich-2']);
            expect(fixture.componentInstance.select.value).toEqual(['steak-0', 'pizza-1', 'sandwich-2']);
            expect(trigger.textContent).toContain('Steak, Pizza, Sandwich');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);

            fixture.detectChanges();
            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            const select = fixture.debugElement.nativeElement.querySelector('mc-select');

            expect(document.activeElement).toBe(select, 'Expected trigger to be focused.');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);
            const select = fixture.debugElement.nativeElement.querySelector('mc-select');

            fixture.detectChanges();
            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.detectChanges();
            flush();

            expect(document.activeElement?.classList).toContain('mc-option', 'Expected option to be focused.');

            select.blur(); // Blur manually since the programmatic click might not do it.
            document.body.click();
            fixture.detectChanges();
            flush();

            expect(document.activeElement).not.toBe(select, 'Expected trigger not to be focused.');
        }


() => {
            const fixture = TestBed.createComponent(BasicSelectWithoutForms);
            const instance = fixture.componentInstance;
            const spy = jasmine.createSpy('change spy');

            fixture.detectChanges();
            instance.select.selectionChange.subscribe(() => spy(instance.selectedFood));

            expect(instance.selectedFood).toBeFalsy();

            fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement.click();
            fixture.detectChanges();
            flush();

            (overlayContainerElement.querySelector('mc-option') as HTMLElement).click();
            fixture.detectChanges();
            flush();

            expect(instance.selectedFood).toBe('steak-0');
            expect(spy).toHaveBeenCalledWith('steak-0');
        }


() => spy(instance.selectedFood)


() => {
        beforeEach(waitForAsync(() => configureMcSelectTestingModule([
            SelectWithoutOptionCentering
        ])));

        let fixture: ComponentFixture<SelectWithoutOptionCentering>;
        let trigger: HTMLElement;

        beforeEach(fakeAsync(() => {
            fixture = TestBed.createComponent(SelectWithoutOptionCentering);
            fixture.detectChanges();
            trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
        }));

        it('should not align the active option with the trigger if centering is disabled',
            fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                // The panel should be scrolled to 0 because centering the option disabled.
                expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);
                // The trigger should contain 'Pizza' because it was preselected
                expect(trigger.textContent).toContain('Pizza');
                // The selected index should be 1 because it was preselected
                expect(fixture.componentInstance.options.toArray()[1].selected).toBe(true);
            }));
    }


() => configureMcSelectTestingModule([
            SelectWithoutOptionCentering
        ])


() => {
            fixture = TestBed.createComponent(SelectWithoutOptionCentering);
            fixture.detectChanges();
            trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
        }


() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                // The panel should be scrolled to 0 because centering the option disabled.
                expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);
                // The trigger should contain 'Pizza' because it was preselected
                expect(trigger.textContent).toContain('Pizza');
                // The selected index should be 1 because it was preselected
                expect(fixture.componentInstance.options.toArray()[1].selected).toBe(true);
            }


() => configureMcSelectTestingModule([
            BasicSelect,
            MultiSelect,
            SelectWithGroups
        ])


() => {
            fixture = TestBed.createComponent(BasicSelect);
            fixture.detectChanges();
            trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            formField = fixture.debugElement.query(By.css('mc-form-field')).nativeElement;
        }


() => {
            beforeEach(fakeAsync(() => {
                // these styles are necessary because we are first testing the overlay's position
                // if there is room for it to open to its full extent in either direction.
                formField.style.position = 'fixed';
                formField.style.top = '285px';
                formField.style.left = '20px';
            }));

            xit('should align the first option with trigger text if no option is selected',
                fakeAsync(() => {
                    // We shouldn't push it too far down for this one, because the default may
                    // end up being too much when running the tests on mobile browsers.
                    formField.style.top = '100px';
                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    // The panel should be scrolled to 0 because centering the option is not possible.
                    expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);
                    checkTriggerAlignedWithOption(0);
                }));

            xit('should align a selected option too high to be centered with the trigger text',
                fakeAsync(() => {
                    // Select the second option, because it can't be scrolled any further downward
                    fixture.componentInstance.control.setValue('pizza-1');
                    fixture.detectChanges();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    // The panel should be scrolled to 0 because centering the option is not possible.
                    expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);
                    checkTriggerAlignedWithOption(1);
                }));

            xit('should align a selected option in the middle with the trigger text', fakeAsync(() => {
                // Select the fifth option, which has enough space to scroll to the center
                fixture.componentInstance.control.setValue('chips-4');
                fixture.detectChanges();
                flush();

                trigger.click();
                fixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                // The selected option should be scrolled to the center of the panel.
                // This will be its original offset from the scrollTop - half the panel height + half
                // the option height. 4 (index) * 48 (option height) = 192px offset from scrollTop
                // 192 - 256/2 + 48/2 = 88px
                expect(scrollContainer.scrollTop)
                    .toEqual(88, `Expected overlay panel to be scrolled to center the selected option.`);

                checkTriggerAlignedWithOption(4);
            }));

            xit('should align a selected option at the scroll max with the trigger text', fakeAsync(() => {
                // Select the last option in the list
                fixture.componentInstance.control.setValue('sushi-7');
                fixture.detectChanges();
                flush();

                trigger.click();
                fixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                // The selected option should be scrolled to the max scroll position.
                // This will be the height of the scrollContainer - the panel height.
                // 8 options * 48px = 384 scrollContainer height, 384 - 256 = 128px max scroll
                expect(scrollContainer.scrollTop)
                    .toEqual(128, `Expected overlay panel to be scrolled to its maximum position.`);

                checkTriggerAlignedWithOption(7);
            }));

            xit('should account for preceding label groups when aligning the option', fakeAsync(() => {
                // Test is off-by-one on edge for some reason, but verified that it looks correct through
                // manual testing.
                if (platform.EDGE) { return; }

                fixture.destroy();

                const groupFixture = TestBed.createComponent(SelectWithGroups);
                groupFixture.detectChanges();
                trigger = groupFixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
                formField = groupFixture.debugElement.query(By.css('mc-form-field')).nativeElement;

                formField.style.position = 'fixed';
                formField.style.top = '200px';
                formField.style.left = '100px';

                // Select an option in the third group, which has a couple of group labels before it.
                groupFixture.componentInstance.control.setValue('vulpix-7');
                groupFixture.detectChanges();

                trigger.click();
                groupFixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__content')!;

                // The selected option should be scrolled to the center of the panel.
                // This will be its original offset from the scrollTop - half the panel height + half the
                // option height. 10 (option index + 3 group labels before it) * 48 (option height) = 480
                // 480 (offset from scrollTop) - 256/2 + 48/2 = 376px
                expect(Math.floor(scrollContainer.scrollTop))
                    .toBe(376, `Expected overlay panel to be scrolled to center the selected option.`);

                checkTriggerAlignedWithOption(7, groupFixture.componentInstance.select);
            }));
        }


() => {
                // these styles are necessary because we are first testing the overlay's position
                // if there is room for it to open to its full extent in either direction.
                formField.style.position = 'fixed';
                formField.style.top = '285px';
                formField.style.left = '20px';
            }


() => {
                    // We shouldn't push it too far down for this one, because the default may
                    // end up being too much when running the tests on mobile browsers.
                    formField.style.top = '100px';
                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    // The panel should be scrolled to 0 because centering the option is not possible.
                    expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);
                    checkTriggerAlignedWithOption(0);
                }


() => {
                    // Select the second option, because it can't be scrolled any further downward
                    fixture.componentInstance.control.setValue('pizza-1');
                    fixture.detectChanges();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    // The panel should be scrolled to 0 because centering the option is not possible.
                    expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);
                    checkTriggerAlignedWithOption(1);
                }


() => {
                // Select the fifth option, which has enough space to scroll to the center
                fixture.componentInstance.control.setValue('chips-4');
                fixture.detectChanges();
                flush();

                trigger.click();
                fixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                // The selected option should be scrolled to the center of the panel.
                // This will be its original offset from the scrollTop - half the panel height + half
                // the option height. 4 (index) * 48 (option height) = 192px offset from scrollTop
                // 192 - 256/2 + 48/2 = 88px
                expect(scrollContainer.scrollTop)
                    .toEqual(88, `Expected overlay panel to be scrolled to center the selected option.`);

                checkTriggerAlignedWithOption(4);
            }


() => {
                // Select the last option in the list
                fixture.componentInstance.control.setValue('sushi-7');
                fixture.detectChanges();
                flush();

                trigger.click();
                fixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                // The selected option should be scrolled to the max scroll position.
                // This will be the height of the scrollContainer - the panel height.
                // 8 options * 48px = 384 scrollContainer height, 384 - 256 = 128px max scroll
                expect(scrollContainer.scrollTop)
                    .toEqual(128, `Expected overlay panel to be scrolled to its maximum position.`);

                checkTriggerAlignedWithOption(7);
            }


() => {
                // Test is off-by-one on edge for some reason, but verified that it looks correct through
                // manual testing.
                if (platform.EDGE) { return; }

                fixture.destroy();

                const groupFixture = TestBed.createComponent(SelectWithGroups);
                groupFixture.detectChanges();
                trigger = groupFixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
                formField = groupFixture.debugElement.query(By.css('mc-form-field')).nativeElement;

                formField.style.position = 'fixed';
                formField.style.top = '200px';
                formField.style.left = '100px';

                // Select an option in the third group, which has a couple of group labels before it.
                groupFixture.componentInstance.control.setValue('vulpix-7');
                groupFixture.detectChanges();

                trigger.click();
                groupFixture.detectChanges();
                flush();

                const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__content')!;

                // The selected option should be scrolled to the center of the panel.
                // This will be its original offset from the scrollTop - half the panel height + half the
                // option height. 10 (option index + 3 group labels before it) * 48 (option height) = 480
                // 480 (offset from scrollTop) - 256/2 + 48/2 = 376px
                expect(Math.floor(scrollContainer.scrollTop))
                    .toBe(376, `Expected overlay panel to be scrolled to center the selected option.`);

                checkTriggerAlignedWithOption(7, groupFixture.componentInstance.select);
            }


() => {
            beforeEach(fakeAsync(() => {
                formField.style.position = 'fixed';
                formField.style.left = '20px';
            }));

            xit('should adjust position of centered option if there is little space above',
                fakeAsync(() => {
                    const selectMenuHeight = 256;
                    const selectMenuViewportPadding = 8;
                    const selectItemHeight = 48;
                    const selectedIndex = 4;
                    const fontSize = 16;
                    const lineHeightEm = 1.125;
                    const expectedExtraScroll = 5;

                    // Trigger element height.
                    const triggerHeight = fontSize * lineHeightEm;

                    // Ideal space above selected item in order to center it.
                    const idealSpaceAboveSelectedItem = (selectMenuHeight - selectItemHeight) / 2;

                    // Actual space above selected item.
                    const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex;

                    // Ideal scroll position to center.
                    const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem;

                    // Top-most select-position that allows for perfect centering.
                    const topMostPositionForPerfectCentering =
                        idealSpaceAboveSelectedItem + selectMenuViewportPadding +
                        (selectItemHeight - triggerHeight) / 2;

                    // Position of select relative to top edge of mc-form-field.
                    const formFieldTopSpace =
                        trigger.getBoundingClientRect().top - formField.getBoundingClientRect().top;

                    const formFieldTop =
                        topMostPositionForPerfectCentering - formFieldTopSpace - expectedExtraScroll;

                    formField.style.top = `${formFieldTop}px`;

                    // Select an option in the middle of the list
                    fixture.componentInstance.control.setValue('chips-4');
                    fixture.detectChanges();
                    flush();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    expect(Math.ceil(scrollContainer.scrollTop))
                        .toEqual(Math.ceil(idealScrollTop + 5),
                            `Expected panel to adjust scroll position to fit in viewport.`);

                    checkTriggerAlignedWithOption(4);
                }));

            xit('should adjust position of centered option if there is little space below',
                fakeAsync(() => {
                    const selectMenuHeight = 256;
                    const selectMenuViewportPadding = 8;
                    const selectItemHeight = 48;
                    const selectedIndex = 4;
                    const fontSize = 16;
                    const lineHeightEm = 1.125;
                    const expectedExtraScroll = 5;

                    // Trigger element height.
                    const triggerHeight = fontSize * lineHeightEm;

                    // Ideal space above selected item in order to center it.
                    const idealSpaceAboveSelectedItem = (selectMenuHeight - selectItemHeight) / 2;

                    // Actual space above selected item.
                    const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex;

                    // Ideal scroll position to center.
                    const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem;

                    // Bottom-most select-position that allows for perfect centering.
                    const bottomMostPositionForPerfectCentering =
                        idealSpaceAboveSelectedItem + selectMenuViewportPadding +
                        (selectItemHeight - triggerHeight) / 2;

                    // Position of select relative to bottom edge of mc-form-field:
                    const formFieldBottomSpace =
                        formField.getBoundingClientRect().bottom - trigger.getBoundingClientRect().bottom;

                    const formFieldBottom =
                        bottomMostPositionForPerfectCentering - formFieldBottomSpace - expectedExtraScroll;

                    // Push the select to a position with not quite enough space on the bottom to open
                    // with the option completely centered (needs 113px at least: 256/2 - 48/2 + 9)
                    formField.style.bottom = `${formFieldBottom}px`;

                    // Select an option in the middle of the list
                    fixture.componentInstance.control.setValue('chips-4');
                    fixture.detectChanges();
                    flush();

                    fixture.detectChanges();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    // Scroll should adjust by the difference between the bottom space available
                    // (56px from the bottom of the screen - 8px padding = 48px)
                    // and the height of the panel below the option (113px).
                    // 113px - 48px = 75px difference. Original scrollTop 88px - 75px = 23px
                    const difference = Math.ceil(scrollContainer.scrollTop) -
                        Math.ceil(idealScrollTop - expectedExtraScroll);

                    // Note that different browser/OS combinations report the different dimensions with
                    // slight deviations (< 1px). We round the expectation and check that the values
                    // are within a pixel of each other to avoid flakes.
                    expect(Math.abs(difference) < 2)
                        .toBe(true, `Expected panel to adjust scroll position to fit in viewport.`);

                    checkTriggerAlignedWithOption(4);
                }));

            xit('should fall back to "above" positioning if scroll adjustment will not help',
                fakeAsync(() => {
                    // Push the select to a position with not enough space on the bottom to open
                    formField.style.bottom = '56px';
                    fixture.detectChanges();

                    // Select an option that cannot be scrolled any farther upward
                    fixture.componentInstance.control.setValue('coke-0');
                    fixture.detectChanges();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const overlayPane = document.querySelector('.cdk-overlay-pane')!;
                    const triggerBottom: number = trigger.getBoundingClientRect().bottom;
                    const overlayBottom: number = overlayPane.getBoundingClientRect().bottom;
                    const scrollContainer = overlayPane.querySelector('.mc-select__panel')!;

                    // Expect no scroll to be attempted
                    expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);

                    const difference = Math.floor(overlayBottom) - Math.floor(triggerBottom);

                    // Check that the values are within a pixel of each other. This avoids sub-pixel
                    // deviations between OS and browser versions.
                    expect(Math.abs(difference) < 2)
                        .toEqual(true, `Expected trigger bottom to align with overlay bottom.`);

                    expect(fixture.componentInstance.select.transformOrigin)
                        .toContain(`bottom`, `Expected panel animation to originate at the bottom.`);
                }));

            xit('should fall back to "below" positioning if scroll adjustment won\'t help',
                fakeAsync(() => {
                    // Push the select to a position with not enough space on the top to open
                    formField.style.top = '85px';

                    // Select an option that cannot be scrolled any farther downward
                    fixture.componentInstance.control.setValue('sushi-7');
                    fixture.detectChanges();
                    flush();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const overlayPane = document.querySelector('.cdk-overlay-pane')!;
                    const triggerTop: number = trigger.getBoundingClientRect().top;
                    const overlayTop: number = overlayPane.getBoundingClientRect().top;
                    const scrollContainer = overlayPane.querySelector('.mc-select__panel')!;

                    // Expect scroll to remain at the max scroll position
                    expect(scrollContainer.scrollTop)
                        .toEqual(128, `Expected panel to be at max scroll.`);

                    expect(Math.floor(overlayTop))
                        .toEqual(Math.floor(triggerTop), `Expected trigger top to align with overlay top.`);

                    expect(fixture.componentInstance.select.transformOrigin)
                        .toContain(`top`, `Expected panel animation to originate at the top.`);
                }));
        }


() => {
                formField.style.position = 'fixed';
                formField.style.left = '20px';
            }


() => {
                    const selectMenuHeight = 256;
                    const selectMenuViewportPadding = 8;
                    const selectItemHeight = 48;
                    const selectedIndex = 4;
                    const fontSize = 16;
                    const lineHeightEm = 1.125;
                    const expectedExtraScroll = 5;

                    // Trigger element height.
                    const triggerHeight = fontSize * lineHeightEm;

                    // Ideal space above selected item in order to center it.
                    const idealSpaceAboveSelectedItem = (selectMenuHeight - selectItemHeight) / 2;

                    // Actual space above selected item.
                    const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex;

                    // Ideal scroll position to center.
                    const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem;

                    // Top-most select-position that allows for perfect centering.
                    const topMostPositionForPerfectCentering =
                        idealSpaceAboveSelectedItem + selectMenuViewportPadding +
                        (selectItemHeight - triggerHeight) / 2;

                    // Position of select relative to top edge of mc-form-field.
                    const formFieldTopSpace =
                        trigger.getBoundingClientRect().top - formField.getBoundingClientRect().top;

                    const formFieldTop =
                        topMostPositionForPerfectCentering - formFieldTopSpace - expectedExtraScroll;

                    formField.style.top = `${formFieldTop}px`;

                    // Select an option in the middle of the list
                    fixture.componentInstance.control.setValue('chips-4');
                    fixture.detectChanges();
                    flush();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    expect(Math.ceil(scrollContainer.scrollTop))
                        .toEqual(Math.ceil(idealScrollTop + 5),
                            `Expected panel to adjust scroll position to fit in viewport.`);

                    checkTriggerAlignedWithOption(4);
                }


() => {
                    const selectMenuHeight = 256;
                    const selectMenuViewportPadding = 8;
                    const selectItemHeight = 48;
                    const selectedIndex = 4;
                    const fontSize = 16;
                    const lineHeightEm = 1.125;
                    const expectedExtraScroll = 5;

                    // Trigger element height.
                    const triggerHeight = fontSize * lineHeightEm;

                    // Ideal space above selected item in order to center it.
                    const idealSpaceAboveSelectedItem = (selectMenuHeight - selectItemHeight) / 2;

                    // Actual space above selected item.
                    const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex;

                    // Ideal scroll position to center.
                    const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem;

                    // Bottom-most select-position that allows for perfect centering.
                    const bottomMostPositionForPerfectCentering =
                        idealSpaceAboveSelectedItem + selectMenuViewportPadding +
                        (selectItemHeight - triggerHeight) / 2;

                    // Position of select relative to bottom edge of mc-form-field:
                    const formFieldBottomSpace =
                        formField.getBoundingClientRect().bottom - trigger.getBoundingClientRect().bottom;

                    const formFieldBottom =
                        bottomMostPositionForPerfectCentering - formFieldBottomSpace - expectedExtraScroll;

                    // Push the select to a position with not quite enough space on the bottom to open
                    // with the option completely centered (needs 113px at least: 256/2 - 48/2 + 9)
                    formField.style.bottom = `${formFieldBottom}px`;

                    // Select an option in the middle of the list
                    fixture.componentInstance.control.setValue('chips-4');
                    fixture.detectChanges();
                    flush();

                    fixture.detectChanges();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const scrollContainer = document.querySelector('.cdk-overlay-pane .mc-select__panel')!;

                    // Scroll should adjust by the difference between the bottom space available
                    // (56px from the bottom of the screen - 8px padding = 48px)
                    // and the height of the panel below the option (113px).
                    // 113px - 48px = 75px difference. Original scrollTop 88px - 75px = 23px
                    const difference = Math.ceil(scrollContainer.scrollTop) -
                        Math.ceil(idealScrollTop - expectedExtraScroll);

                    // Note that different browser/OS combinations report the different dimensions with
                    // slight deviations (< 1px). We round the expectation and check that the values
                    // are within a pixel of each other to avoid flakes.
                    expect(Math.abs(difference) < 2)
                        .toBe(true, `Expected panel to adjust scroll position to fit in viewport.`);

                    checkTriggerAlignedWithOption(4);
                }


() => {
                    // Push the select to a position with not enough space on the bottom to open
                    formField.style.bottom = '56px';
                    fixture.detectChanges();

                    // Select an option that cannot be scrolled any farther upward
                    fixture.componentInstance.control.setValue('coke-0');
                    fixture.detectChanges();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const overlayPane = document.querySelector('.cdk-overlay-pane')!;
                    const triggerBottom: number = trigger.getBoundingClientRect().bottom;
                    const overlayBottom: number = overlayPane.getBoundingClientRect().bottom;
                    const scrollContainer = overlayPane.querySelector('.mc-select__panel')!;

                    // Expect no scroll to be attempted
                    expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`);

                    const difference = Math.floor(overlayBottom) - Math.floor(triggerBottom);

                    // Check that the values are within a pixel of each other. This avoids sub-pixel
                    // deviations between OS and browser versions.
                    expect(Math.abs(difference) < 2)
                        .toEqual(true, `Expected trigger bottom to align with overlay bottom.`);

                    expect(fixture.componentInstance.select.transformOrigin)
                        .toContain(`bottom`, `Expected panel animation to originate at the bottom.`);
                }


() => {
                    // Push the select to a position with not enough space on the top to open
                    formField.style.top = '85px';

                    // Select an option that cannot be scrolled any farther downward
                    fixture.componentInstance.control.setValue('sushi-7');
                    fixture.detectChanges();
                    flush();

                    trigger.click();
                    fixture.detectChanges();
                    flush();

                    const overlayPane = document.querySelector('.cdk-overlay-pane')!;
                    const triggerTop: number = trigger.getBoundingClientRect().top;
                    const overlayTop: number = overlayPane.getBoundingClientRect().top;
                    const scrollContainer = overlayPane.querySelector('.mc-select__panel')!;

                    // Expect scroll to remain at the max scroll position
                    expect(scrollContainer.scrollTop)
                        .toEqual(128, `Expected panel to be at max scroll.`);

                    expect(Math.floor(overlayTop))
                        .toEqual(Math.floor(triggerTop), `Expected trigger top to align with overlay top.`);

                    expect(fixture.componentInstance.select.transformOrigin)
                        .toContain(`top`, `Expected panel animation to originate at the top.`);
                }


() => {
            const startingWindowHeight = window.innerHeight;

            // Need to set the scrollTop two different ways to support
            // both Chrome and Firefox.
            function setScrollTop(num: number) {
                document.body.scrollTop = num;
                document.documentElement.scrollTop = num;
            }

            beforeEach(fakeAsync(() => {
                // Make the div above the select very tall, so the page will scroll
                fixture.componentInstance.heightAbove = 2000;
                fixture.detectChanges();
                setScrollTop(0);

                // Give the select enough horizontal space to open
                formField.style.marginLeft = '20px';
                formField.style.marginRight = '20px';
            }));

            xit('should fall back to "above" positioning properly when scrolled', fakeAsync(() => {
                // Give the select insufficient space to open below the trigger
                fixture.componentInstance.heightAbove = 0;
                fixture.componentInstance.heightBelow = 100;
                trigger.style.marginTop = '2000px';
                fixture.detectChanges();

                // Scroll the select into view
                setScrollTop(1400);

                // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the
                // body causes karma's iframe for the test to stretch to fit that content once we attempt to
                // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not
                // successfully constrain its size. As such, skip assertions in environments where the
                // window size has changed since the start of the test.
                if (window.innerHeight > startingWindowHeight) {
                    return;
                }

                trigger.click();
                fixture.detectChanges();
                flush();

                const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
                const triggerBottom: number = trigger.getBoundingClientRect().bottom;
                const overlayBottom: number = overlayPane.getBoundingClientRect().bottom;
                const difference = Math.floor(overlayBottom) - Math.floor(triggerBottom);

                // Check that the values are within a pixel of each other. This avoids sub-pixel
                // deviations between OS and browser versions.
                expect(Math.abs(difference) < 2)
                    .toEqual(true, `Expected trigger bottom to align with overlay bottom.`);
            }));

            xit('should fall back to "below" positioning properly when scrolled', fakeAsync(() => {
                // Give plenty of space for the select to open below the trigger
                fixture.componentInstance.heightBelow = 650;
                fixture.detectChanges();

                // Select an option too low in the list to fit in limited space above
                fixture.componentInstance.control.setValue('sushi-7');
                fixture.detectChanges();

                // Scroll the select so that it has insufficient space to open above the trigger
                setScrollTop(1950);

                // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the
                // body causes karma's iframe for the test to stretch to fit that content once we attempt to
                // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not
                // successfully constrain its size. As such, skip assertions in environments where the
                // window size has changed since the start of the test.
                if (window.innerHeight > startingWindowHeight) { return; }

                trigger.click();
                fixture.detectChanges();
                flush();

                const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
                const triggerTop: number = trigger.getBoundingClientRect().top;
                const overlayTop: number = overlayPane.getBoundingClientRect().top;

                expect(Math.floor(overlayTop))
                    .toEqual(Math.floor(triggerTop), `Expected trigger top to align with overlay top.`);
            }));
        }


() => {
                // Make the div above the select very tall, so the page will scroll
                fixture.componentInstance.heightAbove = 2000;
                fixture.detectChanges();
                setScrollTop(0);

                // Give the select enough horizontal space to open
                formField.style.marginLeft = '20px';
                formField.style.marginRight = '20px';
            }


() => {
                // Give the select insufficient space to open below the trigger
                fixture.componentInstance.heightAbove = 0;
                fixture.componentInstance.heightBelow = 100;
                trigger.style.marginTop = '2000px';
                fixture.detectChanges();

                // Scroll the select into view
                setScrollTop(1400);

                // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the
                // body causes karma's iframe for the test to stretch to fit that content once we attempt to
                // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not
                // successfully constrain its size. As such, skip assertions in environments where the
                // window size has changed since the start of the test.
                if (window.innerHeight > startingWindowHeight) {
                    return;
                }

                trigger.click();
                fixture.detectChanges();
                flush();

                const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
                const triggerBottom: number = trigger.getBoundingClientRect().bottom;
                const overlayBottom: number = overlayPane.getBoundingClientRect().bottom;
                const difference = Math.floor(overlayBottom) - Math.floor(triggerBottom);

                // Check that the values are within a pixel of each other. This avoids sub-pixel
                // deviations between OS and browser versions.
                expect(Math.abs(difference) < 2)
                    .toEqual(true, `Expected trigger bottom to align with overlay bottom.`);
            }


() => {
                // Give plenty of space for the select to open below the trigger
                fixture.componentInstance.heightBelow = 650;
                fixture.detectChanges();

                // Select an option too low in the list to fit in limited space above
                fixture.componentInstance.control.setValue('sushi-7');
                fixture.detectChanges();

                // Scroll the select so that it has insufficient space to open above the trigger
                setScrollTop(1950);

                // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the
                // body causes karma's iframe for the test to stretch to fit that content once we attempt to
                // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not
                // successfully constrain its size. As such, skip assertions in environments where the
                // window size has changed since the start of the test.
                if (window.innerHeight > startingWindowHeight) { return; }

                trigger.click();
                fixture.detectChanges();
                flush();

                const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
                const triggerTop: number = trigger.getBoundingClientRect().top;
                const overlayTop: number = overlayPane.getBoundingClientRect().top;

                expect(Math.floor(overlayTop))
                    .toEqual(Math.floor(triggerTop), `Expected trigger top to align with overlay top.`);
            }


() => {
            beforeEach(fakeAsync(() => {
                formField.style.position = 'fixed';
                formField.style.left = '30px';
            }));

            xit('should align the trigger and the selected option on the x-axis in ltr', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                const triggerLeft: number = trigger.getBoundingClientRect().left;
                const firstOptionLeft = document.querySelector('.cdk-overlay-pane mc-option')!
                    .getBoundingClientRect().left;

                // Each option is 32px wider than the trigger, so it must be adjusted 16px
                // to ensure the text overlaps correctly.
                expect(Math.floor(firstOptionLeft)).toEqual(Math.floor(triggerLeft - 16),
                    `Expected trigger to align with the selected option on the x-axis in LTR.`);
            }));

            xit('should align the trigger and the selected option on the x-axis in rtl', fakeAsync(() => {
                dir.value = 'rtl';
                fixture.detectChanges();

                trigger.click();
                fixture.detectChanges();
                flush();

                const triggerRight: number = trigger.getBoundingClientRect().right;
                const firstOptionRight = document.querySelector('.cdk-overlay-pane mc-option')!
                    .getBoundingClientRect().right;

                // Each option is 32px wider than the trigger, so it must be adjusted 16px
                // to ensure the text overlaps correctly.
                expect(Math.floor(firstOptionRight))
                    .toEqual(Math.floor(triggerRight + 16),
                        `Expected trigger to align with the selected option on the x-axis in RTL.`);
            }));
        }


() => {
                formField.style.position = 'fixed';
                formField.style.left = '30px';
            }