diff --git a/docs/DebugView.md b/docs/DebugView.md index 8ed6fae24..bad0c3c68 100644 --- a/docs/DebugView.md +++ b/docs/DebugView.md @@ -25,3 +25,61 @@ await debugConfiguration.selectLaunchConfiguration("Test Launch"); // start selected launch configuration await debugView.start(); ``` + +### Sections + +It's possible to work with all individual sections. + +#### Variables + +![Variables Section](./images/debugView-Variables.png) + +```typescript +import { DebugVariableSection } from 'vscode-extension-tester'; +... +const variableSection = await debugView.getVariablesSection(); +... +await variablesSection?.openItem('Local'); // open section +const var = await variablesSection.findItem('variableName'); // get one variable +const items = await variablesSection.getVisibleItems(); // get all variables +... +const name = var.getVariableName(); // get name +const value = var.getVariableValue(); // get current value +await var.setVariableValue('newValue'); // change value +``` + +#### Watch + +![Watch Section](./images/debugView-WatchSection.png) + +```typescript +import { WatchSection } from 'vscode-extension-tester'; +... +const watchSection = await debugView.getWatchSection(); +... +const items = await watchSection.getVisibleItems(); // get all items +await watchSection.removeAllExpressions(); // remove all expressions +... +await watchSection.addItem('name'); // add new expression +const item = await items.at(num); // get expression at position num +const label = await item.getLabel(); // get label of expression +const value = await item.getValu(); // get value of expression +await item.remove(); // remove expression from watch section +``` + +#### Call Stack + +![Call Stack Section](./images/debugView-CallStack.png) + +```typescript +import { CallStackSection } from 'vscode-extension-tester'; +... +const callStack = await debugView.getCallStackSection(); +... +const items = await callStack.getVisibleItems(); // get all items +... +const item = await items.at(num); // get item at position num +const label = await item.getLabel(); // get label of item +const text = await item.getText(); // get text of item +const btns = await item.getActionButtons(); // get available action buttons +``` diff --git a/docs/images/debugView-CallStack.png b/docs/images/debugView-CallStack.png new file mode 100644 index 000000000..4c6934b8c Binary files /dev/null and b/docs/images/debugView-CallStack.png differ diff --git a/docs/images/debugView-Variables.png b/docs/images/debugView-Variables.png new file mode 100644 index 000000000..42695e700 Binary files /dev/null and b/docs/images/debugView-Variables.png differ diff --git a/docs/images/debugView-WatchSection.png b/docs/images/debugView-WatchSection.png new file mode 100644 index 000000000..e8e3edad0 Binary files /dev/null and b/docs/images/debugView-WatchSection.png differ diff --git a/packages/locators/lib/1.37.0.ts b/packages/locators/lib/1.37.0.ts index 2d2556a48..c95595592 100644 --- a/packages/locators/lib/1.37.0.ts +++ b/packages/locators/lib/1.37.0.ts @@ -377,6 +377,19 @@ const sideBar = { label: By.className('monaco-highlighted-label'), text: By.className('state label monaco-count-badge long'), }, + WatchSection: { + predicate: async (section: ViewSection) => (await section.getTitle()).toLowerCase() === 'watch', + input: By.css('input'), + addExpression: 'Add Expression', + refresh: 'Refresh', + removeAll: 'Remove All Expressions', + collapseAll: 'Collapse All', + }, + WatchSectionItem: { + label: By.className('monaco-highlighted-label'), + value: By.xpath(`.//*[contains(@class, 'value ') and starts-with(@class, 'value ')]`), + remove: 'Remove Expression', + }, ExtensionsViewSection: { items: By.className('monaco-list-rows'), itemRow: By.className('monaco-list-row'), diff --git a/packages/page-objects/src/components/sidebar/debug/DebugView.ts b/packages/page-objects/src/components/sidebar/debug/DebugView.ts index e43e6de77..320927da2 100644 --- a/packages/page-objects/src/components/sidebar/debug/DebugView.ts +++ b/packages/page-objects/src/components/sidebar/debug/DebugView.ts @@ -19,6 +19,7 @@ import { SideBarView } from '../SideBarView'; import { DebugBreakpointSection } from '../tree/debug/DebugBreakpointSection'; import { DebugCallStackSection } from '../tree/debug/DebugCallStackSection'; import { DebugVariableSection } from '../tree/debug/DebugVariablesSection'; +import { WatchSection } from '../tree/debug/WatchSection'; /** * Page object representing the Run/Debug view in the side bar @@ -100,4 +101,13 @@ export class DebugView extends SideBarView { const content = this.getContent(); return content.getSection(DebugCallStackSection.locators.DebugCallStackSection.predicate, DebugCallStackSection); } + + /** + * Get Watch section + * @returns WatchSection page object + */ + async getWatchSection(): Promise { + const content = this.getContent(); + return content.getSection(WatchSection.locators.WatchSection.predicate, WatchSection); + } } diff --git a/packages/page-objects/src/components/sidebar/tree/debug/WatchSection.ts b/packages/page-objects/src/components/sidebar/tree/debug/WatchSection.ts new file mode 100644 index 000000000..ed64ced17 --- /dev/null +++ b/packages/page-objects/src/components/sidebar/tree/debug/WatchSection.ts @@ -0,0 +1,60 @@ +/** + * 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. + */ + +import { Key, WebElement } from 'selenium-webdriver'; +import { ViewContent } from '../../ViewContent'; +import { GenericCustomTreeSection } from '../custom/CustomTreeSection'; +import { WatchSectionItem } from './WatchSectionItem'; + +export class WatchSection extends GenericCustomTreeSection { + constructor(panel: WebElement, viewContent: ViewContent) { + super(panel, viewContent, WatchSectionItem); + } + + /** + * Add item to Watch section. + * @param name + */ + async addItem(name: string): Promise { + await (await this.getAction(WatchSection.locators.WatchSection.addExpression))?.click(); + await new Promise((res) => setTimeout(res, 1000)); + const textInput = await this.findElement(WatchSection.locators.WatchSection.input); + await textInput.clear(); + await textInput.sendKeys(name + Key.ENTER); + } + + /** + * Click on 'Refresh' button. + */ + async refresh(): Promise { + await (await this.getAction(WatchSection.locators.WatchSection.refresh))?.click(); + } + + /** + * Remove all items in Watch seection by using 'Remove All Expression' button. + */ + async removeAllExpressions(): Promise { + await (await this.getAction(WatchSection.locators.WatchSection.removeAll))?.click(); + } + + /** + * Collapse all items in Watch section by using 'Collapse All' button. + */ + async collapseAll(): Promise { + await (await this.getAction(WatchSection.locators.WatchSection.collapseAll))?.click(); + } +} diff --git a/packages/page-objects/src/components/sidebar/tree/debug/WatchSectionItem.ts b/packages/page-objects/src/components/sidebar/tree/debug/WatchSectionItem.ts new file mode 100644 index 000000000..4a0302c28 --- /dev/null +++ b/packages/page-objects/src/components/sidebar/tree/debug/WatchSectionItem.ts @@ -0,0 +1,52 @@ +/** + * 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. + */ + +import { WebElement } from 'selenium-webdriver'; +import { TreeSection } from '../TreeSection'; +import { CustomTreeItem } from '../custom/CustomTreeItem'; + +export class WatchSectionItem extends CustomTreeItem { + constructor(element: WebElement, viewPart: TreeSection) { + super(element, viewPart); + } + + /** + * Get label of the Watch section item. + * @returns a promise resolving to Watch section item label string + */ + async getLabel(): Promise { + const name = await this.findElement(WatchSectionItem.locators.WatchSectionItem.label); + return await name?.getAttribute('textContent'); + } + + /** + * Get value of the Watch section item. + * @returns a promise resolving to Watch section value label string + */ + async getValue(): Promise { + const value = await this.findElement(WatchSectionItem.locators.WatchSectionItem.value); + return await value.getText(); + } + + /** + * Remove item from Watch section. + */ + async remove(): Promise { + const button = await this.getActionButton(WatchSectionItem.locators.WatchSectionItem.remove); + await button?.click(); + } +} diff --git a/packages/page-objects/src/index.ts b/packages/page-objects/src/index.ts index bc78af405..32df73194 100644 --- a/packages/page-objects/src/index.ts +++ b/packages/page-objects/src/index.ts @@ -52,6 +52,8 @@ export * from './components/sidebar/tree/debug/DebugVariablesSection'; export * from './components/sidebar/tree/debug/VariableSectionItem'; export * from './components/sidebar/tree/debug/CallStackItem'; export * from './components/sidebar/tree/debug/DebugCallStackSection'; +export * from './components/sidebar/tree/debug/WatchSection'; +export * from './components/sidebar/tree/debug/WatchSectionItem'; export * from './components/sidebar/extensions/ExtensionsViewSection'; export * from './components/sidebar/extensions/ExtensionsViewItem'; export { ScmView, ScmProvider, ScmChange } from './components/sidebar/scm/ScmView'; diff --git a/packages/page-objects/src/locators/locators.ts b/packages/page-objects/src/locators/locators.ts index d6e4c5576..2dc672ac6 100644 --- a/packages/page-objects/src/locators/locators.ts +++ b/packages/page-objects/src/locators/locators.ts @@ -379,6 +379,19 @@ export interface Locators { label: By; text: By; }; + WatchSection: { + predicate: WebElementFunction; + input: By; + addExpression: string; + refresh: string; + removeAll: string; + collapseAll: string; + }; + WatchSectionItem: { + label: By; + value: By; + remove: string; + }; ExtensionsViewSection: { items: By; itemRow: By; diff --git a/tests/test-project/src/test/debug/debug.test.ts b/tests/test-project/src/test/debug/debug.test.ts index 56001d8c6..945affd4b 100644 --- a/tests/test-project/src/test/debug/debug.test.ts +++ b/tests/test-project/src/test/debug/debug.test.ts @@ -30,6 +30,7 @@ import { TextEditor, until, VSBrowser, + WatchSection, WebDriver, Workbench, } from 'vscode-extension-tester'; @@ -98,6 +99,7 @@ describe('Debugging', function () { let driver: WebDriver; let breakpoint!: Breakpoint; let callStack: DebugCallStackSection; + let watchSection: WatchSection; before(async function () { editor = (await new EditorView().openEditor('test.js')) as TextEditor; @@ -291,6 +293,60 @@ describe('Debugging', function () { await item?.setVariableValue('42'); item = await getNumVariable(view, this.timeout() - 2000); expect(await item?.getVariableValue()).equals('42'); + await item + ?.getDriver() + .actions() + .move({ x: Math.ceil(0), y: Math.ceil(0) }) + .click() + .perform(); // dismiss hover to prevent errors in next tests + }); + + it('WatchSection.getWatchSection', async function () { + watchSection = await view.getWatchSection(); + expect(watchSection).not.undefined; + }); + + it('WatchSection.getVisibleItems', async function () { + const items = await watchSection.getVisibleItems(); + expect(items.length).equals(0); + }); + + it('WatchSection.addItem', async function () { + await watchSection.addItem('num'); + await watchSection.addItem('bool'); + await watchSection.addItem('line'); + + const items = await watchSection.getVisibleItems(); + expect(items.length).equals(3); + }); + + it('WatchSectionItem.getLabel', async function () { + const items = await watchSection.getVisibleItems(); + expect(await items.at(0)?.getLabel()).to.contain('num'); + expect(await items.at(1)?.getLabel()).to.contain('bool'); + expect(await items.at(2)?.getLabel()).to.contain('line'); + }); + + it('WatchSectionItem.getValue', async function () { + const items = await watchSection.getVisibleItems(); + expect(await items.at(0)?.getValue()).to.contain('42'); + expect(await items.at(1)?.getValue()).to.contain('false'); + expect(await items.at(2)?.getValue()).to.contain("'line'"); + }); + + it('WatchSectionItem.remove', async function () { + let items = await watchSection.getVisibleItems(); + await items.at(0)?.remove(); + items = await watchSection.getVisibleItems(); + expect(items.length).equals(2); + expect(await items.at(0)?.getLabel()).to.contain('bool'); + expect(await items.at(1)?.getLabel()).to.contain('line'); + }); + + it('WatchSection.removeAllExpressions', async function () { + await watchSection.removeAllExpressions(); + const items = await watchSection.getVisibleItems(); + expect(items.length).equals(0); }); it('CallStack: getCallStackSection', async function () {