getConfigurationNames(ignoreCompoundsAndPresentation = false): string[] {
		const config = this.getConfig();
		if (!config || (!Array.isArray(config.configurations) && !Array.isArray(config.compounds))) {
			return [];
		} else {
			const configurations: (IConfig | ICompound)[] = [];
			if (config.configurations) {
				configurations.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string'));
			}

			if (ignoreCompoundsAndPresentation) {
				return configurations.map(c => c.name);
			}

			if (config.compounds) {
				configurations.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length));
			}
			return getVisibleAndSorted(configurations).map(c => c.name);
		}
	}


getConfiguration(name: string): IConfig | undefined {
		// We need to clone the configuration in order to be able to make changes to it #42198
		const config = objects.deepClone(this.getConfig());
		if (!config || !config.configurations) {
			return undefined;
		}

		return config.configurations.find(config => config && config.name === name);
	}


async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise<string> {
		let content = '';
		const adapter = await this.configurationManager.guessDebugger(type);
		if (adapter) {
			const initialConfigs = await this.configurationManager.provideDebugConfigurations(folderUri, adapter.type, token || CancellationToken.None);
			content = await adapter.getInitialConfigurationContent(initialConfigs);
		}
		return content;
	}


protected getConfig(): IGlobalConfig | undefined {
		return this.configurationService.inspect<IGlobalConfig>('launch', { resource: this.workspace.uri }).workspaceFolderValue;
	}


async openConfigFile(preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditorPane | null, created: boolean }> {
		const resource = this.uri;
		let created = false;
		let content = '';
		try {
			const fileContent = await this.fileService.readFile(resource);
			content = fileContent.value.toString();
		} catch {
			// launch.json not found: create one by collecting launch configs from debugConfigProviders
			content = await this.getInitialConfigurationContent(this.workspace.uri, type, token);
			if (content) {
				created = true; // pin only if config file is created #8727
				try {
					await this.textFileService.write(resource, content);
				} catch (error) {
					throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
				}
			}
		}

		if (content === '') {
			return { editor: null, created: false };
		}

		const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
		let startLineNumber = 1;
		for (let i = 0; i < index; i++) {
			if (content.charAt(i) === '\n') {
				startLineNumber++;
			}
		}
		const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined;

		const editor = await this.editorService.openEditor({
			resource,
			options: {
				selection,
				preserveFocus,
				pinned: created,
				revealIfVisible: true
			},
		}, ACTIVE_GROUP);

		return ({
			editor: withUndefinedAsNull(editor),
			created
		});
	}


async writeConfiguration(configuration: IConfig): Promise<void> {
		const fullConfig = objects.deepClone(this.getConfig()!);
		fullConfig.configurations.push(configuration);
		await this.configurationService.updateValue('launch', fullConfig, { resource: this.workspace.uri }, ConfigurationTarget.WORKSPACE_FOLDER);
	}


protected getConfig(): IGlobalConfig | undefined {
		return this.configurationService.inspect<IGlobalConfig>('launch').workspaceValue;
	}


async openConfigFile(preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditorPane | null, created: boolean }> {
		let launchExistInFile = !!this.getConfig();
		if (!launchExistInFile) {
			// Launch property in workspace config not found: create one by collecting launch configs from debugConfigProviders
			let content = await this.getInitialConfigurationContent(undefined, type, token);
			if (content) {
				await this.configurationService.updateValue('launch', json.parse(content), ConfigurationTarget.WORKSPACE);
			} else {
				return { editor: null, created: false };
			}
		}

		const editor = await this.editorService.openEditor({
			resource: this.contextService.getWorkspace().configuration!,
			options: { preserveFocus }
		}, ACTIVE_GROUP);

		return ({
			editor: withUndefinedAsNull(editor),
			created: false
		});
	}


protected getConfig(): IGlobalConfig | undefined {
		return this.configurationService.inspect<IGlobalConfig>('launch').userValue;
	}


async openConfigFile(preserveFocus: boolean): Promise<{ editor: IEditorPane | null, created: boolean }> {
		const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus });
		return ({
			editor: withUndefinedAsNull(editor),
			created: false
		});
	}


export declare class RowPositionUtils {
    private rowModel;
    private pinnedRowModel;
    getRowNode(gridRow: RowPosition): RowNode | null;
    sameRow(rowA: RowPosition | undefined, rowB: RowPosition | undefined): boolean;
    before(rowA: RowPosition, rowB: RowPosition): boolean;
}


export interface RowPosition {
    rowIndex: number;
    rowPinned: string | undefined;
}


getRowNode(gridRow: RowPosition): RowNode | null;


sameRow(rowA: RowPosition | undefined, rowB: RowPosition | undefined): boolean;


before(rowA: RowPosition, rowB: RowPosition): boolean;


