export class ScalingInput extends React.Component<ScalingInputProps, ScalingInputState> {
    public static defaultProps = {
        onChange: util.boundNoop,
        onFontSizeChange: util.boundNoop,
        maxLength: 7,
        scalingSettings: defaultScalingSettings,
        isDisabled: false,
        hasAutofocus: false,
    };
    public state: ScalingInputState = {
        inputWidthPxAtPhaseChange: undefined,
    };
    private readonly _inputRef = React.createRef<HTMLInputElement>();
    public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase {
        if (value.length <= textLengthThreshold) {
            return ScalingInputPhase.FixedFontSize;
        }
        return ScalingInputPhase.ScalingFontSize;
    }
    public static getPhaseFromProps(props: ScalingInputProps): ScalingInputPhase {
        const { value, textLengthThreshold } = props;
        return ScalingInput.getPhase(textLengthThreshold, value);
    }
    public static calculateFontSize(
        textLengthThreshold: number,
        maxFontSizePx: number,
        phase: ScalingInputPhase,
        value: string,
        percentageToReduceFontSizePerCharacter: number,
    ): number {
        if (phase !== ScalingInputPhase.ScalingFontSize) {
            return maxFontSizePx;
        }
        const charactersOverMax = value.length - textLengthThreshold;
        const scalingFactor = (1 - percentageToReduceFontSizePerCharacter) ** charactersOverMax;
        const fontSize = scalingFactor * maxFontSizePx;
        return fontSize;
    }
    public static calculateFontSizeFromProps(props: ScalingInputProps, phase: ScalingInputPhase): number {
        const { textLengthThreshold, value, maxFontSizePx, scalingSettings } = props;
        return ScalingInput.calculateFontSize(
            textLengthThreshold,
            maxFontSizePx,
            phase,
            value,
            scalingSettings.percentageToReduceFontSizePerCharacter,
        );
    }
    public getSnapshotBeforeUpdate(): ScalingInputSnapshot {
        return {
            inputWidthPx: this._getInputWidthInPx(),
        };
    }
    public componentDidMount(): void {
        // Trigger an initial notification of the calculated fontSize.
        const currentPhase = ScalingInput.getPhaseFromProps(this.props);
        const currentFontSize = ScalingInput.calculateFontSizeFromProps(this.props, currentPhase);
        this.props.onFontSizeChange(currentFontSize);
    }
    public componentDidUpdate(
        prevProps: ScalingInputProps,
        prevState: ScalingInputState,
        snapshot: ScalingInputSnapshot,
    ): void {
        const prevPhase = ScalingInput.getPhaseFromProps(prevProps);
        const curPhase = ScalingInput.getPhaseFromProps(this.props);
        // if we went from fixed to scaling, save the width from the transition
        if (prevPhase !== ScalingInputPhase.ScalingFontSize && curPhase === ScalingInputPhase.ScalingFontSize) {
            this.setState({
                inputWidthPxAtPhaseChange: snapshot.inputWidthPx,
            });
        }
        // if we went from scaling to fixed, revert back to scaling using `ch`
        if (prevPhase === ScalingInputPhase.ScalingFontSize && curPhase !== ScalingInputPhase.ScalingFontSize) {
            this.setState({
                inputWidthPxAtPhaseChange: undefined,
            });
        }
        const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase);
        const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase);
        // If font size has changed, notify.
        if (prevFontSize !== curFontSize) {
            this.props.onFontSizeChange(curFontSize);
        }
    }
    public render(): React.ReactNode {
        const { hasAutofocus, isDisabled, fontColor, onChange, placeholder, value, maxLength } = this.props;
        const phase = ScalingInput.getPhaseFromProps(this.props);
        return (
            <Input
                ref={this._inputRef as any}
                fontColor={fontColor}
                onChange={onChange}
                value={value}
                placeholder={placeholder}
                fontSize={`${this._calculateFontSize(phase)}px`}
                width={this._calculateWidth(phase)}
                maxLength={maxLength}
                disabled={isDisabled}
                autoFocus={hasAutofocus}
            />
        );
    }


export interface ScalingSettings {
    percentageToReduceFontSizePerCharacter: number;
    constantPxToIncreaseWidthPerCharacter: number;
}


export interface ScalingInputProps {
    textLengthThreshold: number;
    maxFontSizePx: number;
    value: string;
    emptyInputWidthCh: number;
    onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
    onFontSizeChange: (fontSizePx: number) => void;
    fontColor?: ColorOption;
    placeholder?: string;
    maxLength?: number;
    scalingSettings: ScalingSettings;
    isDisabled: boolean;
    hasAutofocus: boolean;
}


export interface ScalingInputState {
    inputWidthPxAtPhaseChange?: number;
}


export interface ScalingInputSnapshot {
    inputWidthPx: number;
}


export enum ScalingInputPhase {
    FixedFontSize,
    ScalingFontSize,
}


public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase {
        if (value.length <= textLengthThreshold) {
            return ScalingInputPhase.FixedFontSize;
        }
        return ScalingInputPhase.ScalingFontSize;
    }


public static getPhaseFromProps(props: ScalingInputProps): ScalingInputPhase {
        const { value, textLengthThreshold } = props;
        return ScalingInput.getPhase(textLengthThreshold, value);
    }


public static calculateFontSize(
        textLengthThreshold: number,
        maxFontSizePx: number,
        phase: ScalingInputPhase,
        value: string,
        percentageToReduceFontSizePerCharacter: number,
    ): number {
        if (phase !== ScalingInputPhase.ScalingFontSize) {
            return maxFontSizePx;
        }
        const charactersOverMax = value.length - textLengthThreshold;
        const scalingFactor = (1 - percentageToReduceFontSizePerCharacter) ** charactersOverMax;
        const fontSize = scalingFactor * maxFontSizePx;
        return fontSize;
    }


public static calculateFontSizeFromProps(props: ScalingInputProps, phase: ScalingInputPhase): number {
        const { textLengthThreshold, value, maxFontSizePx, scalingSettings } = props;
        return ScalingInput.calculateFontSize(
            textLengthThreshold,
            maxFontSizePx,
            phase,
            value,
            scalingSettings.percentageToReduceFontSizePerCharacter,
        );
    }


public getSnapshotBeforeUpdate(): ScalingInputSnapshot {
        return {
            inputWidthPx: this._getInputWidthInPx(),
        };
    }


public componentDidMount(): void {
        // Trigger an initial notification of the calculated fontSize.
        const currentPhase = ScalingInput.getPhaseFromProps(this.props);
        const currentFontSize = ScalingInput.calculateFontSizeFromProps(this.props, currentPhase);
        this.props.onFontSizeChange(currentFontSize);
    }


public componentDidUpdate(
        prevProps: ScalingInputProps,
        prevState: ScalingInputState,
        snapshot: ScalingInputSnapshot,
    ): void {
        const prevPhase = ScalingInput.getPhaseFromProps(prevProps);
        const curPhase = ScalingInput.getPhaseFromProps(this.props);
        // if we went from fixed to scaling, save the width from the transition
        if (prevPhase !== ScalingInputPhase.ScalingFontSize && curPhase === ScalingInputPhase.ScalingFontSize) {
            this.setState({
                inputWidthPxAtPhaseChange: snapshot.inputWidthPx,
            });
        }
        // if we went from scaling to fixed, revert back to scaling using `ch`
        if (prevPhase === ScalingInputPhase.ScalingFontSize && curPhase !== ScalingInputPhase.ScalingFontSize) {
            this.setState({
                inputWidthPxAtPhaseChange: undefined,
            });
        }
        const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase);
        const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase);
        // If font size has changed, notify.
        if (prevFontSize !== curFontSize) {
            this.props.onFontSizeChange(curFontSize);
        }
    }


