UNPKG

@redhat-developer/page-objects

Version:

Page Object API implementation for a VS Code editor used by ExTester framework.

471 lines 18.5 kB
"use strict"; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License", destination); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ArraySettingItem = exports.ArraySetting = exports.LinkSetting = exports.CheckboxSetting = exports.TextSetting = exports.ComboSetting = exports.Setting = exports.SettingsEditor = void 0; const Editor_1 = require("./Editor"); const selenium_webdriver_1 = require("selenium-webdriver"); const AbstractElement_1 = require("../AbstractElement"); const __1 = require("../.."); const compare_versions_1 = require("compare-versions"); /** * Page object representing the internal VS Code settings editor */ class SettingsEditor extends Editor_1.Editor { divider = (0, compare_versions_1.satisfies)(SettingsEditor.versionInfo.version, '>=1.83.0') ? '›' : ' › '; constructor(view = new __1.EditorView()) { super(view); } /** * Search for a setting with a particular title and category. * Returns an appropriate Setting object if the label is found, * undefined otherwise. * * If your setting has nested categories (i.e `example.general.test`), * pass in each category as a separate string. * * @param title title of the setting * @param categories category of the setting * @returns Promise resolving to a Setting object if found, undefined otherwise */ async findSetting(title, ...categories) { const category = categories.join(this.divider); const searchBox = await this.findElement(SettingsEditor.locators.Editor.inputArea); await searchBox.sendKeys(selenium_webdriver_1.Key.chord(SettingsEditor.ctlKey, 'a')); await searchBox.sendKeys(`${category}>${title}`); return await this._getSettingItem(title, category); } /** * Search for a setting with a precise ID. * Returns an appropriate Setting object if it exists, * undefined otherwise. * * @param id of the setting * @returns Promise resolving to a Setting object if found, undefined otherwise */ async findSettingByID(id) { const searchBox = await this.findElement(SettingsEditor.locators.Editor.inputArea); await searchBox.sendKeys(selenium_webdriver_1.Key.chord(SettingsEditor.ctlKey, 'a')); await searchBox.sendKeys(id); const title = id.split('.').pop(); return await this._getSettingItem(title); } async _getSettingItem(title = '', category = '') { const count = await this.findElement(SettingsEditor.locators.SettingsEditor.itemCount); let textCount = await count.getText(); await this.getDriver().wait(async function () { await new Promise((res) => setTimeout(res, 1500)); const text = await count.getText(); if (text !== textCount) { textCount = text; return false; } return true; }); let setting; const items = await this.findElements(SettingsEditor.locators.SettingsEditor.itemRow); for (const item of items) { try { const itemCategory = (await (await item.findElement(SettingsEditor.locators.SettingsEditor.settingCategory)).getText()).replace(':', ''); const itemTitle = await (await item.findElement(SettingsEditor.locators.SettingsEditor.settingLabel)).getText(); if (category !== '') { if (category.toLowerCase().replace(this.divider, '').replace(/\s/g, '').trim() !== itemCategory.toLowerCase().replace(this.divider, '').replace(/\s/g, '').trim()) { continue; } } if (title !== '') { if (title.toLowerCase().replace(/\s/g, '').trim() !== itemTitle.toLowerCase().replace(/\s/g, '').trim()) { continue; } } return await (await this.createSetting(item, itemTitle, itemCategory)).wait(); } catch (err) { // do nothing } } return setting; } /** * Switch between settings perspectives * Works only if your vscode instance has both user and workspace settings available * * @param perspective User or Workspace * @returns Promise that resolves when the appropriate button is clicked */ async switchToPerspective(perspective) { const actions = await this.findElement(SettingsEditor.locators.SettingsEditor.header) .findElement(SettingsEditor.locators.SettingsEditor.tabs) .findElement(SettingsEditor.locators.SettingsEditor.actions); await actions.findElement(SettingsEditor.locators.SettingsEditor.action(perspective)).click(); } /** * Context menu is disabled in this editor, throw an error */ async openContextMenu() { throw new Error('Operation not supported!'); } async createSetting(element, title, category) { await element.findElement(SettingsEditor.locators.SettingsEditor.settingConstructor(title, category)); try { // try a combo setting await element.findElement(SettingsEditor.locators.SettingsEditor.comboSetting); return new ComboSetting(SettingsEditor.locators.SettingsEditor.settingConstructor(title, category), this); } catch (err) { try { // try text setting await element.findElement(SettingsEditor.locators.SettingsEditor.textSetting); return new TextSetting(SettingsEditor.locators.SettingsEditor.settingConstructor(title, category), this); } catch (err) { try { // try checkbox setting await element.findElement(SettingsEditor.locators.SettingsEditor.checkboxSetting); return new CheckboxSetting(SettingsEditor.locators.SettingsEditor.settingConstructor(title, category), this); } catch (err) { // try link setting try { await element.findElement(SettingsEditor.locators.SettingsEditor.linkButton); return new LinkSetting(SettingsEditor.locators.SettingsEditor.settingConstructor(title, category), this); } catch (err) { // try array setting try { await element.findElement(SettingsEditor.locators.SettingsEditor.arraySetting); return new ArraySetting(SettingsEditor.locators.SettingsEditor.settingConstructor(title, category), this); } catch (err) { throw new Error('Setting type not supported!'); } } } } } } } exports.SettingsEditor = SettingsEditor; /** * Abstract item representing a Setting with title, description and * an input element (combo/textbox/checkbox/link/array) */ class Setting extends AbstractElement_1.AbstractElement { constructor(settingsConstructor, settings) { super(settingsConstructor, settings); } /** * Get the category of the setting * All settings are labeled as Category: Title */ async getCategory() { return await (await this.findElement(SettingsEditor.locators.SettingsEditor.settingCategory)).getText(); } /** * Get description of the setting * @returns Promise resolving to setting description */ async getDescription() { return await (await this.findElement(SettingsEditor.locators.SettingsEditor.settingDescription)).getText(); } /** * Get title of the setting */ async getTitle() { return await (await this.findElement(SettingsEditor.locators.SettingsEditor.settingLabel)).getText(); } } exports.Setting = Setting; /** * Setting with a combo box */ class ComboSetting extends Setting { async getValue() { const combo = await this.findElement(SettingsEditor.locators.SettingsEditor.comboSetting); return await combo.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue); } async setValue(value) { const rows = await this.getOptions(); for (const row of rows) { if ((await row.getAttribute('class')).indexOf('disabled') < 0) { const text = await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue); if (value === text) { return await row.click(); } } } } /** * Get the labels of all options from the combo * @returns Promise resolving to array of string values */ async getValues() { const values = []; const rows = await this.getOptions(); for (const row of rows) { values.push(await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue)); } return values; } async getOptions() { const combo = await this.findElement(SettingsEditor.locators.SettingsEditor.comboSetting); return await combo.findElements(SettingsEditor.locators.SettingsEditor.comboOption); } } exports.ComboSetting = ComboSetting; /** * Setting with a text box input */ class TextSetting extends Setting { async getValue() { const input = await this.findElement(SettingsEditor.locators.SettingsEditor.textSetting); return await input.getAttribute('value'); } async setValue(value) { const input = await this.findElement(SettingsEditor.locators.SettingsEditor.textSetting); await input.clear(); await input.sendKeys(value); } } exports.TextSetting = TextSetting; /** * Setting with a checkbox */ class CheckboxSetting extends Setting { async getValue() { const checkbox = await this.findElement(SettingsEditor.locators.SettingsEditor.checkboxSetting); const checked = await checkbox.getAttribute(SettingsEditor.locators.SettingsEditor.checkboxChecked); if (checked === 'true') { return true; } return false; } async setValue(value) { const checkbox = await this.findElement(SettingsEditor.locators.SettingsEditor.checkboxSetting); if ((await this.getValue()) !== value) { await checkbox.click(); } } } exports.CheckboxSetting = CheckboxSetting; /** * Setting with no value, with a link to settings.json instead */ class LinkSetting extends Setting { async getValue() { throw new Error('Method getValue is not available for LinkSetting!'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars async setValue(value) { throw new Error('Method setValue is not available for LinkSetting!'); } /** * Open the link that leads to the value in settings.json * @returns Promise resolving when the link has been clicked */ async openLink() { const link = await this.findElement(SettingsEditor.locators.SettingsEditor.linkButton); await link.click(); } } exports.LinkSetting = LinkSetting; /** * Setting with an array of string values (rows) */ class ArraySetting extends Setting { /** * @deprecated Method 'getValue' is not available for ArraySetting! */ async getValue() { throw new Error("Method 'getValue' is not available for ArraySetting!"); } /** * @deprecated Method 'setValue' is not available for ArraySetting! */ // eslint-disable-next-line @typescript-eslint/no-unused-vars async setValue(value) { throw new Error("Method 'setValue' is not available for ArraySetting!"); } /** * Select a row of a setting array of string values * @param item label | index */ async select(item) { const toSelect = await this.getItem(item); await toSelect?.select(); } /** * Get a row as new ArraySettingItem object * @param item label or index * @returns ArraySettingItem | undefined */ async getItem(item) { const row = await this.findRow(item); if (row) { return new ArraySettingItem(row, this); } return undefined; } /** * Get a list of all rows as ArraySettingItem objects * @returns ArraySettingItem[] */ async getItems() { const listRows = await this.getRows(); const items = []; for (const row of listRows) { items.push(new ArraySettingItem(row, this)); } return items; } /** * Get a list of all rows values * @returns string[] */ async getValues() { const items = await this.getItems(); const values = []; for (const item of items) { values.push(await item.getValue()); } return values; } /** * Click 'Add Item' and get row as ArraySettingItem in edit mode * @returns ArraySettingItem */ async add() { // click 'Add Item' button const button = await this.findElement(SettingsEditor.locators.SettingsEditor.arrayNewRow).findElement(SettingsEditor.locators.SettingsEditor.button); await button.click(); await new Promise((sleep) => setTimeout(sleep, 1_000)); // need to force some time to allow DOM rerender elements // get item row switched to 'edit' mode const list = await this.getListRootElement(); const editRow = await list.findElement(SettingsEditor.locators.SettingsEditor.arrayEditRow); return new ArraySettingItem(editRow, this); } /** * Select a row, then click 'Edit Item' and get row as ArraySettingItem in edit mode * @param item label | index * @returns ArraySettingItem | undefined */ async edit(item) { const row = await this.findRow(item); if (row) { // select item row const toEdit = new ArraySettingItem(row, this); await toEdit.select(); // click 'Edit Item' button const edit = await toEdit.findElement(SettingsEditor.locators.SettingsEditor.arrayBtnConstructor('Edit Item')); await edit.click(); await new Promise((sleep) => setTimeout(sleep, 1_000)); // need to force some time to allow DOM rerender elements // get item row switched to 'edit' mode const list = await this.getListRootElement(); const editRow = await list.findElement(SettingsEditor.locators.SettingsEditor.arrayEditRow); return new ArraySettingItem(editRow, this); } return undefined; } async getListRootElement() { return await this.findElement(SettingsEditor.locators.SettingsEditor.arrayRoot); } async getRows() { const list = await this.getListRootElement(); return await list.findElements(SettingsEditor.locators.SettingsEditor.arrayRow); } async findRow(item) { const listRows = await this.getRows(); if (Number.isInteger(item)) { const index = +item; if (index < 0 || index > listRows.length - 1) { throw Error(`Index '${index}' is of bounds! Found items have length = ${listRows.length}.`); } return listRows[index]; } else { for (const row of listRows) { const li = await row.findElement(SettingsEditor.locators.SettingsEditor.arrayRowValue); if ((await li.getText()) === item) { return row; } } } return undefined; } } exports.ArraySetting = ArraySetting; /** * Represents each row of an ArraySetting array of strings */ class ArraySettingItem extends AbstractElement_1.AbstractElement { constructor(element, setting) { super(element, setting); } /** * Select an item */ async select() { await this.click(); await new Promise((sleep) => setTimeout(sleep, 500)); } /** * Get item text value * @returns string */ async getValue() { return await this.getText(); } /** * Set item text value * @param value string */ async setValue(value) { const input = await this.findElement(SettingsEditor.locators.SettingsEditor.textSetting); await input.clear(); await input.sendKeys(value); } /** * Click 'Remove Item' button */ async remove() { await this.select(); const remove = await this.findElement(SettingsEditor.locators.SettingsEditor.arrayBtnConstructor('Remove Item')); await remove.click(); await new Promise((sleep) => setTimeout(sleep, 500)); } /** * Click 'OK' button * @description Only when the item is in edit mode */ async ok() { const ok = await this.findElement(SettingsEditor.locators.SettingsEditor.arraySettingItem.btnConstructor('OK')); await ok.click(); await new Promise((sleep) => setTimeout(sleep, 500)); } /** * Click 'Cancel' button * @description Only when the item is in edit mode */ async cancel() { const cancel = await this.findElement(SettingsEditor.locators.SettingsEditor.arraySettingItem.btnConstructor('Cancel')); await cancel.click(); await new Promise((sleep) => setTimeout(sleep, 500)); } } exports.ArraySettingItem = ArraySettingItem; //# sourceMappingURL=SettingsEditor.js.map