@NgModule({
  imports: [
    JhipsterSharedModule,
    RouterModule.forRoot(
      [ COMPANY_TEST_ROUTE ],
      { useHash: true })
  ],
  declarations: [
    CompanyTestComponent,
  ],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

export class JhipsterCompanyTestModule { }


(root: Root) => {
      for (const shape of root.shapes) {
        this.deleteByID(root, shape.getID());
      }
    }


abstract class Worker {
  abstract type: ToolType;

  abstract update: Function;

  abstract mousedown(point: Point, options: Options): void;

  abstract mousemove(point: Point): void;

  abstract mouseup(): void;

  abstract flushTask(): void;

  getElementByID(root: Root, createID: TimeTicket): Shape | undefined {
    return root.shapes.getElementByID(createID);
  }

  deleteByID(root: Root, createID: TimeTicket): Shape | undefined {
    return root.shapes.deleteByID(createID);
  }

  clearAll() {
    this.update((root: Root) => {
      for (const shape of root.shapes) {
        this.deleteByID(root, shape.getID());
      }
    });
  }
}


type Options = LineOption;


abstract mousedown(point: Point, options: Options): void;


abstract mousemove(point: Point): void;


abstract mouseup(): void;


abstract flushTask(): void;


getElementByID(root: Root, createID: TimeTicket): Shape | undefined {
    return root.shapes.getElementByID(createID);
  }


deleteByID(root: Root, createID: TimeTicket): Shape | undefined {
    return root.shapes.deleteByID(createID);
  }


clearAll() {
    this.update((root: Root) => {
      for (const shape of root.shapes) {
        this.deleteByID(root, shape.getID());
      }
    });
  }


/**
 * This event is called when a player clicks in an inventory.
 *
 * Because InventoryClickEvent occurs within a modification of the Inventory, not all Inventory related methods are safe to use.
 *
 * The following should never be invoked by an EventHandler for InventoryClickEvent using the HumanEntity or InventoryView associated with this event:
 * - HumanEntity.closeInventory()
 * - HumanEntity.openInventory(Inventory)
 * - HumanEntity.openWorkbench(Location, boolean)
 * - HumanEntity.openEnchanting(Location, boo[]lean)
 * - InventoryView.close()
 *
 * To invoke one of these methods, schedule a task using setTimeout, which will run the task on the next tick. Also be aware that this is not an exhaustive list, and other methods could potentially create issues as well.
 * Assuming the EntityHuman associated with this event is an instance of a Player, manipulating the MaxStackSize or contents of an Inventory will require an Invocation of Player.updateInventory().
 *
 * Modifications to slots that are modified by the results of this InventoryClickEvent can be overwritten. To change these slots, this event should be cancelled and all desired changes to the inventory applied. Alternatively, scheduling a task using BukkitScheduler.runTask(Plugin, Runnable), which would execute the task on the next tick, would work as well.
 */
export class InventoryClickEvent extends InventoryInteractEvent {
	public static getBukkitClasspath(): string {
		return 'org.bukkit.event.inventory.InventoryClickEvent';
	}

	/**
	 * Gets the type of slot that was clicked.
	 */
	getSlotType(): InventorySlotType {
		return this.toJava().getSlotType().name();
	}

	/**
	 * Gets the current ItemStack on the cursor.
	 */
	getCursor(): ItemStack | null {
		return ItemStack.fromJavaNullable(this.toJava().getCursor());
	}

	/**
	 * Gets the ItemStack currently in the clicked slot.
	 */
	getCurrentItem(): ItemStack | null {
		return ItemStack.fromJavaNullable(this.toJava().getCurrentItem());
	}

	/**
	 * Gets whether or not the ClickType for this event represents a right click.
	 * @returns true if the ClickType uses the right mouse button.
	 */
	isRightClick(): boolean {
		return this.toJava().isRightClick();
	}

	/**
	 * Gets whether or not the ClickType for this event represents a left click.
	 * @returns true if the ClickType uses the left mouse button.
	 */
	isLeftClick(): boolean {
		return this.toJava().isLeftClick();
	}

	/**
	 * Gets whether the ClickType for this event indicates that the key was pressed down when the click was made.
	 * @returns true if the ClickType uses Shift or Ctrl.
	 */
	isShiftClick(): boolean {
		return this.toJava().isShiftClick();
	}

	/**
	 * Sets the ItemStack currently in the clicked slot.
	 */
	setCurrentItem(item: ItemStack | null): void {
		this.toJava().setCurrentItem(item && item.toJava());
	}

	/**
	 * Gets the inventory corresponding to the clicked slot.
	 * @returns inventory, or null if clicked outside
	 */
	getClickedInventory(): Inventory | null {
		const inv = this.toJava().getClickedInventory();
		return inv && ConstructInventory(inv);
	}

	/**
	 * The slot number that was clicked, ready for passing to Inventory.getItem(int). Note that there may be two slots with the same slot number, since a view links two different inventories.
	 */
	getSlot(): number {
		return this.toJava().getSlot();
	}

	/**
	 * The raw slot number clicked. This slot number is unique for the view.
	 */
	getRawSlot(): number {
		return this.toJava().getRawSlot();
	}

	/**
	 * If the ClickType is NUMBER_KEY, this method will return the index of the pressed key (0-8).
	 * @returns the number on the key minus 1 (range 0-8); or -1 if not a NUMBER_KEY action
	 */
	getHotbarButton(): number {
		return this.toJava().getHotbarButton();
	}

	/**
	 * Gets the InventoryAction that triggered this event.
	 *
	 * This action cannot be changed, and represents what the normal outcome of
	 * the event will be. To change the behavior of this InventoryClickEvent,
	 * changes must be manually applied.
	 */
	getAction(): InventoryAction {
		return this.toJava().getAction().name();
	}

	/**
	 * Gets the ClickType for this event.
	 *
	 * This is insulated against changes to the inventory by other plugins.
	 * @todo Give a better explination of what this means
	 */
	getClick(): ClickType {
		return this.toJava().getClick().name();
	}
}


public static getBukkitClasspath(): string {
		return 'org.bukkit.event.inventory.InventoryClickEvent';
	}


/**
	 * Gets the type of slot that was clicked.
	 */
getSlotType(): InventorySlotType {
		return this.toJava().getSlotType().name();
	}


/**
	 * Gets the current ItemStack on the cursor.
	 */
getCursor(): ItemStack | null {
		return ItemStack.fromJavaNullable(this.toJava().getCursor());
	}


/**
	 * Gets the ItemStack currently in the clicked slot.
	 */
getCurrentItem(): ItemStack | null {
		return ItemStack.fromJavaNullable(this.toJava().getCurrentItem());
	}


/**
	 * Gets whether or not the ClickType for this event represents a right click.
	 * @returns true if the ClickType uses the right mouse button.
	 */
isRightClick(): boolean {
		return this.toJava().isRightClick();
	}


/**
	 * Gets whether or not the ClickType for this event represents a left click.
	 * @returns true if the ClickType uses the left mouse button.
	 */
isLeftClick(): boolean {
		return this.toJava().isLeftClick();
	}


/**
	 * Gets whether the ClickType for this event indicates that the key was pressed down when the click was made.
	 * @returns true if the ClickType uses Shift or Ctrl.
	 */
isShiftClick(): boolean {
		return this.toJava().isShiftClick();
	}


/**
	 * Sets the ItemStack currently in the clicked slot.
	 */
setCurrentItem(item: ItemStack | null): void {
		this.toJava().setCurrentItem(item && item.toJava());
	}


/**
	 * Gets the inventory corresponding to the clicked slot.
	 * @returns inventory, or null if clicked outside
	 */
getClickedInventory(): Inventory | null {
		const inv = this.toJava().getClickedInventory();
		return inv && ConstructInventory(inv);
	}


/**
	 * The slot number that was clicked, ready for passing to Inventory.getItem(int). Note that there may be two slots with the same slot number, since a view links two different inventories.
	 */
getSlot(): number {
		return this.toJava().getSlot();
	}


/**
	 * The raw slot number clicked. This slot number is unique for the view.
	 */
getRawSlot(): number {
		return this.toJava().getRawSlot();
	}


/**
	 * If the ClickType is NUMBER_KEY, this method will return the index of the pressed key (0-8).
	 * @returns the number on the key minus 1 (range 0-8); or -1 if not a NUMBER_KEY action
	 */
getHotbarButton(): number {
		return this.toJava().getHotbarButton();
	}


/**
	 * Gets the InventoryAction that triggered this event.
	 *
	 * This action cannot be changed, and represents what the normal outcome of
	 * the event will be. To change the behavior of this InventoryClickEvent,
	 * changes must be manually applied.
	 */
getAction(): InventoryAction {
		return this.toJava().getAction().name();
	}


/**
	 * Gets the ClickType for this event.
	 *
	 * This is insulated against changes to the inventory by other plugins.
	 * @todo Give a better explination of what this means
	 */
getClick(): ClickType {
		return this.toJava().getClick().name();
	}


(state: State) => {
    return {
        hostWidth: state.host.width,
        selectedPortalApp: state.selectedPortalApp,
        appLoadingError: state.appLoadingError
    };
}


() => {
    let rule: CachedInterpretersAutoSelectionRuleTest;
    let stateFactory: IPersistentStateFactory;
    let fs: IFileSystem;
    let state: PersistentState<PythonInterpreter | undefined>;
    let systemInterpreter: IInterpreterAutoSelectionRule;
    let currentPathInterpreter: IInterpreterAutoSelectionRule;
    let winRegInterpreter: IInterpreterAutoSelectionRule;
    let helper: IInterpreterHelper;
    class CachedInterpretersAutoSelectionRuleTest extends CachedInterpretersAutoSelectionRule {
        public readonly rules!: IInterpreterAutoSelectionRule[];
        public async setGlobalInterpreter(
            interpreter?: PythonInterpreter,
            manager?: IInterpreterAutoSelectionService
        ): Promise<boolean> {
            return super.setGlobalInterpreter(interpreter, manager);
        }
        public async onAutoSelectInterpreter(
            resource: Resource,
            manager?: IInterpreterAutoSelectionService
        ): Promise<NextAction> {
            return super.onAutoSelectInterpreter(resource, manager);
        }
    }
    setup(() => {
        stateFactory = mock(PersistentStateFactory);
        state = mock(PersistentState);
        fs = mock(FileSystem);
        helper = mock(InterpreterHelper);
        systemInterpreter = mock(SystemWideInterpretersAutoSelectionRule);
        currentPathInterpreter = mock(SystemWideInterpretersAutoSelectionRule);
        winRegInterpreter = mock(SystemWideInterpretersAutoSelectionRule);

        when(stateFactory.createGlobalPersistentState<PythonInterpreter | undefined>(anything(), undefined)).thenReturn(
            instance(state)
        );
        rule = new CachedInterpretersAutoSelectionRuleTest(
            instance(fs),
            instance(helper),
            instance(stateFactory),
            instance(systemInterpreter),
            instance(currentPathInterpreter),
            instance(winRegInterpreter)
        );
    });

    test('Invoke next rule if there are no cached interpreters', async () => {
        const manager = mock(InterpreterAutoSelectionService);
        const resource = Uri.file('x');

        when(systemInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).thenReturn(undefined);
        when(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).thenReturn(undefined);
        when(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).thenReturn(undefined);

        const nextAction = await rule.onAutoSelectInterpreter(resource, manager);

        verify(systemInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).once();
        verify(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).once();
        verify(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).once();
        expect(nextAction).to.be.equal(NextAction.runNextRule);
    });
    test('Invoke next rule if fails to update global state', async () => {
        const manager = mock(InterpreterAutoSelectionService);
        const winRegInterpreterInfo = { path: '1', version: new SemVer('1.0.0') } as any;
        const resource = Uri.file('x');

        when(helper.getBestInterpreter(deepEqual(anything()))).thenReturn(winRegInterpreterInfo);
        when(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(winRegInterpreterInfo);

        const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true);
        moq.callBase = true;
        moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny()))
            .returns(() => Promise.resolve(false))
            .verifiable(typemoq.Times.once());

        const nextAction = await moq.object.onAutoSelectInterpreter(resource, manager);

        verify(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        moq.verifyAll();
        expect(nextAction).to.be.equal(NextAction.runNextRule);
    });
    test('Must not Invoke next rule if updating global state is successful', async () => {
        const manager = mock(InterpreterAutoSelectionService);
        const winRegInterpreterInfo = { path: '1', version: new SemVer('1.0.0') } as any;
        const resource = Uri.file('x');

        when(helper.getBestInterpreter(deepEqual(anything()))).thenReturn(winRegInterpreterInfo);
        when(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(winRegInterpreterInfo);

        const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true);
        moq.callBase = true;
        moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny()))
            .returns(() => Promise.resolve(true))
            .verifiable(typemoq.Times.once());

        const nextAction = await moq.object.onAutoSelectInterpreter(resource, manager);

        verify(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        moq.verifyAll();
        expect(nextAction).to.be.equal(NextAction.exit);
    });
}


() => {
        stateFactory = mock(PersistentStateFactory);
        state = mock(PersistentState);
        fs = mock(FileSystem);
        helper = mock(InterpreterHelper);
        systemInterpreter = mock(SystemWideInterpretersAutoSelectionRule);
        currentPathInterpreter = mock(SystemWideInterpretersAutoSelectionRule);
        winRegInterpreter = mock(SystemWideInterpretersAutoSelectionRule);

        when(stateFactory.createGlobalPersistentState<PythonInterpreter | undefined>(anything(), undefined)).thenReturn(
            instance(state)
        );
        rule = new CachedInterpretersAutoSelectionRuleTest(
            instance(fs),
            instance(helper),
            instance(stateFactory),
            instance(systemInterpreter),
            instance(currentPathInterpreter),
            instance(winRegInterpreter)
        );
    }


async () => {
        const manager = mock(InterpreterAutoSelectionService);
        const resource = Uri.file('x');

        when(systemInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).thenReturn(undefined);
        when(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).thenReturn(undefined);
        when(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).thenReturn(undefined);

        const nextAction = await rule.onAutoSelectInterpreter(resource, manager);

        verify(systemInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).once();
        verify(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).once();
        verify(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(resource)).once();
        expect(nextAction).to.be.equal(NextAction.runNextRule);
    }


async () => {
        const manager = mock(InterpreterAutoSelectionService);
        const winRegInterpreterInfo = { path: '1', version: new SemVer('1.0.0') } as any;
        const resource = Uri.file('x');

        when(helper.getBestInterpreter(deepEqual(anything()))).thenReturn(winRegInterpreterInfo);
        when(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(winRegInterpreterInfo);

        const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true);
        moq.callBase = true;
        moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny()))
            .returns(() => Promise.resolve(false))
            .verifiable(typemoq.Times.once());

        const nextAction = await moq.object.onAutoSelectInterpreter(resource, manager);

        verify(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        moq.verifyAll();
        expect(nextAction).to.be.equal(NextAction.runNextRule);
    }


(m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())


() => Promise.resolve(false)