public render(): React.ReactNode {
        const { hasAutofocus, isDisabled, fontColor, onChange, placeholder, value, maxLength } = this.props;
        const phase = ScalingInput.getPhaseFromProps(this.props);
        return (
            <Input
                ref={this._inputRef as any}
                fontColor={fontColor}
                onChange={onChange}
                value={value}
                placeholder={placeholder}
                fontSize={`${this._calculateFontSize(phase)}px`}


/**
     * Configures the test module for McSelect with the given declarations. This is broken out so
     * that we're only compiling the necessary test components for each test in order to speed up
     * overall test time.
     * @param declarations Components to declare for this block
     */
function configureMcSelectTestingModule(declarations: any[]) {
        TestBed.configureTestingModule({
            imports: [
                McFormFieldModule,
                McSelectModule,
                McInputModule,
                ReactiveFormsModule,
                FormsModule,
                NoopAnimationsModule
            ],
            declarations,
            providers: [
                { provide: Directionality, useFactory: () => dir = { value: 'ltr' } },
                {
                    provide: ScrollDispatcher, useFactory: () => ({
                        scrolled: () => scrolledSubject.asObservable()
                    })
                }
            ]
        }).compileComponents();

        inject([OverlayContainer, Platform], (oc: OverlayContainer, p: Platform) => {
            overlayContainer = oc;
            overlayContainerElement = oc.getContainerElement();
            platform = p;
        })();
    }


/**
         * Asserts that the given option is aligned with the trigger.
         * @param index The index of the option.
         * @param selectInstance Instance of the `mc-select` component to check against.
         */
function checkTriggerAlignedWithOption(index: number, selectInstance =
            fixture.componentInstance.select): void {

            const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
            const triggerTop: number = trigger.getBoundingClientRect().top;
            const overlayTop: number = overlayPane.getBoundingClientRect().top;
            const options: NodeListOf<HTMLElement> = overlayPane.querySelectorAll('mc-option');
            const optionTop = options[index].getBoundingClientRect().top;
            const triggerFontSize = parseInt(window.getComputedStyle(trigger)['font-size']);
            const triggerLineHeightEm = 1.125;

            // Extra trigger height beyond the font size caused by the fact that the line-height is
            // greater than 1em.
            const triggerExtraLineSpaceAbove = (1 - triggerLineHeightEm) * triggerFontSize / 2;
            const topDifference = Math.floor(optionTop) -
                Math.floor(triggerTop - triggerFontSize - triggerExtraLineSpaceAbove);

            // Expect the coordinates to be within a pixel of each other. We can't rely on comparing
            // the exact value, because different browsers report the various sizes with slight (< 1px)
            // deviations.
            expect(Math.abs(topDifference) < 2)
                .toBe(true, `Expected trigger to align with option ${index}.`);

            // For the animation to start at the option's center, its origin must be the distance
            // from the top of the overlay to the option top + half the option height (48/2 = 24).
            const expectedOrigin = Math.floor(optionTop - overlayTop + 24);
            const rawYOrigin = selectInstance.transformOrigin.split(' ')[1].trim();
            const origin = Math.floor(parseInt(rawYOrigin));

            // Because the origin depends on the Y axis offset, we also have to
            // round down and check that the difference is within a pixel.
            expect(Math.abs(expectedOrigin - origin) < 2).toBe(true,
                `Expected panel animation to originate in the center of option ${index}.`);
        }


// 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;
            }


(value) => this.getFilteredOptions(value)


(option) =>
                option.toLowerCase().includes((searchFilter.toLowerCase()))


() => dir = { value: 'ltr' }


() => ({
                        scrolled: () => scrolledSubject.asObservable()
                    })


() => scrolledSubject.asObservable()


(oc: OverlayContainer, p: Platform) => {
            overlayContainer = oc;
            overlayContainerElement = oc.getContainerElement();
            platform = p;
        }


() => {
        overlayContainer.ngOnDestroy();
    }


() => {
            configureMcSelectTestingModule([
                BasicSelect,
                BasicEvents,
                MultiSelect,
                SelectWithGroups,
                SelectWithGroupsAndNgContainer,
                SelectWithFormFieldLabel,
                SelectWithChangeEvent
            ]);
        }


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


() => {
                    expect(select.getAttribute('tabindex')).toEqual('0');
                }


() => {
                    fixture.componentInstance.tabIndexOverride = 3;
                    fixture.detectChanges();

                    expect(select.getAttribute('tabindex')).toBe('3');
                }


() => {
                    fixture.componentInstance.control.disable();
                    fixture.detectChanges();
                    expect(select.getAttribute('tabindex')).toEqual('-1');

                    fixture.componentInstance.control.enable();
                    fixture.detectChanges();
                    expect(select.getAttribute('tabindex')).toEqual('0');
                }


() => {
                    const formControl = fixture.componentInstance.control;
                    const options = fixture.componentInstance.options.toArray();

                    expect(formControl.value).toBeFalsy('Expected no initial value.');

                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

                    expect(options[0].selected).toBe(true, 'Expected first option to be selected.');
                    expect(formControl.value).toBe(options[0].value,
                        'Expected value from first option to have been set on the model.');

                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

                    // Note that the third option is skipped, because it is disabled.
                    expect(options[3].selected).toBe(true, 'Expected fourth option to be selected.');
                    expect(formControl.value).toBe(options[3].value,
                        'Expected value from fourth option to have been set on the model.');

                    dispatchKeyboardEvent(select, 'keydown', UP_ARROW);

                    expect(options[1].selected).toBe(true, 'Expected second option to be selected.');
                    expect(formControl.value).toBe(options[1].value,
                        'Expected value from second option to have been set on the model.');
                }


() => {
                    const formControl = fixture.componentInstance.control;
                    const options = fixture.componentInstance.options.toArray();

                    expect(formControl.value).toBeFalsy('Expected no initial value.');

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

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

                    expect(formControl.value).toBe(options[3].value);

                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
                    fixture.detectChanges();

                    expect(formControl.value).toBe(options[4].value);
                }


() => {
                    const formControl = fixture.componentInstance.control;
                    const options = fixture.componentInstance.options.toArray();

                    expect(formControl.value).toBeFalsy('Expected no initial value.');

                    dispatchKeyboardEvent(select, 'keydown', RIGHT_ARROW);

                    expect(options[0].selected).toBe(true, 'Expected first option to be selected.');
                    expect(formControl.value).toBe(options[0].value,
                        'Expected value from first option to have been set on the model.');

                    dispatchKeyboardEvent(select, 'keydown', RIGHT_ARROW);
                    dispatchKeyboardEvent(select, 'keydown', RIGHT_ARROW);

                    // Note that the third option is skipped, because it is disabled.
                    expect(options[3].selected).toBe(true, 'Expected fourth option to be selected.');
                    expect(formControl.value).toBe(options[3].value,
                        'Expected value from fourth option to have been set on the model.');

                    dispatchKeyboardEvent(select, 'keydown', LEFT_ARROW);

                    expect(options[1].selected).toBe(true, 'Expected second option to be selected.');
                    expect(formControl.value).toBe(options[1].value,
                        'Expected value from second option to have been set on the model.');
                }


() => {
                    const { control: formControl, select: selectInstance } = fixture.componentInstance;

                    expect(selectInstance.panelOpen).toBe(false, 'Expected select to be closed.');
                    expect(formControl.value).toBeFalsy('Expected no initial value.');

                    const event = createKeyboardEvent('keydown', DOWN_ARROW);
                    Object.defineProperty(event, 'altKey', { get: () => true });

                    dispatchEvent(select, event);

                    expect(selectInstance.panelOpen).toBe(true, 'Expected select to be open.');
                    expect(formControl.value).toBeFalsy('Expected value not to have changed.');
                }


() => {
                    const { control: formControl, select: selectInstance } = fixture.componentInstance;

                    expect(selectInstance.panelOpen).toBe(false, 'Expected select to be closed.');
                    expect(formControl.value).toBeFalsy('Expected no initial value.');

                    const event = createKeyboardEvent('keydown', UP_ARROW);
                    Object.defineProperty(event, 'altKey', { get: () => true });

                    dispatchEvent(select, event);

                    expect(selectInstance.panelOpen).toBe(true, 'Expected select to be open.');
                    expect(formControl.value).toBeFalsy('Expected value not to have changed.');
                }


() => {
                    const { select: selectInstance } = fixture.componentInstance;

                    selectInstance.open();

                    expect(selectInstance.panelOpen).toBe(true, 'Expected select to be open.');

                    const event = createKeyboardEvent('keydown', DOWN_ARROW);
                    Object.defineProperty(event, 'altKey', { get: () => true });

                    dispatchEvent(select, event);

                    expect(selectInstance.panelOpen).toBe(false, 'Expected select to be closed.');
                    expect(event.defaultPrevented).toBe(true, 'Expected default action to be prevented.');
                }


() => {
                    const { select: selectInstance } = fixture.componentInstance;

                    selectInstance.open();

                    expect(selectInstance.panelOpen).toBe(true, 'Expected select to be open.');

                    const event = createKeyboardEvent('keydown', UP_ARROW);
                    Object.defineProperty(event, 'altKey', { get: () => true });

                    dispatchEvent(select, event);

                    expect(selectInstance.panelOpen).toBe(false, 'Expected select to be closed.');
                    expect(event.defaultPrevented).toBe(true, 'Expected default action to be prevented.');
                }


() => {
                    const formControl = fixture.componentInstance.control;
                    const options = fixture.componentInstance.options.toArray();

                    expect(formControl.value).toBeFalsy('Expected no initial value.');

                    dispatchEvent(select, createKeyboardEvent('keydown', 80, undefined, 'p'));
                    tick(200);

                    expect(options[1].selected).toBe(true, 'Expected second option to be selected.');
                    expect(formControl.value).toBe(options[1].value,
                        'Expected value from second option to have been set on the model.');

                    dispatchEvent(select, createKeyboardEvent('keydown', 69, undefined, 'e'));
                    tick(200);

                    expect(options[5].selected).toBe(true, 'Expected sixth option to be selected.');
                    expect(formControl.value).toBe(options[5].value,
                        'Expected value from sixth option to have been set on the model.');
                }


() => {
                        fixture.destroy();

                        const multiFixture = TestBed.createComponent(MultiSelect);
                        const instance = multiFixture.componentInstance;

                        multiFixture.detectChanges();
                        select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;

                        const initialValue = instance.control.value;

                        expect(instance.select.panelOpen).toBe(false, 'Expected panel to be closed.');

                        const event = dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

                        expect(instance.select.panelOpen).toBe(true, 'Expected panel to be open.');
                        expect(instance.control.value).toBe(initialValue, 'Expected value to stay the same.');
                        expect(event.defaultPrevented).toBe(true, 'Expected default to be prevented.');
                    }


() => {
                        fixture.destroy();

                        const multiFixture = TestBed.createComponent(MultiSelect);
                        const instance = multiFixture.componentInstance;

                        multiFixture.detectChanges();
                        select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;

                        const initialValue = instance.control.value;

                        expect(instance.select.panelOpen).toBe(false, 'Expected panel to be closed.');

                        const event = dispatchKeyboardEvent(select, 'keydown', RIGHT_ARROW);

                        expect(instance.select.panelOpen).toBe(true, 'Expected panel to be open.');
                        expect(instance.control.value).toBe(initialValue, 'Expected value to stay the same.');
                        expect(event.defaultPrevented).toBe(true, 'Expected default to be prevented.');
                    }


() => {
                    fixture.destroy();

                    const multiFixture = TestBed.createComponent(MultiSelect);
                    const instance = multiFixture.componentInstance;

                    multiFixture.detectChanges();
                    select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;

                    const initialValue = instance.control.value;

                    expect(instance.select.panelOpen).toBe(false, 'Expected panel to be closed.');

                    dispatchEvent(select, createKeyboardEvent('keydown', 80, undefined, 'p'));

                    expect(instance.select.panelOpen).toBe(false, 'Expected panel to stay closed.');
                    expect(instance.control.value).toBe(initialValue, 'Expected value to stay the same.');
                }


() => {
                    const formControl = fixture.componentInstance.control;

                    expect(formControl.value).toBeNull('Expected form control value to be empty.');
                    expect(formControl.pristine).toBe(true, 'Expected form control to be clean.');

                    dispatchKeyboardEvent(select, 'keydown', 16); // Press a random key.

                    expect(formControl.value).toBeNull('Expected form control value to stay empty.');
                    expect(formControl.pristine).toBe(true, 'Expected form control to stay clean.');
                }


() => {
                        const formControl = fixture.componentInstance.control;

                        formControl.setValue('eggs-5');

                        dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

                        expect(formControl.value).toBe('pasta-6');
                        expect(fixture.componentInstance.options.toArray()[6].selected).toBe(true);
                    }


() => {
                    fixture.destroy();

                    const multiFixture = TestBed.createComponent(MultiSelect);

                    multiFixture.detectChanges();
                    select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;
                    multiFixture.componentInstance.select.open();

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

                    options[3].focus();
                    expect(document.activeElement).toBe(options[3], 'Expected fourth option to be focused.');

                    multiFixture.componentInstance.control.setValue(['steak-0', 'sushi-7']);

                    expect(document.activeElement)
                        .toBe(options[3], 'Expected fourth option to remain focused.');
                }


() => {
                    const formControl = fixture.componentInstance.control;

                    formControl.setValue('eggs-5');
                    formControl.disable();

                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

                    expect(formControl.value).toBe('eggs-5', 'Expected value to remain unchaged.');
                }


() => {
                    const lastOption = fixture.componentInstance.options.last;

                    fixture.componentInstance.options.forEach(() => {
                        dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
                    });

                    expect(lastOption.selected).toBe(true, 'Expected last option to be selected.');

                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

                    expect(lastOption.selected).toBe(true, 'Expected last option to stay selected.');
                }


() => {
                        dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
                    }


() => {
                    fixture.destroy();

                    const multiFixture = TestBed.createComponent(MultiSelect);

                    multiFixture.detectChanges();
                    select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;

                    expect(multiFixture.componentInstance.select.panelOpen)
                        .toBe(false, 'Expected panel to be closed initially.');

                    dispatchKeyboardEvent(select, 'keydown', TAB);

                    expect(multiFixture.componentInstance.select.panelOpen)
                        .toBe(false, 'Expected panel to stay closed.');
                }


() => {
                        fixture.destroy();

                        const multiFixture = TestBed.createComponent(MultiSelect);
                        const event = createKeyboardEvent('keydown', DOWN_ARROW);
                        Object.defineProperty(event, 'shiftKey', { get: () => true });

                        multiFixture.detectChanges();
                        select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;

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

                        expect(multiFixture.componentInstance.select.value).toBeFalsy();

                        dispatchEvent(select, event);
                        expect(multiFixture.componentInstance.select.value).toEqual(['pizza-1']);

                        dispatchEvent(select, event);
                        expect(multiFixture.componentInstance.select.value).toEqual(['pizza-1', 'tacos-2']);
                    }


() => {
                        fixture.destroy();

                        const multiFixture = TestBed.createComponent(MultiSelect);
                        const event = createKeyboardEvent('keydown', UP_ARROW);
                        Object.defineProperty(event, 'shiftKey', { get: () => true });

                        multiFixture.detectChanges();
                        select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;

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

                        // Move focus down first.
                        for (let i = 0; i < 5; i++) {
                            dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
                            multiFixture.detectChanges();
                        }

                        expect(multiFixture.componentInstance.select.value).toBeFalsy();

                        dispatchEvent(select, event);
                        expect(multiFixture.componentInstance.select.value).toEqual(['chips-4']);

                        dispatchEvent(select, event);
                        expect(multiFixture.componentInstance.select.value).toEqual(['sandwich-3', 'chips-4']);
                    }


() => {
                    const event = dispatchKeyboardEvent(select, 'keydown', SPACE);
                    expect(event.defaultPrevented).toBe(true);
                }


() => {
                    const option = fixture.componentInstance.options.first;
                    const spy = jasmine.createSpy('option selection spy');
                    const subscription =
                        option.onSelectionChange.pipe(map((e) => e.isUserInput)).subscribe(spy);

                    dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
                    expect(spy).toHaveBeenCalledWith(true);

                    subscription.unsubscribe();
                }


(e) => e.isUserInput


() => {
                    document.body.focus(); // ensure that focus isn't on the trigger already

                    fixture.componentInstance.select.focus();

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


() => {
                        fixture.destroy();

                        const multiFixture = TestBed.createComponent(MultiSelect);
                        const instance = multiFixture.componentInstance;

                        multiFixture.detectChanges();
                        select = multiFixture.debugElement.query(By.css('mc-select')).nativeElement;
                        instance.select.open();

                        // Ensure that the select isn't focused to begin with.
                        select.blur();
                        expect(document.activeElement).not.toBe(select, 'Expected trigger not to be focused.');

                        const option = overlayContainerElement.querySelector('mc-option') as HTMLElement;
                        option.click();
                        expect(document.activeElement).toBe(select, 'Expected trigger to be focused.');
                    }


() => {
                let fixture: ComponentFixture<BasicSelect>;
                let trigger: HTMLElement;
                let options: NodeListOf<HTMLElement>;

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

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

                it('should set the tabindex of each option according to disabled state', fakeAsync(() => {
                    expect(options[0].getAttribute('tabindex')).toEqual('0');
                    expect(options[1].getAttribute('tabindex')).toEqual('0');
                    expect(options[2].getAttribute('tabindex')).toEqual('-1');
                }));
            }


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

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


() => {
                    expect(options[0].getAttribute('tabindex')).toEqual('0');
                    expect(options[1].getAttribute('tabindex')).toEqual('0');
                    expect(options[2].getAttribute('tabindex')).toEqual('-1');
                }


() => {
            let fixture: ComponentFixture<BasicSelect>;
            let trigger: HTMLElement;

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

            it('should not throw when attempting to open too early', () => {
                // Create component and then immediately open without running change detection
                fixture = TestBed.createComponent(BasicSelect);
                expect(() => fixture.componentInstance.select.open()).not.toThrow();
            });

            it('should open the panel when trigger is clicked', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(true);
                expect(overlayContainerElement.textContent).toContain('Steak');
                expect(overlayContainerElement.textContent).toContain('Pizza');
                expect(overlayContainerElement.textContent).toContain('Tacos');
            }));

            it('should close the panel when an item is clicked', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

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

                expect(overlayContainerElement.textContent).toEqual('');
                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }));

            it('should close the panel when a click occurs outside the panel', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

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

                expect(overlayContainerElement.textContent).toEqual('');
                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }));

            it('should set the width of the overlay based on the trigger', fakeAsync(() => {
                trigger.style.width = '200px';

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

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

            it('should not attempt to open a select that does not have any options', fakeAsync(() => {
                fixture.componentInstance.foods = [];
                fixture.detectChanges();

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

                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }));

            it('should close the panel when tabbing out', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(true);

                dispatchKeyboardEvent(trigger, 'keydown', TAB);
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }));

            it('should restore focus to the host before tabbing away', fakeAsync(() => {
                const select = fixture.nativeElement.querySelector('.mc-select');

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

                expect(fixture.componentInstance.select.panelOpen).toBe(true);

                // Use a spy since focus can be flaky in unit tests.
                spyOn(select, 'focus').and.callThrough();

                dispatchKeyboardEvent(trigger, 'keydown', TAB);
                fixture.detectChanges();
                flush();

                expect(select.focus).toHaveBeenCalled();
            }));

            it('should close when tabbing out from inside the panel', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(true);

                const panel = overlayContainerElement.querySelector('.mc-select__panel')!;
                dispatchKeyboardEvent(panel, 'keydown', TAB);
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }));

            it('should focus the first option when pressing HOME', fakeAsync(() => {
                fixture.componentInstance.control.setValue('pizza-1');
                fixture.detectChanges();

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

                const event = dispatchKeyboardEvent(trigger, 'keydown', HOME);
                fixture.detectChanges();

                expect(fixture.componentInstance.select.keyManager.activeItemIndex).toBe(0);
                expect(event.defaultPrevented).toBe(true);
            }));

            it('should focus the last option when pressing END', fakeAsync(() => {
                fixture.componentInstance.control.setValue('pizza-1');
                fixture.detectChanges();

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

                const event = dispatchKeyboardEvent(trigger, 'keydown', END);
                fixture.detectChanges();

                expect(fixture.componentInstance.select.keyManager.activeItemIndex).toBe(7);
                expect(event.defaultPrevented).toBe(true);
            }));

            it('should be able to set extra classes on the panel', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                const panel = overlayContainerElement.querySelector('.mc-select__panel') as HTMLElement;

                expect(panel.classList).toContain('custom-one');
                expect(panel.classList).toContain('custom-two');
            }));

            it('should prevent the default action when pressing SPACE on an option', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                const option = overlayContainerElement.querySelector('mc-option') as Node;
                const event = dispatchKeyboardEvent(option, 'keydown', SPACE);

                expect(event.defaultPrevented).toBe(true);
            }));

            it('should prevent the default action when pressing ENTER on an option', fakeAsync(() => {
                trigger.click();
                fixture.detectChanges();
                flush();

                const option = overlayContainerElement.querySelector('mc-option') as Node;
                const event = dispatchKeyboardEvent(option, 'keydown', ENTER);

                expect(event.defaultPrevented).toBe(true);
            }));

            it('should be able to render options inside groups with an ng-container', fakeAsync(() => {
                fixture.destroy();

                const groupFixture = TestBed.createComponent(SelectWithGroupsAndNgContainer);
                groupFixture.detectChanges();
                trigger = groupFixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
                trigger.click();
                groupFixture.detectChanges();
                flush();

                expect(document.querySelectorAll('.cdk-overlay-container mc-option').length)
                    .toBeGreaterThan(0, 'Expected at least one option to be rendered.');
            }));

            it('should not consider itself as blurred if the trigger loses focus while the ' +
                'panel is still open', fakeAsync(() => {
                const selectElement = fixture.nativeElement.querySelector('.mc-select');
                const selectInstance = fixture.componentInstance.select;

                dispatchFakeEvent(selectElement, 'focus');
                fixture.detectChanges();

                /* tslint:disable-next-line:deprecation */
                expect(selectInstance.focused).toBe(true, 'Expected select to be focused.');

                selectInstance.open();
                fixture.detectChanges();
                flush();
                dispatchFakeEvent(selectElement, 'blur');
                fixture.detectChanges();

                /* tslint:disable-next-line:deprecation */
                expect(selectInstance.focused).toBe(true, 'Expected select element to remain focused.');
            }));
        }


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


