@redhat-developer/page-objects
Version:
Page Object API implementation for a VS Code editor used by ExTester framework.
471 lines • 18.5 kB
JavaScript
"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