async () => {
        const manager = mock(InterpreterAutoSelectionService);
        const winRegInterpreterInfo = { path: '1', version: new SemVer('1.0.0') } as any;
        const resource = Uri.file('x');

        when(helper.getBestInterpreter(deepEqual(anything()))).thenReturn(winRegInterpreterInfo);
        when(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(undefined);
        when(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).thenReturn(winRegInterpreterInfo);

        const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true);
        moq.callBase = true;
        moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny()))
            .returns(() => Promise.resolve(true))
            .verifiable(typemoq.Times.once());

        const nextAction = await moq.object.onAutoSelectInterpreter(resource, manager);

        verify(systemInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(currentPathInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        verify(winRegInterpreter.getPreviouslyAutoSelectedInterpreter(anything())).once();
        moq.verifyAll();
        expect(nextAction).to.be.equal(NextAction.exit);
    }


() => Promise.resolve(true)


class CachedInterpretersAutoSelectionRuleTest extends CachedInterpretersAutoSelectionRule {
        public readonly rules!: IInterpreterAutoSelectionRule[];
        public async setGlobalInterpreter(
            interpreter?: PythonInterpreter,
            manager?: IInterpreterAutoSelectionService
        ): Promise<boolean> {
            return super.setGlobalInterpreter(interpreter, manager);
        }
        public async onAutoSelectInterpreter(
            resource: Resource,
            manager?: IInterpreterAutoSelectionService
        ): Promise<NextAction> {
            return super.onAutoSelectInterpreter(resource, manager);
        }
    }


public async setGlobalInterpreter(
            interpreter?: PythonInterpreter,
            manager?: IInterpreterAutoSelectionService
        ): Promise<boolean> {
            return super.setGlobalInterpreter(interpreter, manager);
        }


public async onAutoSelectInterpreter(
            resource: Resource,
            manager?: IInterpreterAutoSelectionService
        ): Promise<NextAction> {
            return super.onAutoSelectInterpreter(resource, manager);
        }


/**
	 * Returns default chart options merged with provided options,
	 * with special cases for axes.
	 * Axes object will not merge the not provided axes.
	 *
	 * @export
	 * @param {AxisChartOptions} defaultOptions Configuration.options[chartType]
	 * @param {AxisChartOptions} providedOptions user provided options
	 * @returns merged options
	 */
export function mergeDefaultChartOptions(defaultOptions: any, providedOptions: any) {
		defaultOptions = Tools.clone(defaultOptions);
		const providedAxesNames = Object.keys(providedOptions.axes || {});

		if (providedAxesNames.length === 0) {
			delete defaultOptions.axes;
		}

		// Update deprecated options to work with the tabular data format
		// Similar to the functionality in model.transformToTabularData()
		for (const axisName in defaultOptions.axes) {
			if (providedAxesNames.includes(axisName)) {
				const providedAxisOptions = providedOptions.axes[axisName];

				if (providedAxisOptions["primary"] || providedAxisOptions["secondary"]) {
					console.warn("`primary` & `secondary` are no longer needed for axis configurations. Read more here https://carbon-design-system.github.io/carbon-charts/?path=/story/tutorials--tabular-data-format");
				}

				const identifier = providedAxisOptions["mapsTo"];
				if (identifier === undefined || identifier === null) {
					const scaleType = providedAxisOptions["scaleType"];

					if (scaleType === undefined || scaleType === null) {
						providedAxisOptions["mapsTo"] = "value";
					} else if (scaleType === ScaleTypes.TIME) {
						providedAxisOptions["mapsTo"] = "date";
					} else if (scaleType === ScaleTypes.LABELS) {
						providedAxisOptions["mapsTo"] = "key";
					}
				}
			} else {
				delete defaultOptions.axes[axisName];
			}
		}

		return Tools.merge(
			defaultOptions,
			providedOptions
		);
	}


/**************************************
	 *  DOM-related operations            *
	 *************************************/
/**
	 * Get width & height of an element
	 *
	 * @export
	 * @param {any} el element to get dimensions from
	 * @returns an object containing the width and height of el
	 */
export function getDimensions(el) {
		return {
			width: parseFloat(el.style.width.replace("px", "") || el.offsetWidth),
			height: parseFloat(el.style.height.replace("px", "") || el.offsetHeight)
		};
	}


/**
	 * Gets elements's x and y translations from transform attribute or returns null
	 *
	 * @param {HTMLElement} element
	 * @returns an object containing the translated x and y values or null
	 */
export function getTranslationValues(elementRef: HTMLElement) {
		// regex to ONLY get values for translate (instead of all rotate, translate, skew, etc)
		const translateRegex = /translate\([0-9]+\.?[0-9]*,[0-9]+\.?[0-9]*\)/;

		const transformStr = elementRef.getAttribute("transform").match(translateRegex);
		// check for the match
		if (transformStr[0]) {
			const transforms = transformStr[0].replace(/translate\(/, "").replace(/\)/, "").split(",");

			return {
				tx: transforms[0],
				ty: transforms[1]
			};
		}
		return null;
	}


/**************************************
	 *  Formatting & calculations         *
	 *************************************/
/**
	 * Gets x and y coordinates from HTML transform attribute
	 *
	 * @export
	 * @param {any} string the transform attribute string ie. transform(x,y)
	 * @returns Returns an object with x and y offsets of the transform
	 */
export function getTranformOffsets(string) {
		const regExp = /\(([^)]+)\)/;
		const match = regExp.exec(string)[1];
		const xyString = match.split(",");

		return {
			x: parseFloat(xyString[0]),
			y: parseFloat(xyString[1])
		};
	}


/**
	 * Returns string value for height/width using pixels if there isn't a specified unit of measure
	 *
	 * @param value string or number value to be checked for unit of measure
	 */
export function formatWidthHeightValues(value) {
		const stringValue = value.toString();

		// If the value provided contains any letters
		// Return it the same way
		if (stringValue.match(/[a-z]/i)) {
			return stringValue;
		}

		return stringValue + "px";
	}


/**
	 * Capitalizes first letter of a string
	 *
	 * @export
	 * @param {any} string the input string to perform first letter capitalization with
	 * @returns The transformed string after first letter is capitalized
	 */
export function capitalizeFirstLetter(string) {
		return string[0].toUpperCase() + string.slice(1);
	}


/**
	 * Get the percentage of a datapoint compared to the entire dataset.
	 * @export
	 * @param {any} item
	 * @param {any} fullData
	 * @returns The percentage in the form of a number (1 significant digit if necessary)
	 */
export function convertValueToPercentage(item, fullData) {
		const percentage = item / fullData.reduce((accum, val) => accum + val.value, 0) * 100;
		// if the value has any significant figures, keep 1
		return percentage % 1 !== 0 ? parseFloat(percentage.toFixed(1)) : percentage;
	}


/**
	* Linearly interpolates a value between minValue and maxValue, then
	* clamps the result to the two limits.
	* The first parameter is always the value to interpolate. When called
	* with one additional parameter, minValue is set to 0, while the only
	* parameter is used as maxValue. When called with 2 additional
	* parameters, they are read as minValue and maxValue, respectively.
	* @export
	* @param {number} value
	* @param {number} firstLimit
	* @param {number} [secondLimit]
	* @returns The interpolated value
	*/
export function interpolateAndClamp(value: number, firstLimit: number, secondLimit?: number): number {
		const minValue = secondLimit === undefined ? 0 : firstLimit;
		const maxValue = secondLimit === undefined ? firstLimit : secondLimit;
		const interpolated = maxValue * value;
		return lodashClamp(interpolated, minValue, maxValue);
	}


/**************************************
	 *  Object/array related checks       *
	 *************************************/
/**
	 * Compares two arrays to return the difference between two arrays' items.
	 *
	 * @export
	 * @param {any[]} oldArray the array to check for missing items
	 * @param {any[]} newArray the array to check for newly added items
	 * @returns An object containing items missing (existing in oldArray but not newArray)
	 * and items added (existing in newArray but not in oldArray). Object is of the form { missing: [], added: [] }
	 */
export function arrayDifferences(oldArray: any[], newArray: any[]) {
		const difference = {
			missing: [],
			added: []
		};

		oldArray.forEach(element => {
			if (newArray.indexOf(element) === -1) {
				difference.missing.push(element);
			}
		});

		newArray.forEach(element => {
			if (oldArray.indexOf(element) === -1) {
				difference.added.push(element);
			}
		});

		return difference;
	}


/**
	 * Gets the duplicated keys from an array of data
	 *
	 * @export
	 * @param {*} data - array of data
	 * @returns A list of the duplicated keys in data
	 */
export function getDuplicateValues(arr: any) {
		const values = [];
		const duplicateValues = [];

		arr.forEach(value => {
			if (values.indexOf(value) !== -1 && duplicateValues.indexOf(value) === -1) {
				duplicateValues.push(value);
			}

			values.push(value);
		});

		return duplicateValues;
	}


// ================================================================================
// D3 Extensions
// ================================================================================
/**
	 * In D3, moves an element to the front of the canvas
	 *
	 * @export
	 * @param {any} element input element to moved in front
	 * @returns The function to be used by D3 to push element to the top of the canvas
	 */
export function moveToFront(element) {
		return element.each(function() {
			this.parentNode.appendChild(this);
		});
	}


export function flipDomainAndRangeBasedOnOrientation<D, R>(domain: D, range: R, orientation?: CartesianOrientations): [D, R] | [R, D] {
		return orientation === CartesianOrientations.VERTICAL ? [domain, range] : [range, domain];
	}


(accum, val) => accum + val.value


element => {
			if (newArray.indexOf(element) === -1) {
				difference.missing.push(element);
			}
		}


element => {
			if (oldArray.indexOf(element) === -1) {
				difference.added.push(element);
			}
		}


value => {
			if (values.indexOf(value) !== -1 && duplicateValues.indexOf(value) === -1) {
				duplicateValues.push(value);
			}

			values.push(value);
		}


(object, ...propPath) => {
		let position = object;
		if (position) {
			for (const prop of propPath) {
				if (position[prop] !== null && position[prop] !== undefined) {
					position = position[prop];
				} else {
					return null;
				}
			}
			return position;
		}

		return null;
	}


(verticalCoordinates: SVGPathCoordinates, orientation?: CartesianOrientations) => {
		if (orientation === CartesianOrientations.HORIZONTAL) {
			return {
				y0: verticalCoordinates.x0,
				y1: verticalCoordinates.x1,
				x0: verticalCoordinates.y0,
				x1: verticalCoordinates.y1
			};
		}

		return verticalCoordinates;
	}


(verticalCoordinates: SVGPathCoordinates, orientation?: CartesianOrientations) => {
		const { x0, x1, y0, y1 } = flipSVGCoordinatesBasedOnOrientation(verticalCoordinates, orientation);

		return `M${x0},${y0}L${x0},${y1}L${x1},${y1}L${x1},${y0}L${x0},${y0}`;
	}


interface SVGPathCoordinates {
		x0: number;
		x1: number;
		y0: number;
		y1: number;
	}


() => {
    TestBed.configureTestingModule({
      imports: [MatButtonToggleModule, FormsModule, ReactiveFormsModule, CommonModule],
      declarations: [
        ButtonToggleGroupWithNgModel,
        ButtonToggleGroupWithFormControl,
        ButtonToggleGroupWithIndirectDescendantToggles,
      ],
    });

    TestBed.compileComponents();
  }


() => {
    let fixture: ComponentFixture<ButtonToggleGroupWithFormControl>;
    let groupDebugElement: DebugElement;
    let groupInstance: MatButtonToggleGroup;
    let testComponent: ButtonToggleGroupWithFormControl;

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

      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);
    }));

    it('should toggle the disabled state', () => {
      testComponent.control.disable();

      expect(groupInstance.disabled).toBe(true);

      testComponent.control.enable();

      expect(groupInstance.disabled).toBe(false);
    });

    it('should set the value', () => {
      testComponent.control.setValue('green');

      expect(groupInstance.value).toBe('green');

      testComponent.control.setValue('red');

      expect(groupInstance.value).toBe('red');
    });

    it('should register the on change callback', () => {
      const spy = jasmine.createSpy('onChange callback');

      testComponent.control.registerOnChange(spy);
      testComponent.control.setValue('blue');

      expect(spy).toHaveBeenCalled();
    });
  }


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

      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);
    }