() => {
                // Create component and then immediately open without running change detection
                fixture = TestBed.createComponent(BasicSelect);
                expect(() => fixture.componentInstance.select.open()).not.toThrow();
            }


() => fixture.componentInstance.select.open()


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

                expect(fixture.componentInstance.select.panelOpen).toBe(true);
                expect(overlayContainerElement.textContent).toContain('Steak');
                expect(overlayContainerElement.textContent).toContain('Pizza');
                expect(overlayContainerElement.textContent).toContain('Tacos');
            }


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

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

                expect(overlayContainerElement.textContent).toEqual('');
                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }


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

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

                expect(overlayContainerElement.textContent).toEqual('');
                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }


() => {
                trigger.style.width = '200px';

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

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


() => {
                fixture.componentInstance.foods = [];
                fixture.detectChanges();

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

                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }


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

                expect(fixture.componentInstance.select.panelOpen).toBe(true);

                dispatchKeyboardEvent(trigger, 'keydown', TAB);
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }


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

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

                expect(fixture.componentInstance.select.panelOpen).toBe(true);

                // Use a spy since focus can be flaky in unit tests.
                spyOn(select, 'focus').and.callThrough();

                dispatchKeyboardEvent(trigger, 'keydown', TAB);
                fixture.detectChanges();
                flush();

                expect(select.focus).toHaveBeenCalled();
            }


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

                expect(fixture.componentInstance.select.panelOpen).toBe(true);

                const panel = overlayContainerElement.querySelector('.mc-select__panel')!;
                dispatchKeyboardEvent(panel, 'keydown', TAB);
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(false);
            }


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

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

                const event = dispatchKeyboardEvent(trigger, 'keydown', HOME);
                fixture.detectChanges();

                expect(fixture.componentInstance.select.keyManager.activeItemIndex).toBe(0);
                expect(event.defaultPrevented).toBe(true);
            }


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

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

                const event = dispatchKeyboardEvent(trigger, 'keydown', END);
                fixture.detectChanges();

                expect(fixture.componentInstance.select.keyManager.activeItemIndex).toBe(7);
                expect(event.defaultPrevented).toBe(true);
            }


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

                const panel = overlayContainerElement.querySelector('.mc-select__panel') as HTMLElement;

                expect(panel.classList).toContain('custom-one');
                expect(panel.classList).toContain('custom-two');
            }


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

                const option = overlayContainerElement.querySelector('mc-option') as Node;
                const event = dispatchKeyboardEvent(option, 'keydown', SPACE);

                expect(event.defaultPrevented).toBe(true);
            }


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

                const option = overlayContainerElement.querySelector('mc-option') as Node;
                const event = dispatchKeyboardEvent(option, 'keydown', ENTER);

                expect(event.defaultPrevented).toBe(true);
            }


() => {
                fixture.destroy();

                const groupFixture = TestBed.createComponent(SelectWithGroupsAndNgContainer);
                groupFixture.detectChanges();
                trigger = groupFixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
                trigger.click();
                groupFixture.detectChanges();
                flush();

                expect(document.querySelectorAll('.cdk-overlay-container mc-option').length)
                    .toBeGreaterThan(0, 'Expected at least one option to be rendered.');
            }


() => {
                const selectElement = fixture.nativeElement.querySelector('.mc-select');
                const selectInstance = fixture.componentInstance.select;

                dispatchFakeEvent(selectElement, 'focus');
                fixture.detectChanges();

                /* tslint:disable-next-line:deprecation */
                expect(selectInstance.focused).toBe(true, 'Expected select to be focused.');

                selectInstance.open();
                fixture.detectChanges();
                flush();
                dispatchFakeEvent(selectElement, 'blur');
                fixture.detectChanges();

                /* tslint:disable-next-line:deprecation */
                expect(selectInstance.focused).toBe(true, 'Expected select element to remain focused.');
            }


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

                expect(fixture.componentInstance.select.keyManager.activeItemIndex).toEqual(0);
            }


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

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

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

                option = overlayContainerElement.querySelector('mc-option') as HTMLElement;

                expect(option.classList).toContain('mc-selected');
                expect(fixture.componentInstance.options.first.selected).toBe(true);
                expect(fixture.componentInstance.select.selected)
                    .toBe(fixture.componentInstance.options.first);
            }


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

                const optionInstances = fixture.componentInstance.options.toArray();
                const optionNodes: NodeListOf<HTMLElement> = overlayContainerElement.querySelectorAll('mc-option');

                optionInstances[1].select();
                fixture.detectChanges();
                flush();

                expect(optionNodes[1].classList).toContain('mc-selected');
                expect(optionInstances[1].selected).toBe(true);
                expect(fixture.componentInstance.select.selected).toBe(optionInstances[1]);
            }


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

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

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

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

                options = overlayContainerElement.querySelectorAll('mc-option');
                expect(options[1].classList).not.toContain('mc-selected');
                expect(options[2].classList).not.toContain('mc-selected');

                const optionInstances = fixture.componentInstance.options.toArray();
                expect(optionInstances[1].selected).toBe(false);
                expect(optionInstances[2].selected).toBe(false);
            }