() => {
      testComponent.control.disable();

      expect(groupInstance.disabled).toBe(true);

      testComponent.control.enable();

      expect(groupInstance.disabled).toBe(false);
    }


() => {
      testComponent.control.setValue('green');

      expect(groupInstance.value).toBe('green');

      testComponent.control.setValue('red');

      expect(groupInstance.value).toBe('red');
    }


() => {
      const spy = jasmine.createSpy('onChange callback');

      testComponent.control.registerOnChange(spy);
      testComponent.control.setValue('blue');

      expect(spy).toHaveBeenCalled();
    }


() => {
    let fixture: ComponentFixture<ButtonToggleGroupWithNgModel>;
    let groupDebugElement: DebugElement;
    let buttonToggleDebugElements: DebugElement[];
    let groupInstance: MatButtonToggleGroup;
    let buttonToggleInstances: MatButtonToggle[];
    let testComponent: ButtonToggleGroupWithNgModel;
    let groupNgModel: NgModel;
    let innerButtons: HTMLElement[];

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(ButtonToggleGroupWithNgModel);
      fixture.detectChanges();
      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);
      groupNgModel = groupDebugElement.injector.get<NgModel>(NgModel);

      buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));
      buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
      innerButtons = buttonToggleDebugElements.map(
        debugEl => debugEl.query(By.css('button'))!.nativeElement);

      fixture.detectChanges();
    }));

    it('should update the model before firing change event', fakeAsync(() => {
      expect(testComponent.modelValue).toBeUndefined();
      expect(testComponent.lastEvent).toBeUndefined();

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

      tick();
      expect(testComponent.modelValue).toBe('red');
      expect(testComponent.lastEvent.value).toBe('red');
    }));

    it('should set individual radio names based on the group name', () => {
      expect(groupInstance.name).toBeTruthy();
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.name).toBe(groupInstance.name);
      }

      groupInstance.name = 'new name';
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.name).toBe(groupInstance.name);
      }
    });

    it('should update the name of radio DOM elements if the name of the group changes', () => {
       expect(innerButtons.every(button => button.getAttribute('name') === groupInstance.name))
          .toBe(true, 'Expected all buttons to have the initial name.');

       fixture.componentInstance.groupName = 'changed-name';
      fixture.detectChanges();

       expect(groupInstance.name).toBe('changed-name');
      expect(innerButtons.every(button => button.getAttribute('name') === groupInstance.name))
          .toBe(true, 'Expected all buttons to have the new name.');
    });

    it('should check the corresponding button toggle on a group value change', () => {
      expect(groupInstance.value).toBeFalsy();
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.checked).toBeFalsy();
      }

      groupInstance.value = 'red';
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.checked).toBe(groupInstance.value === buttonToggle.value);
      }

      const selected = groupInstance.selected as MatButtonToggle;

      expect(selected.value).toBe(groupInstance.value);
    });

    it('should have the correct FormControl state initially and after interaction',
      fakeAsync(() => {
        expect(groupNgModel.valid).toBe(true);
        expect(groupNgModel.pristine).toBe(true);
        expect(groupNgModel.touched).toBe(false);

        buttonToggleInstances[1].checked = true;
        fixture.detectChanges();
        tick();

        expect(groupNgModel.valid).toBe(true);
        expect(groupNgModel.pristine).toBe(true);
        expect(groupNgModel.touched).toBe(false);

        innerButtons[2].click();
        fixture.detectChanges();
        tick();

        expect(groupNgModel.valid).toBe(true);
        expect(groupNgModel.pristine).toBe(false);
        expect(groupNgModel.touched).toBe(true);
      }));

    it('should update the ngModel value when selecting a button toggle', fakeAsync(() => {
      innerButtons[1].click();
      fixture.detectChanges();

      tick();

      expect(testComponent.modelValue).toBe('green');
    }));

    it('should show a ripple on label click', () => {
      const groupElement = groupDebugElement.nativeElement;

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);

      dispatchMouseEvent(innerButtons[0], 'mousedown');
      dispatchMouseEvent(innerButtons[0], 'mouseup');

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(1);
    });

    it('should allow ripples to be disabled', () => {
      const groupElement = groupDebugElement.nativeElement;

      testComponent.disableRipple = true;
      fixture.detectChanges();

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);

      dispatchMouseEvent(innerButtons[0], 'mousedown');
      dispatchMouseEvent(innerButtons[0], 'mouseup');

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);
    });

    it('should maintain the selected value when swapping out the list of toggles with one ' +
      'that still contains the value', fakeAsync(() => {
        expect(buttonToggleInstances[0].checked).toBe(false);
        expect(fixture.componentInstance.modelValue).toBeFalsy();
        expect(groupInstance.value).toBeFalsy();

        groupInstance.value = 'red';
        fixture.detectChanges();

        expect(buttonToggleInstances[0].checked).toBe(true);
        expect(groupInstance.value).toBe('red');

        fixture.componentInstance.options = [...fixture.componentInstance.options];
        fixture.detectChanges();
        tick();
        fixture.detectChanges();

        buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));
        buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);

        expect(buttonToggleInstances[0].checked).toBe(true);
        expect(groupInstance.value).toBe('red');
      }));

  }


() => {
      fixture = TestBed.createComponent(ButtonToggleGroupWithNgModel);
      fixture.detectChanges();
      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);
      groupNgModel = groupDebugElement.injector.get<NgModel>(NgModel);

      buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));
      buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
      innerButtons = buttonToggleDebugElements.map(
        debugEl => debugEl.query(By.css('button'))!.nativeElement);

      fixture.detectChanges();
    }


debugEl => debugEl.componentInstance


debugEl => debugEl.query(By.css('button'))!.nativeElement


() => {
      expect(testComponent.modelValue).toBeUndefined();
      expect(testComponent.lastEvent).toBeUndefined();

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

      tick();
      expect(testComponent.modelValue).toBe('red');
      expect(testComponent.lastEvent.value).toBe('red');
    }


() => {
      expect(groupInstance.name).toBeTruthy();
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.name).toBe(groupInstance.name);
      }

      groupInstance.name = 'new name';
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.name).toBe(groupInstance.name);
      }
    }


() => {
       expect(innerButtons.every(button => button.getAttribute('name') === groupInstance.name))
          .toBe(true, 'Expected all buttons to have the initial name.');

       fixture.componentInstance.groupName = 'changed-name';
      fixture.detectChanges();

       expect(groupInstance.name).toBe('changed-name');
      expect(innerButtons.every(button => button.getAttribute('name') === groupInstance.name))
          .toBe(true, 'Expected all buttons to have the new name.');
    }


button => button.getAttribute('name') === groupInstance.name


() => {
      expect(groupInstance.value).toBeFalsy();
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.checked).toBeFalsy();
      }

      groupInstance.value = 'red';
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.checked).toBe(groupInstance.value === buttonToggle.value);
      }

      const selected = groupInstance.selected as MatButtonToggle;

      expect(selected.value).toBe(groupInstance.value);
    }


() => {
        expect(groupNgModel.valid).toBe(true);
        expect(groupNgModel.pristine).toBe(true);
        expect(groupNgModel.touched).toBe(false);

        buttonToggleInstances[1].checked = true;
        fixture.detectChanges();
        tick();

        expect(groupNgModel.valid).toBe(true);
        expect(groupNgModel.pristine).toBe(true);
        expect(groupNgModel.touched).toBe(false);

        innerButtons[2].click();
        fixture.detectChanges();
        tick();

        expect(groupNgModel.valid).toBe(true);
        expect(groupNgModel.pristine).toBe(false);
        expect(groupNgModel.touched).toBe(true);
      }


() => {
      innerButtons[1].click();
      fixture.detectChanges();

      tick();

      expect(testComponent.modelValue).toBe('green');
    }


() => {
      const groupElement = groupDebugElement.nativeElement;

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);

      dispatchMouseEvent(innerButtons[0], 'mousedown');
      dispatchMouseEvent(innerButtons[0], 'mouseup');

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(1);
    }


() => {
      const groupElement = groupDebugElement.nativeElement;

      testComponent.disableRipple = true;
      fixture.detectChanges();

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);

      dispatchMouseEvent(innerButtons[0], 'mousedown');
      dispatchMouseEvent(innerButtons[0], 'mouseup');

      expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);
    }


() => {
        expect(buttonToggleInstances[0].checked).toBe(false);
        expect(fixture.componentInstance.modelValue).toBeFalsy();
        expect(groupInstance.value).toBeFalsy();

        groupInstance.value = 'red';
        fixture.detectChanges();

        expect(buttonToggleInstances[0].checked).toBe(true);
        expect(groupInstance.value).toBe('red');

        fixture.componentInstance.options = [...fixture.componentInstance.options];
        fixture.detectChanges();
        tick();
        fixture.detectChanges();

        buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));
        buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);

        expect(buttonToggleInstances[0].checked).toBe(true);
        expect(groupInstance.value).toBe('red');
      }


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

    const button = fixture.nativeElement.querySelector('.mat-button-toggle button');
    const groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
    const groupInstance =
        groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);

    button.click();
    fixture.detectChanges();
    tick();

    expect(groupInstance.value).toBe('red');
    expect(fixture.componentInstance.control.value).toBe('red');
    expect(groupInstance._buttonToggles.length).toBe(3);
  }


() => {
    TestBed.configureTestingModule({
      imports: [MatButtonToggleModule],
      declarations: [
        ButtonTogglesInsideButtonToggleGroup,
        ButtonTogglesInsideButtonToggleGroupMultiple,
        FalsyButtonTogglesInsideButtonToggleGroupMultiple,
        ButtonToggleGroupWithInitialValue,
        StandaloneButtonToggle,
        ButtonToggleWithAriaLabel,
        ButtonToggleWithAriaLabelledby,
        RepeatedButtonTogglesWithPreselectedValue,
        ButtonToggleWithTabindex,
        ButtonToggleWithStaticName,
        ButtonToggleWithStaticChecked,
        ButtonToggleWithStaticAriaAttributes,
      ],
    });

    TestBed.compileComponents();
  }


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

      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupNativeElement = groupDebugElement.nativeElement;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);

      buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));

      buttonToggleNativeElements = buttonToggleDebugElements
        .map(debugEl => debugEl.nativeElement);

      buttonToggleLabelElements = fixture.debugElement.queryAll(By.css('button'))
        .map(debugEl => debugEl.nativeElement);

      buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
    }