() => {
                const control = fixture.componentInstance.control;
                const foods = fixture.componentInstance.foods;

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

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

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

                control.setValue(foods[1].value);
                fixture.detectChanges();

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

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

                expect(options[0].classList)
                    .not.toContain('mc-selected', 'Expected first option to no longer be selected');
                expect(options[1].classList)
                    .toContain('mc-selected', 'Expected second option to be selected');

                const optionInstances = fixture.componentInstance.options.toArray();

                expect(optionInstances[0].selected)
                    .toBe(false, 'Expected first option to no longer be selected');
                expect(optionInstances[1].selected)
                    .toBe(true, 'Expected second option to be selected');
            }


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

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

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

                expect(value.textContent).toContain('Steak');
            }


() => {
                // must wait for initial writeValue promise to finish
                flush();

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

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

                // must wait for animation to finish
                fixture.detectChanges();
                expect(fixture.componentInstance.select.keyManager.activeItemIndex).toEqual(1);
            }


() => {
                fixture.componentInstance.foods.push({ viewValue: 'Potatoes', value: 'potatoes-8' });
                trigger.click();
                fixture.detectChanges();
                flush();

                const options: NodeListOf<HTMLElement> = overlayContainerElement.querySelectorAll('mc-option');
                options[8].click();
                fixture.detectChanges();
                flush();

                expect(trigger.textContent).toContain('Potatoes');
                expect(fixture.componentInstance.select.selected)
                    .toBe(fixture.componentInstance.options.last);
            }


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

                expect(trigger.querySelector('.mc-select__matcher-text')!.textContent!.trim())
                    .toBe('Pizza');

                fixture.componentInstance.foods[1].viewValue = 'Calzone';
                fixture.detectChanges();
                flush();

                expect(trigger.querySelector('.mc-select__matcher-text')!.textContent!.trim())
                    .toBe('Calzone');
            }


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

                expect(fixture.componentInstance.select.selected).toBeUndefined();

                const options: NodeListOf<HTMLElement> = overlayContainerElement.querySelectorAll('mc-option');
                options[2].click();
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.select.panelOpen).toBe(true);
                expect(options[2].classList).not.toContain('mc-selected');
                expect(fixture.componentInstance.select.selected).toBeUndefined();
            }


() => {
                fixture.destroy();

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

                const disabledGroup = overlayContainerElement.querySelectorAll('mc-optgroup')[1];
                const options: NodeListOf<HTMLElement> = disabledGroup.querySelectorAll('mc-option');

                options[0].click();
                groupFixture.detectChanges();
                flush();

                expect(groupFixture.componentInstance.select.panelOpen).toBe(true);
                expect(options[0].classList).not.toContain('mc-selected');
                expect(groupFixture.componentInstance.select.selected).toBeUndefined();
            }


() => {
                expect(() => fixture.componentInstance.select.triggerValue).not.toThrow();
            }


() => fixture.componentInstance.select.triggerValue


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

                const spy = jasmine.createSpy('option selection spy');
                const subscription = fixture.componentInstance.select.optionSelectionChanges.subscribe(spy);
                const option = overlayContainerElement.querySelector('mc-option') as HTMLElement;
                option.click();
                fixture.detectChanges();
                flush();

                expect(spy).toHaveBeenCalledWith(jasmine.any(McOptionSelectionChange));

                subscription.unsubscribe();
            }


() => {
                    fixture.destroy();
                    fixture = TestBed.createComponent(BasicSelect);

                    const spy = jasmine.createSpy('option selection spy');
                    let subscription: Subscription;

                    expect(fixture.componentInstance.select.options).toBeFalsy();
                    expect(() => {
                        subscription = fixture.componentInstance.select.optionSelectionChanges.subscribe(spy);
                    }).not.toThrow();

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

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

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

                    expect(spy).toHaveBeenCalledWith(jasmine.any(McOptionSelectionChange));

                    /* tslint:disable-next-line:no-unnecessary-type-assertion */
                    subscription!.unsubscribe();
                }


() => {
                        subscription = fixture.componentInstance.select.optionSelectionChanges.subscribe(spy);
                    }


() => {
                fixture.componentInstance.control = new FormControl('pizza-1');
                fixture.detectChanges();

                const value = fixture.debugElement.query(By.css('.mc-select__matcher'));
                expect(value.nativeElement.textContent)
                    .toContain('Pizza', `Expected trigger to be populated by the control's initial value.`);

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

                const options =
                    overlayContainerElement.querySelectorAll('mc-option');
                expect(options[1].classList)
                    .toContain('mc-selected',
                        `Expected option with the control's initial value to be selected.`);
            }


() => {
                let value = fixture.debugElement.query(By.css('.mc-select__matcher'));
                expect(value.nativeElement.textContent.trim()).toBe('Food');

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

                value = fixture.debugElement.query(By.css('.mc-select__matcher'));
                expect(value.nativeElement.textContent)
                    .toContain('Pizza', `Expected trigger to be populated by the control's new value.`);

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

                const options =
                    overlayContainerElement.querySelectorAll('mc-option');
                expect(options[1].classList).toContain(
                    'mc-selected', `Expected option with the control's new value to be selected.`);
            }


() => {
                expect(fixture.componentInstance.control.value)
                    .toEqual(null, `Expected the control's value to be empty initially.`);

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

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

                expect(fixture.componentInstance.control.value)
                    .toEqual('steak-0', `Expected control's value to be set to the new option.`);
            }


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

                fixture.componentInstance.control.setValue('gibberish');
                fixture.detectChanges();

                const value = fixture.debugElement.query(By.css('.mc-select__matcher'));
                expect(value.nativeElement.textContent.trim())
                    .toBe('Food', `Expected trigger to show the placeholder.`);
                expect(trigger.textContent)
                    .not.toContain('Pizza', `Expected trigger is cleared when option value is not found.`);

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

                const options =
                    overlayContainerElement.querySelectorAll('mc-option');
                expect(options[1].classList)
                    .not.toContain('mc-selected', `Expected option w/ the old value not to be selected.`);
            }


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

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

                const value = fixture.debugElement.query(By.css('.mc-select__matcher'));
                expect(value.nativeElement.textContent.trim())
                    .toBe('Food', `Expected trigger to show the placeholder.`);
                expect(trigger.textContent)
                    .not.toContain('Pizza', `Expected trigger is cleared when option value is not found.`);

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

                const options =
                    overlayContainerElement.querySelectorAll('mc-option');
                expect(options[1].classList)
                    .not.toContain('mc-selected', `Expected option w/ the old value not to be selected.`);
            }


() => {
                expect(fixture.componentInstance.control.touched)
                    .toEqual(false, `Expected the control to start off as untouched.`);

                trigger.click();
                dispatchFakeEvent(trigger, 'blur');
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.control.touched)
                    .toEqual(false, `Expected the control to stay untouched when menu opened.`);

                document.body.click();
                dispatchFakeEvent(trigger, 'blur');
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.control.touched)
                    .toEqual(true, `Expected the control to be touched as soon as focus left the select.`);
            }


() => {
                expect(fixture.componentInstance.control.touched)
                    .toBe(false, 'Expected the control to start off as untouched.');

                trigger.click();
                dispatchFakeEvent(trigger, 'blur');
                fixture.detectChanges();
                flush();

                expect(fixture.componentInstance.control.touched)
                    .toBe(false, 'Expected the control to stay untouched when dropdown opened.');

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

                expect(fixture.componentInstance.control.touched)
                    .toBe(true, 'Expected the control to be touched when the panel was closed.');
            }


() => {
                expect(fixture.componentInstance.control.touched)
                    .toBe(false, 'Expected the control to start off as untouched.');

                fixture.componentInstance.control.disable();
                dispatchFakeEvent(trigger, 'blur');

                expect(fixture.componentInstance.control.touched)
                    .toBe(false, 'Expected the control to stay untouched.');
            }


() => {
                expect(fixture.componentInstance.control.dirty)
                    .toEqual(false, `Expected control to start out pristine.`);

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

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

                expect(fixture.componentInstance.control.dirty)
                    .toEqual(true, `Expected control to be dirty after value was changed by user.`);
            }


() => {
                    expect(fixture.componentInstance.control.dirty)
                        .toEqual(false, `Expected control to start out pristine.`);

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

                    expect(fixture.componentInstance.control.dirty)
                        .toEqual(false, `Expected control to stay pristine after programmatic change.`);
                }


() => {
                let requiredMarker = fixture.debugElement.query(By.css('.mc-form-field-required-marker'));
                expect(requiredMarker)
                    .toBeNull(`Expected label not to have an asterisk, as control was not required.`);

                fixture.componentInstance.isRequired = true;
                fixture.detectChanges();

                requiredMarker = fixture.debugElement.query(By.css('.mc-form-field-required-marker'));
                expect(requiredMarker)
                    .not.toBeNull(`Expected label to have an asterisk, as control was required.`);
            }


() => {
            it('should disable itself when control is disabled programmatically', fakeAsync(() => {
                const fixture = TestBed.createComponent(BasicSelect);
                fixture.detectChanges();

                fixture.componentInstance.control.disable();
                fixture.detectChanges();
                const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
                expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                    .toEqual('default', `Expected cursor to be default arrow on disabled control.`);

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

                expect(overlayContainerElement.textContent)
                    .toEqual('', `Expected select panel to stay closed.`);
                expect(fixture.componentInstance.select.panelOpen)
                    .toBe(false, `Expected select panelOpen property to stay false.`);

                fixture.componentInstance.control.enable();
                fixture.detectChanges();
                expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                    .toEqual('pointer', `Expected cursor to be a pointer on enabled control.`);

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

                expect(overlayContainerElement.textContent)
                    .toContain('Steak', `Expected select panel to open normally on re-enabled control`);
                expect(fixture.componentInstance.select.panelOpen)
                    .toBe(true, `Expected select panelOpen property to become true.`);
            }));
        }


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

                fixture.componentInstance.control.disable();
                fixture.detectChanges();
                const trigger = fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
                expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                    .toEqual('default', `Expected cursor to be default arrow on disabled control.`);

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

                expect(overlayContainerElement.textContent)
                    .toEqual('', `Expected select panel to stay closed.`);
                expect(fixture.componentInstance.select.panelOpen)
                    .toBe(false, `Expected select panelOpen property to stay false.`);

                fixture.componentInstance.control.enable();
                fixture.detectChanges();
                expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                    .toEqual('pointer', `Expected cursor to be a pointer on enabled control.`);

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

                expect(overlayContainerElement.textContent)
                    .toContain('Steak', `Expected select panel to open normally on re-enabled control`);
                expect(fixture.componentInstance.select.panelOpen)
                    .toBe(true, `Expected select panelOpen property to become true.`);
            }