debugEl => debugEl.nativeElement


() => {
      expect(groupInstance.name).toBeTruthy();
      for (let buttonToggle of buttonToggleInstances) {
        expect(buttonToggle.name).toBe(groupInstance.name);
      }
    }


() => {
      testComponent.isGroupDisabled = true;
      fixture.detectChanges();

      buttonToggleNativeElements[0].click();
      expect(buttonToggleInstances[0].checked).toBe(false);
      expect(buttonToggleInstances[0].disabled).toBe(true);

      testComponent.isGroupDisabled = false;
      fixture.detectChanges();

      expect(buttonToggleInstances[0].disabled).toBe(false);

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

      expect(buttonToggleInstances[0].checked).toBe(true);
    }


() => {
      expect(groupNativeElement.getAttribute('aria-disabled')).toBe('false');

      testComponent.isGroupDisabled = true;
      fixture.detectChanges();

      expect(groupNativeElement.getAttribute('aria-disabled')).toBe('true');
    }


() => {
      const buttons = buttonToggleNativeElements.map(toggle => toggle.querySelector('button')!);

      expect(buttons.every(input => input.disabled)).toBe(false);

      testComponent.isGroupDisabled = true;
      fixture.detectChanges();

      expect(buttons.every(input => input.disabled)).toBe(true);
    }


toggle => toggle.querySelector('button')!


input => input.disabled


() => {
      expect(groupInstance.value).toBeFalsy();
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();

      expect(groupInstance.value).toBe('test1');
      expect(groupInstance.selected).toBe(buttonToggleInstances[0]);
    }


() => {
      expect(groupInstance.value).toBeFalsy();
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();

      expect(groupInstance.value).toBe('test1');
      expect(testComponent.groupValue).toBe('test1');
    }


() => {
      expect(groupInstance.value).toBeFalsy();
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();

      expect(groupInstance.value).toBe('test1');
      expect(groupInstance.selected).toBe(buttonToggleInstances[0]);
      expect(buttonToggleInstances[0].checked).toBe(true);
      expect(buttonToggleInstances[1].checked).toBe(false);

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

      expect(groupInstance.value).toBe('test2');
      expect(groupInstance.selected).toBe(buttonToggleInstances[1]);
      expect(buttonToggleInstances[0].checked).toBe(false);
      expect(buttonToggleInstances[1].checked).toBe(true);
    }


() => {
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();

      expect(buttonToggleInstances[0].checked).toBe(true);
      expect(groupInstance.value).toBe('test1');
    }


() => {
      expect(groupNativeElement.classList).not.toContain('mat-button-toggle-vertical');

      groupInstance.vertical = true;
      fixture.detectChanges();

      expect(groupNativeElement.classList).toContain('mat-button-toggle-vertical');
    }


() => {
      expect(buttonToggleInstances[0].checked).toBe(false);

      const changeSpy = jasmine.createSpy('button-toggle change listener');
      buttonToggleInstances[0].change.subscribe(changeSpy);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalledTimes(1);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();

      // Always emit change event when button toggle is clicked
      expect(changeSpy).toHaveBeenCalledTimes(2);
    }


() => {
      expect(groupInstance.value).toBeFalsy();

      const changeSpy = jasmine.createSpy('button-toggle-group change listener');
      groupInstance.change.subscribe(changeSpy);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalled();

      buttonToggleLabelElements[1].click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalledTimes(2);
    }


() => {
      expect(groupInstance.value).toBeFalsy();

      testComponent.groupValue = 'test1';
      fixture.detectChanges();

      expect(groupInstance.value).toBe('test1');
      expect(groupInstance.selected).toBe(buttonToggleInstances[0]);
      expect(buttonToggleInstances[0].checked).toBe(true);
      expect(buttonToggleInstances[1].checked).toBe(false);

      testComponent.groupValue = 'test2';
      fixture.detectChanges();

      expect(groupInstance.value).toBe('test2');
      expect(groupInstance.selected).toBe(buttonToggleInstances[1]);
      expect(buttonToggleInstances[0].checked).toBe(false);
      expect(buttonToggleInstances[1].checked).toBe(true);
    }


() => {
      buttonToggleInstances[0].checked = true;

      expect(groupInstance.value).toBeTruthy();

      groupInstance.value = null;

      expect(buttonToggleInstances.every(toggle => !toggle.checked)).toBe(true);
    }


toggle => !toggle.checked


() => {
      expect(groupInstance.value).toBeFalsy();
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();

      expect(groupInstance.value).toBe('test1');
      expect(groupInstance.selected).toBe(buttonToggleInstances[0]);

      testComponent.renderFirstToggle = false;
      fixture.detectChanges();
      tick();

      expect(groupInstance.value).toBeFalsy();
      expect(groupInstance.selected).toBeFalsy();
    }


() => {

    it('should not fire an initial change event', () => {
      const fixture = TestBed.createComponent(ButtonToggleGroupWithInitialValue);
      const testComponent = fixture.debugElement.componentInstance;
      const groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      const groupInstance: MatButtonToggleGroup = groupDebugElement.injector
          .get<MatButtonToggleGroup>(MatButtonToggleGroup);

      fixture.detectChanges();

      // Note that we cast to a boolean, because the event has some circular references
      // which will crash the runner when Jasmine attempts to stringify them.
      expect(!!testComponent.lastEvent).toBe(false);
      expect(groupInstance.value).toBe('red');

      groupInstance.value = 'green';
      fixture.detectChanges();

      expect(!!testComponent.lastEvent).toBe(false);
      expect(groupInstance.value).toBe('green');
    });

  }


() => {
      const fixture = TestBed.createComponent(ButtonToggleGroupWithInitialValue);
      const testComponent = fixture.debugElement.componentInstance;
      const groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      const groupInstance: MatButtonToggleGroup = groupDebugElement.injector
          .get<MatButtonToggleGroup>(MatButtonToggleGroup);

      fixture.detectChanges();

      // Note that we cast to a boolean, because the event has some circular references
      // which will crash the runner when Jasmine attempts to stringify them.
      expect(!!testComponent.lastEvent).toBe(false);
      expect(groupInstance.value).toBe('red');

      groupInstance.value = 'green';
      fixture.detectChanges();

      expect(!!testComponent.lastEvent).toBe(false);
      expect(groupInstance.value).toBe('green');
    }


() => {
    let fixture: ComponentFixture<ButtonTogglesInsideButtonToggleGroupMultiple>;
    let groupDebugElement: DebugElement;
    let groupNativeElement: HTMLElement;
    let buttonToggleDebugElements: DebugElement[];
    let buttonToggleNativeElements: HTMLElement[];
    let buttonToggleLabelElements: HTMLLabelElement[];
    let groupInstance: MatButtonToggleGroup;
    let buttonToggleInstances: MatButtonToggle[];
    let testComponent: ButtonTogglesInsideButtonToggleGroupMultiple;

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

      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupNativeElement = groupDebugElement.nativeElement;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);

      buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));
      buttonToggleNativeElements = buttonToggleDebugElements
        .map(debugEl => debugEl.nativeElement);
      buttonToggleLabelElements = fixture.debugElement.queryAll(By.css('button'))
        .map(debugEl => debugEl.nativeElement);
      buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
    }));

    it('should disable click interactions when the group is disabled', () => {
      testComponent.isGroupDisabled = true;
      fixture.detectChanges();

      buttonToggleNativeElements[0].click();
      expect(buttonToggleInstances[0].checked).toBe(false);
    });

    it('should check a button toggle when clicked', () => {
      expect(buttonToggleInstances.every(buttonToggle => !buttonToggle.checked)).toBe(true);

      const nativeCheckboxLabel =
          buttonToggleDebugElements[0].query(By.css('button'))!.nativeElement;

      nativeCheckboxLabel.click();

      expect(groupInstance.value).toEqual(['eggs']);
      expect(buttonToggleInstances[0].checked).toBe(true);
    });

    it('should allow for multiple toggles to be selected', () => {
      buttonToggleInstances[0].checked = true;
      fixture.detectChanges();

      expect(groupInstance.value).toEqual(['eggs']);
      expect(buttonToggleInstances[0].checked).toBe(true);

      buttonToggleInstances[1].checked = true;
      fixture.detectChanges();

      expect(groupInstance.value).toEqual(['eggs', 'flour']);
      expect(buttonToggleInstances[1].checked).toBe(true);
      expect(buttonToggleInstances[0].checked).toBe(true);
    });

    it('should check a button toggle upon interaction with underlying native checkbox', () => {
      const nativeCheckboxButton =
          buttonToggleDebugElements[0].query(By.css('button'))!.nativeElement;

      nativeCheckboxButton.click();
      fixture.detectChanges();

      expect(groupInstance.value).toEqual(['eggs']);
      expect(buttonToggleInstances[0].checked).toBe(true);
    });

    it('should change the vertical state', () => {
      expect(groupNativeElement.classList).not.toContain('mat-button-toggle-vertical');

      groupInstance.vertical = true;
      fixture.detectChanges();

      expect(groupNativeElement.classList).toContain('mat-button-toggle-vertical');
    });

    it('should deselect a button toggle when selected twice', fakeAsync(() => {
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();

      expect(buttonToggleInstances[0].checked).toBe(true);
      expect(groupInstance.value).toEqual(['eggs']);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();

      expect(groupInstance.value).toEqual([]);
      expect(buttonToggleInstances[0].checked).toBe(false);
    }));

    it('should emit a change event for state changes', fakeAsync(() => {
      expect(buttonToggleInstances[0].checked).toBe(false);

      const changeSpy = jasmine.createSpy('button-toggle change listener');
      buttonToggleInstances[0].change.subscribe(changeSpy);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalled();
      expect(groupInstance.value).toEqual(['eggs']);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();
      expect(groupInstance.value).toEqual([]);

      // The default browser behavior is to emit an event, when the value was set
      // to false. That's because the current input type is set to `checkbox` when
      // using the multiple mode.
      expect(changeSpy).toHaveBeenCalledTimes(2);
    }));

    it('should throw when attempting to assign a non-array value', () => {
      expect(() => {
        groupInstance.value = 'not-an-array';
      }).toThrowError(/Value must be an array/);
    });

  }


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

      testComponent = fixture.debugElement.componentInstance;

      groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!;
      groupNativeElement = groupDebugElement.nativeElement;
      groupInstance = groupDebugElement.injector.get<MatButtonToggleGroup>(MatButtonToggleGroup);

      buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle));
      buttonToggleNativeElements = buttonToggleDebugElements
        .map(debugEl => debugEl.nativeElement);
      buttonToggleLabelElements = fixture.debugElement.queryAll(By.css('button'))
        .map(debugEl => debugEl.nativeElement);
      buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
    }


() => {
      testComponent.isGroupDisabled = true;
      fixture.detectChanges();

      buttonToggleNativeElements[0].click();
      expect(buttonToggleInstances[0].checked).toBe(false);
    }


() => {
      expect(buttonToggleInstances.every(buttonToggle => !buttonToggle.checked)).toBe(true);

      const nativeCheckboxLabel =
          buttonToggleDebugElements[0].query(By.css('button'))!.nativeElement;

      nativeCheckboxLabel.click();

      expect(groupInstance.value).toEqual(['eggs']);
      expect(buttonToggleInstances[0].checked).toBe(true);
    }


buttonToggle => !buttonToggle.checked


() => {
      buttonToggleInstances[0].checked = true;
      fixture.detectChanges();

      expect(groupInstance.value).toEqual(['eggs']);
      expect(buttonToggleInstances[0].checked).toBe(true);

      buttonToggleInstances[1].checked = true;
      fixture.detectChanges();

      expect(groupInstance.value).toEqual(['eggs', 'flour']);
      expect(buttonToggleInstances[1].checked).toBe(true);
      expect(buttonToggleInstances[0].checked).toBe(true);
    }


() => {
      const nativeCheckboxButton =
          buttonToggleDebugElements[0].query(By.css('button'))!.nativeElement;

      nativeCheckboxButton.click();
      fixture.detectChanges();

      expect(groupInstance.value).toEqual(['eggs']);
      expect(buttonToggleInstances[0].checked).toBe(true);
    }


() => {
      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();

      expect(buttonToggleInstances[0].checked).toBe(true);
      expect(groupInstance.value).toEqual(['eggs']);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();

      expect(groupInstance.value).toEqual([]);
      expect(buttonToggleInstances[0].checked).toBe(false);
    }


() => {
      expect(buttonToggleInstances[0].checked).toBe(false);

      const changeSpy = jasmine.createSpy('button-toggle change listener');
      buttonToggleInstances[0].change.subscribe(changeSpy);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalled();
      expect(groupInstance.value).toEqual(['eggs']);

      buttonToggleLabelElements[0].click();
      fixture.detectChanges();
      tick();
      expect(groupInstance.value).toEqual([]);

      // The default browser behavior is to emit an event, when the value was set
      // to false. That's because the current input type is set to `checkbox` when
      // using the multiple mode.
      expect(changeSpy).toHaveBeenCalledTimes(2);
    }


() => {
      expect(() => {
        groupInstance.value = 'not-an-array';
      }).toThrowError(/Value must be an array/);
    }


() => {
        groupInstance.value = 'not-an-array';
      }


() => {
    let fixture: ComponentFixture<StandaloneButtonToggle>;
    let buttonToggleDebugElement: DebugElement;
    let buttonToggleNativeElement: HTMLElement;
    let buttonToggleLabelElement: HTMLLabelElement;
    let buttonToggleInstance: MatButtonToggle;
    let buttonToggleButtonElement: HTMLButtonElement;

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

      buttonToggleDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      buttonToggleNativeElement = buttonToggleDebugElement.nativeElement;
      buttonToggleLabelElement = fixture.debugElement
          .query(By.css('.mat-button-toggle-label-content'))!.nativeElement;
      buttonToggleInstance = buttonToggleDebugElement.componentInstance;
      buttonToggleButtonElement =
          buttonToggleNativeElement.querySelector('button')! as HTMLButtonElement;
    }));

    it('should toggle when clicked', fakeAsync(() => {
      buttonToggleLabelElement.click();
      fixture.detectChanges();
      flush();

      expect(buttonToggleInstance.checked).toBe(true);

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

      expect(buttonToggleInstance.checked).toBe(false);
    }));

    it('should emit a change event for state changes', fakeAsync(() => {

      expect(buttonToggleInstance.checked).toBe(false);

      const changeSpy = jasmine.createSpy('button-toggle change listener');
      buttonToggleInstance.change.subscribe(changeSpy);

      buttonToggleLabelElement.click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalled();

      buttonToggleLabelElement.click();
      fixture.detectChanges();
      tick();

      // The default browser behavior is to emit an event, when the value was set
      // to false. That's because the current input type is set to `checkbox`.
      expect(changeSpy).toHaveBeenCalledTimes(2);
    }));

    it('should focus on underlying input element when focus() is called', () => {
      const nativeButton = buttonToggleDebugElement.query(By.css('button'))!.nativeElement;
      expect(document.activeElement).not.toBe(nativeButton);

      buttonToggleInstance.focus();
      fixture.detectChanges();

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

    it('should not assign a name to the underlying input if one is not passed in', () => {
      expect(buttonToggleButtonElement.getAttribute('name')).toBeFalsy();
    });

    it('should have correct aria-pressed attribute', () => {
      expect(buttonToggleButtonElement.getAttribute('aria-pressed'))
          .toBe('false');

      buttonToggleLabelElement.click();

      fixture.detectChanges();

      expect(buttonToggleButtonElement.getAttribute('aria-pressed'))
        .toBe('true');
    });
  }


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

      buttonToggleDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      buttonToggleNativeElement = buttonToggleDebugElement.nativeElement;
      buttonToggleLabelElement = fixture.debugElement
          .query(By.css('.mat-button-toggle-label-content'))!.nativeElement;
      buttonToggleInstance = buttonToggleDebugElement.componentInstance;
      buttonToggleButtonElement =
          buttonToggleNativeElement.querySelector('button')! as HTMLButtonElement;
    }


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

      expect(buttonToggleInstance.checked).toBe(true);

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

      expect(buttonToggleInstance.checked).toBe(false);
    }


() => {

      expect(buttonToggleInstance.checked).toBe(false);

      const changeSpy = jasmine.createSpy('button-toggle change listener');
      buttonToggleInstance.change.subscribe(changeSpy);

      buttonToggleLabelElement.click();
      fixture.detectChanges();
      tick();
      expect(changeSpy).toHaveBeenCalled();

      buttonToggleLabelElement.click();
      fixture.detectChanges();
      tick();

      // The default browser behavior is to emit an event, when the value was set
      // to false. That's because the current input type is set to `checkbox`.
      expect(changeSpy).toHaveBeenCalledTimes(2);
    }


() => {
      const nativeButton = buttonToggleDebugElement.query(By.css('button'))!.nativeElement;
      expect(document.activeElement).not.toBe(nativeButton);

      buttonToggleInstance.focus();
      fixture.detectChanges();

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


() => {
      expect(buttonToggleButtonElement.getAttribute('name')).toBeFalsy();
    }


() => {
      expect(buttonToggleButtonElement.getAttribute('aria-pressed'))
          .toBe('false');

      buttonToggleLabelElement.click();

      fixture.detectChanges();

      expect(buttonToggleButtonElement.getAttribute('aria-pressed'))
        .toBe('true');
    }


() => {
    it('should not set the aria-label attribute if none is provided', () => {
      const fixture = TestBed.createComponent(StandaloneButtonToggle);
      const checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      const checkboxNativeElement = checkboxDebugElement.nativeElement;
      const buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.hasAttribute('aria-label')).toBe(false);
    });

    it('should use the provided aria-label', () => {
      const fixture = TestBed.createComponent(ButtonToggleWithAriaLabel);
      const checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      const checkboxNativeElement = checkboxDebugElement.nativeElement;
      const buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.getAttribute('aria-label')).toBe('Super effective');
    });

    it('should clear the static aria from the host node', () => {
      const fixture = TestBed.createComponent(ButtonToggleWithStaticAriaAttributes);
      fixture.detectChanges();
      const hostNode: HTMLElement = fixture.nativeElement.querySelector('mat-button-toggle');

      expect(hostNode.hasAttribute('aria-label')).toBe(false);
      expect(hostNode.hasAttribute('aria-labelledby')).toBe(false);
    });

  }


() => {
      const fixture = TestBed.createComponent(StandaloneButtonToggle);
      const checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      const checkboxNativeElement = checkboxDebugElement.nativeElement;
      const buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.hasAttribute('aria-label')).toBe(false);
    }


() => {
      const fixture = TestBed.createComponent(ButtonToggleWithAriaLabel);
      const checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      const checkboxNativeElement = checkboxDebugElement.nativeElement;
      const buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.getAttribute('aria-label')).toBe('Super effective');
    }


() => {
      const fixture = TestBed.createComponent(ButtonToggleWithStaticAriaAttributes);
      fixture.detectChanges();
      const hostNode: HTMLElement = fixture.nativeElement.querySelector('mat-button-toggle');

      expect(hostNode.hasAttribute('aria-label')).toBe(false);
      expect(hostNode.hasAttribute('aria-labelledby')).toBe(false);
    }


() => {
    let checkboxDebugElement: DebugElement;
    let checkboxNativeElement: HTMLElement;
    let buttonElement: HTMLButtonElement;

    it('should use the provided aria-labelledby', () => {
      const fixture = TestBed.createComponent(ButtonToggleWithAriaLabelledby);
      checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      checkboxNativeElement = checkboxDebugElement.nativeElement;
      buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.getAttribute('aria-labelledby')).toBe('some-id');
    });

    it('should not assign aria-labelledby if none is provided', () => {
      const fixture = TestBed.createComponent(StandaloneButtonToggle);
      checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      checkboxNativeElement = checkboxDebugElement.nativeElement;
      buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.getAttribute('aria-labelledby')).toBe(null);
    });
  }


() => {
      const fixture = TestBed.createComponent(ButtonToggleWithAriaLabelledby);
      checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      checkboxNativeElement = checkboxDebugElement.nativeElement;
      buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.getAttribute('aria-labelledby')).toBe('some-id');
    }


() => {
      const fixture = TestBed.createComponent(StandaloneButtonToggle);
      checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!;
      checkboxNativeElement = checkboxDebugElement.nativeElement;
      buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement;

      fixture.detectChanges();
      expect(buttonElement.getAttribute('aria-labelledby')).toBe(null);
    }