() => {
            let fixture: ComponentFixture<BasicSelect>;
            let host: HTMLElement;
            let panel: HTMLElement;

            beforeEach(fakeAsync(() => {
                fixture = TestBed.createComponent(BasicSelect);

                fixture.componentInstance.foods = [];

                for (let i = 0; i < 30; i++) {
                    fixture.componentInstance.foods.push({ value: `value-${i}`, viewValue: `Option ${i}` });
                }

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

                host = fixture.debugElement.query(By.css('mc-select')).nativeElement;
                panel = overlayContainerElement.querySelector('.mc-select__content') as HTMLElement;
            }));

            it('should not scroll to options that are completely in the view', fakeAsync(() => {
                const initialScrollPosition = panel.scrollTop;

                [1, 2, 3].forEach(() => {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                });

                expect(panel.scrollTop)
                    .toBe(initialScrollPosition, 'Expected scroll position not to change');
            }));

            it('should scroll down to the active option', fakeAsync(() => {
                for (let i = 0; i < 15; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }

                expect(panel.scrollTop).toBe(316, 'Expected scroll to be at the 16th option.');
            }));

            it('should scroll up to the active option', fakeAsync(() => {
                // Scroll to the bottom.
                for (let i = 0; i < fixture.componentInstance.foods.length; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }

                for (let i = 0; i < 20; i++) {
                    dispatchKeyboardEvent(host, 'keydown', UP_ARROW);
                }

                expect(panel.scrollTop).toBe(252, 'Expected scroll to be at the 9th option.');
            }));

            it('should skip option group labels', fakeAsync(() => {
                fixture.destroy();

                const groupFixture = TestBed.createComponent(SelectWithGroups);

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

                host = groupFixture.debugElement.query(By.css('mc-select')).nativeElement;
                panel = overlayContainerElement.querySelector('.mc-select__content') as HTMLElement;

                for (let i = 0; i < 5; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }

                // Note that we press down 5 times, but it will skip
                // 3 options because the second group is disabled.
                expect(panel.scrollTop).toBe(188, 'Expected scroll to be at the 9th option.');
            }));

            it('should scroll top the top when pressing HOME', fakeAsync(() => {
                for (let i = 0; i < 20; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                    fixture.detectChanges();
                }

                expect(panel.scrollTop).toBeGreaterThan(0, 'Expected panel to be scrolled down.');

                dispatchKeyboardEvent(host, 'keydown', HOME);
                fixture.detectChanges();

                expect(panel.scrollTop).toBe(0, 'Expected panel to be scrolled to the top');
            }));

            it('should scroll to the bottom of the panel when pressing END', fakeAsync(() => {
                dispatchKeyboardEvent(host, 'keydown', END);
                fixture.detectChanges();

                expect(panel.scrollTop).toBe(728, 'Expected panel to be scrolled to the bottom');
            }));

            it('should scroll to the active option when typing', fakeAsync(() => {
                for (let i = 0; i < 15; i++) {
                    // Press the letter 'o' 15 times since all the options are named 'Option <index>'
                    dispatchEvent(host, createKeyboardEvent('keydown', 79, undefined, 'o'));
                    fixture.detectChanges();
                    tick(LETTER_KEY_DEBOUNCE_INTERVAL);
                }
                flush();

                expect(panel.scrollTop).toBe(316, 'Expected scroll to be at the 16th option.');
            }));
        }


() => {
                fixture = TestBed.createComponent(BasicSelect);

                fixture.componentInstance.foods = [];

                for (let i = 0; i < 30; i++) {
                    fixture.componentInstance.foods.push({ value: `value-${i}`, viewValue: `Option ${i}` });
                }

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

                host = fixture.debugElement.query(By.css('mc-select')).nativeElement;
                panel = overlayContainerElement.querySelector('.mc-select__content') as HTMLElement;
            }


() => {
                const initialScrollPosition = panel.scrollTop;

                [1, 2, 3].forEach(() => {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                });

                expect(panel.scrollTop)
                    .toBe(initialScrollPosition, 'Expected scroll position not to change');
            }


() => {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }


() => {
                for (let i = 0; i < 15; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }

                expect(panel.scrollTop).toBe(316, 'Expected scroll to be at the 16th option.');
            }


() => {
                // Scroll to the bottom.
                for (let i = 0; i < fixture.componentInstance.foods.length; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }

                for (let i = 0; i < 20; i++) {
                    dispatchKeyboardEvent(host, 'keydown', UP_ARROW);
                }

                expect(panel.scrollTop).toBe(252, 'Expected scroll to be at the 9th option.');
            }


() => {
                fixture.destroy();

                const groupFixture = TestBed.createComponent(SelectWithGroups);

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

                host = groupFixture.debugElement.query(By.css('mc-select')).nativeElement;
                panel = overlayContainerElement.querySelector('.mc-select__content') as HTMLElement;

                for (let i = 0; i < 5; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                }

                // Note that we press down 5 times, but it will skip
                // 3 options because the second group is disabled.
                expect(panel.scrollTop).toBe(188, 'Expected scroll to be at the 9th option.');
            }


() => {
                for (let i = 0; i < 20; i++) {
                    dispatchKeyboardEvent(host, 'keydown', DOWN_ARROW);
                    fixture.detectChanges();
                }

                expect(panel.scrollTop).toBeGreaterThan(0, 'Expected panel to be scrolled down.');

                dispatchKeyboardEvent(host, 'keydown', HOME);
                fixture.detectChanges();

                expect(panel.scrollTop).toBe(0, 'Expected panel to be scrolled to the top');
            }


() => {
                dispatchKeyboardEvent(host, 'keydown', END);
                fixture.detectChanges();

                expect(panel.scrollTop).toBe(728, 'Expected panel to be scrolled to the bottom');
            }


() => {
                for (let i = 0; i < 15; i++) {
                    // Press the letter 'o' 15 times since all the options are named 'Option <index>'
                    dispatchEvent(host, createKeyboardEvent('keydown', 79, undefined, 'o'));
                    fixture.detectChanges();
                    tick(LETTER_KEY_DEBOUNCE_INTERVAL);
                }
                flush();

                expect(panel.scrollTop).toBe(316, 'Expected scroll to be at the 16th option.');
            }


() => {
            let fixture: ComponentFixture<BasicEvents>;
            let trigger: HTMLElement;

            beforeEach(fakeAsync(() => {
                fixture = TestBed.createComponent(BasicEvents);

                fixture.detectChanges();
                flush();

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

            it('should fire openedChange event on open select', fakeAsync(() => {
                expect(fixture.componentInstance.openedChangeListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedChangeListener).toHaveBeenCalled();
            }));

            it('should fire openedChange event on close select', fakeAsync(() => {
                expect(fixture.componentInstance.openedChangeListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedChangeListener).toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedChangeListener).toHaveBeenCalledTimes(2);
            }));

            it('should fire opened event on open select', fakeAsync(() => {
                expect(fixture.componentInstance.openedListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedListener).toHaveBeenCalled();
            }));

            it('should fire closed event on close select', fakeAsync(() => {
                expect(fixture.componentInstance.closedListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.closedListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.closedListener).toHaveBeenCalled();
            }));
        }


() => {
                fixture = TestBed.createComponent(BasicEvents);

                fixture.detectChanges();
                flush();

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


() => {
                expect(fixture.componentInstance.openedChangeListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedChangeListener).toHaveBeenCalled();
            }


() => {
                expect(fixture.componentInstance.openedChangeListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedChangeListener).toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedChangeListener).toHaveBeenCalledTimes(2);
            }


() => {
                expect(fixture.componentInstance.openedListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.openedListener).toHaveBeenCalled();
            }


() => {
                expect(fixture.componentInstance.closedListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.closedListener).not.toHaveBeenCalled();

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

                expect(fixture.componentInstance.closedListener).toHaveBeenCalled();
            }


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

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

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

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

        it('should have search input', fakeAsync(() => {
            trigger.click();
            fixture.detectChanges();
            flush();

            expect(fixture.debugElement.query(By.css('input'))).toBeDefined();
        }));

        it('should search filed should be focused after open', fakeAsync(() => {
            trigger.click();
            fixture.detectChanges();
            flush();

            const input = fixture.debugElement.query(By.css('input')).nativeElement;

            expect(input).toBe(document.activeElement);
        }));

        it('should search', fakeAsync(() => {
            trigger.click();
            fixture.detectChanges();
            flush();

            const inputElementDebug = fixture.debugElement.query(By.css('input'));

            inputElementDebug.nativeElement.value = 'lu';

            inputElementDebug.triggerEventHandler('input', { target: inputElementDebug.nativeElement });
            fixture.detectChanges();
            flush();

            const optionsTexts = fixture.debugElement.queryAll(By.css('mc-option'))
                .map((el) => el.nativeElement.innerText);

            expect(optionsTexts).toEqual(['Kaluga', 'Luga']);
        }));

        it('should clear search by esc', (() => {
            trigger.click();
            fixture.detectChanges();

            const inputElementDebug = fixture.debugElement.query(By.css('input'));

            inputElementDebug.nativeElement.value = 'lu';

            inputElementDebug.triggerEventHandler('input', { target: inputElementDebug.nativeElement });
            fixture.detectChanges();

            dispatchKeyboardEvent(inputElementDebug.nativeElement, 'keydown', ESCAPE);

            fixture.detectChanges();

            expect(inputElementDebug.nativeElement.value).toBe('');
        }));

        it('should close list by esc if input is empty', () => {
            trigger.click();
            fixture.detectChanges();

            const inputElementDebug = fixture.debugElement.query(By.css('input'));

            dispatchKeyboardEvent(inputElementDebug.nativeElement, 'keydown', ESCAPE);

            fixture.detectChanges();

            const selectInstance = fixture.componentInstance.select;

            expect(selectInstance.panelOpen).toBe(false);
        });
    }


() => configureMcSelectTestingModule([SelectWithSearch])


() => {
            fixture = TestBed.createComponent(SelectWithSearch);
            fixture.detectChanges();

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


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

            expect(fixture.debugElement.query(By.css('input'))).toBeDefined();
        }


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

            const input = fixture.debugElement.query(By.css('input')).nativeElement;

            expect(input).toBe(document.activeElement);
        }


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

            const inputElementDebug = fixture.debugElement.query(By.css('input'));

            inputElementDebug.nativeElement.value = 'lu';

            inputElementDebug.triggerEventHandler('input', { target: inputElementDebug.nativeElement });
            fixture.detectChanges();
            flush();

            const optionsTexts = fixture.debugElement.queryAll(By.css('mc-option'))
                .map((el) => el.nativeElement.innerText);

            expect(optionsTexts).toEqual(['Kaluga', 'Luga']);
        }