() => {
    it('should forward the tabindex to the underlying button', () => {
      const fixture = TestBed.createComponent(ButtonToggleWithTabindex);
      fixture.detectChanges();

      const button = fixture.nativeElement.querySelector('.mat-button-toggle button');

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

    it('should have role "presentation"', () => {
      const fixture = TestBed.createComponent(ButtonToggleWithTabindex);
      fixture.detectChanges();

      const host = fixture.nativeElement.querySelector('.mat-button-toggle');

      expect(host.getAttribute('role')).toBe('presentation');
    });

    it('should forward focus to the underlying button when the host is focused', () => {
      const fixture = TestBed.createComponent(ButtonToggleWithTabindex);
      fixture.detectChanges();

      const host = fixture.nativeElement.querySelector('.mat-button-toggle');
      const button = host.querySelector('button');

      expect(document.activeElement).not.toBe(button);

      host.focus();

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


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

      const button = fixture.nativeElement.querySelector('.mat-button-toggle button');

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


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

      const host = fixture.nativeElement.querySelector('.mat-button-toggle');

      expect(host.getAttribute('role')).toBe('presentation');
    }


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

      const host = fixture.nativeElement.querySelector('.mat-button-toggle');
      const button = host.querySelector('button');

      expect(document.activeElement).not.toBe(button);

      host.focus();

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


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

    expect(() => fixture.detectChanges()).not.toThrow();
    expect(fixture.componentInstance.toggleGroup.value).toBe('Two');
    expect(fixture.componentInstance.toggles.toArray()[1].checked).toBe(true);
  }


() => fixture.detectChanges()


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

    const hostNode: HTMLElement = fixture.nativeElement.querySelector('.mat-button-toggle');

    expect(hostNode.hasAttribute('name')).toBe(false);
    expect(hostNode.querySelector('button')!.getAttribute('name')).toBe('custom-name');
  }


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

      expect(fixture.componentInstance.toggleGroup.value).toBe('Two');
      expect(fixture.componentInstance.toggles.toArray()[1].checked).toBe(true);

      fixture.componentInstance.possibleValues = ['Five', 'Six', 'Seven'];
      fixture.componentInstance.value = 'Seven';
      fixture.detectChanges();

      expect(fixture.componentInstance.toggleGroup.value).toBe('Seven');
      expect(fixture.componentInstance.toggles.toArray()[2].checked).toBe(true);
    }


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

    expect(fixture.componentInstance.toggles.toArray()[0].checked).toBe(true);
    expect(fixture.componentInstance.toggles.toArray()[1].checked).toBe(false);
    expect(fixture.componentInstance.toggles.toArray()[2].checked).toBe(false);

    fixture.componentInstance.value = [0, false];
    fixture.detectChanges();

    expect(fixture.componentInstance.toggles.toArray()[0].checked).toBe(true);
    expect(fixture.componentInstance.toggles.toArray()[1].checked).toBe(false);
    expect(fixture.componentInstance.toggles.toArray()[2].checked).toBe(true);
  }


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

    // In Ivy static inputs are set during creation. We simulate this by not calling
    // `fixture.detectChanges` immediately, but getting a hold of the instance via the
    // DebugElement and setting the value ourselves.
    expect(() => {
      const toggle = fixture.debugElement.query(By.css('mat-button-toggle'))!.componentInstance;
      toggle.checked = true;
      fixture.detectChanges();
    }).not.toThrow();
  }


() => {
      const toggle = fixture.debugElement.query(By.css('mat-button-toggle'))!.componentInstance;
      toggle.checked = true;
      fixture.detectChanges();
    }


() => {
    const fixture = TestBed.createComponent(ButtonTogglesInsideButtonToggleGroup);
    const buttonNativeElements = [...fixture.debugElement.nativeElement.querySelectorAll('button')];

    expect(buttonNativeElements.every(element => element.classList.contains('mat-focus-indicator')))
        .toBe(true);
  }


element => element.classList.contains('mat-focus-indicator')


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

    expect(fixture.componentInstance.toggles.map(t => t.checked)).toEqual([false, true]);
    expect(fixture.componentInstance.group.value).toBe('2');
  }


t => t.checked


@Component({
  template: `
  <mat-button-toggle-group [disabled]="isGroupDisabled"
                           [vertical]="isVertical"
                           [(value)]="groupValue">
    <mat-button-toggle value="test1" *ngIf="renderFirstToggle">Test1</mat-button-toggle>
    <mat-button-toggle value="test2">Test2</mat-button-toggle>
    <mat-button-toggle value="test3">Test3</mat-button-toggle>
  </mat-button-toggle-group>
  `
})
class ButtonTogglesInsideButtonToggleGroup {
  isGroupDisabled: boolean = false;
  isVertical: boolean = false;
  groupValue: string;
  renderFirstToggle = true;
}


@Component({
  template: `
  <mat-button-toggle-group
    [name]="groupName"
    [(ngModel)]="modelValue"
    (change)="lastEvent = $event">
    <mat-button-toggle *ngFor="let option of options" [value]="option.value"
                       [disableRipple]="disableRipple">
      {{option.label}}
    </mat-button-toggle>
  </mat-button-toggle-group>
  `
})
class ButtonToggleGroupWithNgModel {
  groupName = 'group-name';
  modelValue: string;
  options = [
    {label: 'Red', value: 'red'},
    {label: 'Green', value: 'green'},
    {label: 'Blue', value: 'blue'},
  ];
  lastEvent: MatButtonToggleChange;
  disableRipple = false;
}


@Component({
  template: `
  <mat-button-toggle-group [disabled]="isGroupDisabled" [vertical]="isVertical" multiple>
    <mat-button-toggle value="eggs">Eggs</mat-button-toggle>
    <mat-button-toggle value="flour">Flour</mat-button-toggle>
    <mat-button-toggle value="sugar">Sugar</mat-button-toggle>
  </mat-button-toggle-group>
  `
})
class ButtonTogglesInsideButtonToggleGroupMultiple {
  isGroupDisabled: boolean = false;
  isVertical: boolean = false;
}


@Component({
  template: `
  <mat-button-toggle-group multiple [value]="value">
    <mat-button-toggle [value]="0">Eggs</mat-button-toggle>
    <mat-button-toggle [value]="null">Flour</mat-button-toggle>
    <mat-button-toggle [value]="false">Sugar</mat-button-toggle>
    <mat-button-toggle>Sugar</mat-button-toggle>
  </mat-button-toggle-group>
  `
})
class FalsyButtonTogglesInsideButtonToggleGroupMultiple {
  value: ('' | number | null | undefined | boolean)[] = [0];
  @ViewChildren(MatButtonToggle) toggles: QueryList<MatButtonToggle>;
}


@Component({
  template: `
  <mat-button-toggle>Yes</mat-button-toggle>
  `
})
class StandaloneButtonToggle { }


@Component({
  template: `
  <mat-button-toggle-group (change)="lastEvent = $event" value="red">
    <mat-button-toggle value="red">Value Red</mat-button-toggle>
    <mat-button-toggle value="green">Value Green</mat-button-toggle>
  </mat-button-toggle-group>
  `
})
class ButtonToggleGroupWithInitialValue {
  lastEvent: MatButtonToggleChange;
}


@Component({
  template: `
  <mat-button-toggle-group [formControl]="control">
    <mat-button-toggle value="red">Value Red</mat-button-toggle>
    <mat-button-toggle value="green">Value Green</mat-button-toggle>
    <mat-button-toggle value="blue">Value Blue</mat-button-toggle>
  </mat-button-toggle-group>
  `
})
class ButtonToggleGroupWithFormControl {
  control = new FormControl();
}


@Component({
  // We need the `ngSwitch` so that there's a directive between the group and the toggles.
  template: `
    <mat-button-toggle-group [formControl]="control">
      <ng-container [ngSwitch]="true">
        <mat-button-toggle value="red">Value Red</mat-button-toggle>
        <mat-button-toggle value="green">Value Green</mat-button-toggle>
        <mat-button-toggle value="blue">Value Blue</mat-button-toggle>
      </ng-container>
    </mat-button-toggle-group>
  `
})
class ButtonToggleGroupWithIndirectDescendantToggles {
  control = new FormControl();
}


/** Simple test component with an aria-label set. */
@Component({
  template: `<mat-button-toggle aria-label="Super effective"></mat-button-toggle>`
})
class ButtonToggleWithAriaLabel { }


/** Simple test component with an aria-label set. */
@Component({
  template: `<mat-button-toggle aria-labelledby="some-id"></mat-button-toggle>`
})
class ButtonToggleWithAriaLabelledby {}


@Component({
  template: `
    <mat-button-toggle-group [(value)]="value">
      <mat-button-toggle *ngFor="let toggle of possibleValues" [value]="toggle">
        {{toggle}}
      </mat-button-toggle>
    </mat-button-toggle-group>
  `
})
class RepeatedButtonTogglesWithPreselectedValue {
  @ViewChild(MatButtonToggleGroup) toggleGroup: MatButtonToggleGroup;
  @ViewChildren(MatButtonToggle) toggles: QueryList<MatButtonToggle>;

  possibleValues = ['One', 'Two', 'Three'];
  value = 'Two';
}


@Component({
  template: `<mat-button-toggle tabindex="3"></mat-button-toggle>`
})
class ButtonToggleWithTabindex {}


@Component({
  template: `<mat-button-toggle name="custom-name"></mat-button-toggle>`
})
class ButtonToggleWithStaticName {}


@Component({
  template: `
    <mat-button-toggle-group>
      <mat-button-toggle value="1">One</mat-button-toggle>
      <mat-button-toggle value="2" checked>Two</mat-button-toggle>
    </mat-button-toggle-group>
  `
})
class ButtonToggleWithStaticChecked {
  @ViewChild(MatButtonToggleGroup) group: MatButtonToggleGroup;
  @ViewChildren(MatButtonToggle) toggles: QueryList<MatButtonToggle>;
}


@Component({
  template: `
    <mat-button-toggle aria-label="Toggle me" aria-labelledby="something"></mat-button-toggle>
  `
})
class ButtonToggleWithStaticAriaAttributes {}


(items: QuickInsertItem[] = this.fetchItems()) =>
    // NOTE: we fetch all items to determine available categories.
    this.filterCategories(items, this.props.categories)


(
    items: QuickInsertItem[],
    categories: Category[] = [],
  ): Category[] => {
    const { showCategories } = this.props;
    if (!showCategories) {
      return [];
    }
    return categories.filter(
      (category) =>
        category.name === 'all' ||
        items.some((item) => (item.categories || []).includes(category.name)),
    );
  }


(category) =>
        category.name === 'all' ||
        items.some((item) => (item.categories || []).includes(category.name))


(item) => (item.categories || []).includes(category.name)


(query?: string, category?: string) => {
    return this.props.getItems(query, category);
  }


(searchTerm: string) => {
    const { defaultCategory } = this.props;
    this.setState({ searchTerm, selectedCategory: defaultCategory });
  }


(clickedCategory: Category) => {
    const { selectedCategory: stateCategoryValue } = this.state;

    /**
     * Reset selection if clicked on the same category twice.
     */
    if (stateCategoryValue === clickedCategory.name) {
      return this.setState({ selectedCategory: this.props.defaultCategory });
    }

    this.setState({
      selectedCategory: clickedCategory.name,
      searchTerm: '',
    });
  }


export default class ElementBrowser extends PureComponent<Props, State> {
  static defaultProps = {
    defaultCategory: 'all',
    onInsertItem: () => {},
  };

  state: State = {
    categories: [],
    items: [],
    searchTerm: '',
    selectedCategory: this.props.defaultCategory,
  };

  componentDidMount() {
    const items = this.fetchItems();
    this.setState({
      items,
      categories: this.getCategories(items),
    });
  }

  getCategories = (items: QuickInsertItem[] = this.fetchItems()) =>
    // NOTE: we fetch all items to determine available categories.
    this.filterCategories(items, this.props.categories);

  filterCategories = (
    items: QuickInsertItem[],
    categories: Category[] = [],
  ): Category[] => {
    const { showCategories } = this.props;
    if (!showCategories) {
      return [];
    }
    return categories.filter(
      (category) =>
        category.name === 'all' ||
        items.some((item) => (item.categories || []).includes(category.name)),
    );
  };

  fetchItems = (query?: string, category?: string) => {
    return this.props.getItems(query, category);
  };

  componentDidUpdate(prevProps: Props, prevState: State) {
    const { searchTerm, selectedCategory } = this.state;

    // Update both items and categories when there's a new getItems
    if (this.props.getItems !== prevProps.getItems) {
      this.setState({
        categories: this.getCategories(),
        items: this.fetchItems(searchTerm, selectedCategory),
      });
    } else if (
      searchTerm !== prevState.searchTerm ||
      selectedCategory !== prevState.selectedCategory
    ) {
      this.setState({
        items: this.fetchItems(searchTerm, selectedCategory),
      });
    }
  }

  handleSearch = (searchTerm: string) => {
    const { defaultCategory } = this.props;
    this.setState({ searchTerm, selectedCategory: defaultCategory });
  };

  handleCategorySelection = (clickedCategory: Category) => {
    const { selectedCategory: stateCategoryValue } = this.state;

    /**
     * Reset selection if clicked on the same category twice.
     */
    if (stateCategoryValue === clickedCategory.name) {
      return this.setState({ selectedCategory: this.props.defaultCategory });
    }

    this.setState({
      selectedCategory: clickedCategory.name,
      searchTerm: '',
    });
  };

  render() {
    const {
      onInsertItem,
      onSelectItem,
      showSearch,
      showCategories,
      mode,
      emptyStateHandler,
    } = this.props;
    const { categories, searchTerm, selectedCategory, items } = this.state;
    return (
      <StatelessElementBrowser
        items={items}
        categories={categories}
        onSearch={this.handleSearch}
        onSelectCategory={this.handleCategorySelection}
        onSelectItem={onSelectItem}
        onInsertItem={onInsertItem}
        selectedCategory={selectedCategory}
        showSearch={showSearch}
        showCategories={showCategories}
        mode={mode}
        searchTerm={searchTerm}
        emptyStateHandler={emptyStateHandler}
      />
    );
  }
}


export interface Props {
  categories?: Category[];
  mode: keyof typeof Modes;
  getItems: (query?: string, category?: string) => QuickInsertItem[];
  onSelectItem?: (item: QuickInsertItem) => void;
  onInsertItem: (item: QuickInsertItem) => void;
  showSearch: boolean;
  showCategories: boolean;
  defaultCategory?: string;
  emptyStateHandler?: EmptyStateHandler;
}


export interface State {
  items: QuickInsertItem[];
  categories: Category[];
  selectedCategory?: string;
  searchTerm?: string;
}


componentDidMount() {
    const items = this.fetchItems();
    this.setState({
      items,
      categories: this.getCategories(items),
    });
  }


componentDidUpdate(prevProps: Props, prevState: State) {
    const { searchTerm, selectedCategory } = this.state;

    // Update both items and categories when there's a new getItems
    if (this.props.getItems !== prevProps.getItems) {
      this.setState({
        categories: this.getCategories(),
        items: this.fetchItems(searchTerm, selectedCategory),
      });
    } else if (
      searchTerm !== prevState.searchTerm ||
      selectedCategory !== prevState.selectedCategory
    ) {
      this.setState({
        items: this.fetchItems(searchTerm, selectedCategory),
      });
    }
  }


render() {
    const {
      onInsertItem,
      onSelectItem,
      showSearch,
      showCategories,
      mode,
      emptyStateHandler,
    } = this.props;
    const { categories, searchTerm, selectedCategory, items } = this.state;
    return (
      <StatelessElementBrowser
        items={items}
        categories={categories}
        onSearch={this.handleSearch}
        onSelectCategory={this.handleCategorySelection}
        onSelectItem={onSelectItem}
        onInsertItem={onInsertItem}
        selectedCategory={selectedCategory}
        showSearch={showSearch}
        showCategories={showCategories}
        mode={mode}
        searchTerm={searchTerm}
        emptyStateHandler={emptyStateHandler}
      />
    );
  }


(i) => i === item


(query) => query.query.queryID === queryId


async (_ev) =>
        this.updateTreeViewSelectionIfVisible()


async (ev) => {
        if (ev.selection.length == 0) {
          this.updateTreeViewSelectionIfVisible();
        }
        this.updateCompareWith(ev.selection);
      }


async (item: CompletedQuery) => {
          return this.handleItemClicked(item, [item]);
        }


() => {
      this.treeDataProvider.refresh();
    }


(edit) =>
        edit.replace(
          textDocument.validateRange(
            new vscode.Range(0, 0, textDocument.lineCount, 0)
          ),
          queryText
        )


(item) => {
      this.treeDataProvider.remove(item);
      item.dispose();
    }


(otherQuery) =>
          otherQuery !== singleItem &&
          otherQuery.didRunSuccessfully &&
          otherQuery.database.name === dbName


(otherQuery) => ({
        label: otherQuery.toString(),
        description: otherQuery.databaseName,
        detail: otherQuery.statusString,
        query: otherQuery,
      })


/**
 * Tree data provider for the query history view.
 */
export class HistoryTreeDataProvider extends DisposableObject {
  private _onDidChangeTreeData = super.push(new vscode.EventEmitter<CompletedQuery | undefined>());

  readonly onDidChangeTreeData: vscode.Event<CompletedQuery | undefined> = this
    ._onDidChangeTreeData.event;

  private history: CompletedQuery[] = [];

  private failedIconPath: string;

  /**
   * When not undefined, must be reference-equal to an item in `this.databases`.
   */
  private current: CompletedQuery | undefined;

  constructor(extensionPath: string) {
    super();
    this.failedIconPath = path.join(
      extensionPath,
      FAILED_QUERY_HISTORY_ITEM_ICON
    );
  }

  async getTreeItem(element: CompletedQuery): Promise<vscode.TreeItem> {
    const treeItem = new vscode.TreeItem(element.toString());

    treeItem.command = {
      title: 'Query History Item',
      command: 'codeQLQueryHistory.itemClicked',
      arguments: [element],
    };

    // Mark this query history item according to whether it has a
    // SARIF file so that we can make context menu items conditionally
    // available.
    const hasResults = await element.query.hasInterpretedResults();
    treeItem.contextValue = hasResults
      ? 'interpretedResultsItem'
      : 'rawResultsItem';

    if (!element.didRunSuccessfully) {
      treeItem.iconPath = this.failedIconPath;
    }

    return treeItem;
  }

  getChildren(
    element?: CompletedQuery
  ): vscode.ProviderResult<CompletedQuery[]> {
    return element ? [] : this.history;
  }

  getParent(_element: CompletedQuery): vscode.ProviderResult<CompletedQuery> {
    return null;
  }

  getCurrent(): CompletedQuery | undefined {
    return this.current;
  }

  pushQuery(item: CompletedQuery): void {
    this.current = item;
    this.history.push(item);
    this.refresh();
  }

  setCurrentItem(item: CompletedQuery) {
    this.current = item;
  }

  remove(item: CompletedQuery) {
    if (this.current === item) this.current = undefined;
    const index = this.history.findIndex((i) => i === item);
    if (index >= 0) {
      this.history.splice(index, 1);
      if (this.current === undefined && this.history.length > 0) {
        // Try to keep a current item, near the deleted item if there
        // are any available.
        this.current = this.history[Math.min(index, this.history.length - 1)];
      }
      this.refresh();
    }
  }

  get allHistory(): CompletedQuery[] {
    return this.history;
  }

  refresh(completedQuery?: CompletedQuery) {
    this._onDidChangeTreeData.fire(completedQuery);
  }

  find(queryId: number): CompletedQuery | undefined {
    return this.allHistory.find((query) => query.query.queryID === queryId);
  }
}


/**
 * query-history.ts
 * ------------
 * Managing state of previous queries that we've executed.
 *
 * The source of truth of the current state resides inside the
 * `TreeDataProvider` subclass below.
 */
export type QueryHistoryItemOptions = {
  label?: string; // user-settable label
  queryText?: string; // text of the selected file
  isQuickQuery?: boolean;
};


async getTreeItem(element: CompletedQuery): Promise<vscode.TreeItem> {
    const treeItem = new vscode.TreeItem(element.toString());

    treeItem.command = {
      title: 'Query History Item',
      command: 'codeQLQueryHistory.itemClicked',
      arguments: [element],
    };

    // Mark this query history item according to whether it has a
    // SARIF file so that we can make context menu items conditionally
    // available.
    const hasResults = await element.query.hasInterpretedResults();
    treeItem.contextValue = hasResults
      ? 'interpretedResultsItem'
      : 'rawResultsItem';

    if (!element.didRunSuccessfully) {
      treeItem.iconPath = this.failedIconPath;
    }

    return treeItem;
  }


getChildren(
    element?: CompletedQuery
  ): vscode.ProviderResult<CompletedQuery[]> {
    return element ? [] : this.history;
  }


getParent(_element: CompletedQuery): vscode.ProviderResult<CompletedQuery> {
    return null;
  }


getCurrent(): CompletedQuery | undefined {
    return this.current;
  }


pushQuery(item: CompletedQuery): void {
    this.current = item;
    this.history.push(item);
    this.refresh();
  }


setCurrentItem(item: CompletedQuery) {
    this.current = item;
  }