(el) => el.nativeElement.innerText


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

            const inputElementDebug = fixture.debugElement.query(By.css('input'));

            inputElementDebug.nativeElement.value = 'lu';

            inputElementDebug.triggerEventHandler('input', { target: inputElementDebug.nativeElement });
            fixture.detectChanges();

            dispatchKeyboardEvent(inputElementDebug.nativeElement, 'keydown', ESCAPE);

            fixture.detectChanges();

            expect(inputElementDebug.nativeElement.value).toBe('');
        }


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

            const inputElementDebug = fixture.debugElement.query(By.css('input'));

            dispatchKeyboardEvent(inputElementDebug.nativeElement, 'keydown', ESCAPE);

            fixture.detectChanges();

            const selectInstance = fixture.componentInstance.select;

            expect(selectInstance.panelOpen).toBe(false);
        }


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

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

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

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

        it('should emit an event when the selected option has changed', fakeAsync(() => {
            trigger.click();
            fixture.detectChanges();

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

            expect(fixture.componentInstance.changeListener).toHaveBeenCalled();
        }));

        xit('should not emit multiple change events for the same option', fakeAsync(() => {
            trigger.click();
            fixture.detectChanges();

            const option = overlayContainerElement.querySelector('mc-option') as HTMLElement;

            option.click();
            option.click();

            expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);
        }));

        it('should only emit one event when pressing arrow keys on closed select', fakeAsync(() => {
            const select = fixture.debugElement.query(By.css('mc-select')).nativeElement;
            dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

            expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);
        }));
    }


() => configureMcSelectTestingModule([SelectWithChangeEvent])


() => {
            fixture = TestBed.createComponent(SelectWithChangeEvent);
            fixture.detectChanges();

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


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

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

            expect(fixture.componentInstance.changeListener).toHaveBeenCalled();
        }


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

            const option = overlayContainerElement.querySelector('mc-option') as HTMLElement;

            option.click();
            option.click();

            expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);
        }


() => {
            const select = fixture.debugElement.query(By.css('mc-select')).nativeElement;
            dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

            expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);
        }


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

        it('should disable itself when control is disabled using the property', fakeAsync(() => {
            const fixture = TestBed.createComponent(NgModelSelect);
            fixture.detectChanges();

            fixture.componentInstance.isDisabled = true;
            fixture.detectChanges();
            flush();

            fixture.detectChanges();
            const trigger =
                fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                .toEqual('default', `Expected cursor to be default arrow on disabled control.`);

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

            expect(overlayContainerElement.textContent)
                .toEqual('', `Expected select panel to stay closed.`);
            expect(fixture.componentInstance.select.panelOpen)
                .toBe(false, `Expected select panelOpen property to stay false.`);

            fixture.componentInstance.isDisabled = false;
            fixture.detectChanges();
            flush();

            fixture.detectChanges();
            expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                .toEqual('pointer', `Expected cursor to be a pointer on enabled control.`);

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

            expect(overlayContainerElement.textContent)
                .toContain('Steak', `Expected select panel to open normally on re-enabled control`);
            expect(fixture.componentInstance.select.panelOpen)
                .toBe(true, `Expected select panelOpen property to become true.`);
        }));
    }


() => configureMcSelectTestingModule([NgModelSelect])


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

            fixture.componentInstance.isDisabled = true;
            fixture.detectChanges();
            flush();

            fixture.detectChanges();
            const trigger =
                fixture.debugElement.query(By.css('.mc-select__trigger')).nativeElement;
            expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                .toEqual('default', `Expected cursor to be default arrow on disabled control.`);

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

            expect(overlayContainerElement.textContent)
                .toEqual('', `Expected select panel to stay closed.`);
            expect(fixture.componentInstance.select.panelOpen)
                .toBe(false, `Expected select panelOpen property to stay false.`);

            fixture.componentInstance.isDisabled = false;
            fixture.detectChanges();
            flush();

            fixture.detectChanges();
            expect(getComputedStyle(trigger).getPropertyValue('cursor'))
                .toEqual('pointer', `Expected cursor to be a pointer on enabled control.`);

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

            expect(overlayContainerElement.textContent)
                .toContain('Steak', `Expected select panel to open normally on re-enabled control`);
            expect(fixture.componentInstance.select.panelOpen)
                .toBe(true, `Expected select panelOpen property to become true.`);
        }


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

        it('should handle nesting in an ngIf', fakeAsync(() => {
            const fixture = TestBed.createComponent(NgIfSelect);
            fixture.detectChanges();

            fixture.componentInstance.isShowing = true;
            fixture.detectChanges();

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

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

            const value = fixture.debugElement.query(By.css('.mc-select__matcher'));
            expect(value.nativeElement.textContent)
                .toContain('Pizza', `Expected trigger to be populated by the control's initial value.`);

            const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
            expect(pane.style.minWidth).toEqual('300px');

            expect(fixture.componentInstance.select.panelOpen).toBe(true);
            expect(overlayContainerElement.textContent).toContain('Steak');
            expect(overlayContainerElement.textContent).toContain('Pizza');
            expect(overlayContainerElement.textContent).toContain('Tacos');
        }));
    }


() => configureMcSelectTestingModule([NgIfSelect])


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

            fixture.componentInstance.isShowing = true;
            fixture.detectChanges();

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

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

            const value = fixture.debugElement.query(By.css('.mc-select__matcher'));
            expect(value.nativeElement.textContent)
                .toContain('Pizza', `Expected trigger to be populated by the control's initial value.`);

            const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
            expect(pane.style.minWidth).toEqual('300px');

            expect(fixture.componentInstance.select.panelOpen).toBe(true);
            expect(overlayContainerElement.textContent).toContain('Steak');
            expect(overlayContainerElement.textContent).toContain('Pizza');
            expect(overlayContainerElement.textContent).toContain('Tacos');
        }