Skip to content

Commit a920039

Browse files
committed
Add page object for WebviewView
Fixes #804 Signed-off-by: David Thompson <davthomp@redhat.com>
1 parent b0c8edd commit a920039

File tree

5 files changed

+149
-4
lines changed

5 files changed

+149
-4
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Locator, WebElement, until } from "selenium-webdriver";
2+
import { AbstractElement } from "../AbstractElement";
3+
4+
/**
5+
* Page object representing a user-contributed panel implemented using a Webview.
6+
*/
7+
export class WebviewView extends AbstractElement {
8+
9+
constructor() {
10+
super(WebviewView.locators.Workbench.constructor);
11+
}
12+
13+
private static handle: string | undefined;
14+
15+
/**
16+
* Search for an element inside the webview iframe.
17+
* Requires webdriver being switched to the webview iframe first.
18+
* (Will attempt to search from the main DOM root otherwise)
19+
*
20+
* @param locator webdriver locator to search by
21+
* @returns promise resolving to WebElement when found
22+
*/
23+
async findWebElement(locator: Locator): Promise<WebElement> {
24+
return this.getDriver().findElement(locator);
25+
}
26+
27+
/**
28+
* Search for all element inside the webview iframe by a given locator
29+
* Requires webdriver being switched to the webview iframe first.
30+
* (Will attempt to search from the main DOM root otherwise)
31+
*
32+
* @param locator webdriver locator to search by
33+
* @returns promise resolving to a list of WebElement objects
34+
*/
35+
async findWebElements(locator: Locator): Promise<WebElement[]> {
36+
return this.getDriver().findElements(locator);
37+
}
38+
39+
/**
40+
* Switch the underlying webdriver context to the webview iframe.
41+
* This allows using the findWebElement methods.
42+
* Note that only elements inside the webview iframe will be accessible.
43+
* Use the switchBack method to switch to the original context.
44+
*/
45+
async switchToFrame(): Promise<void> {
46+
if (!WebviewView.handle) {
47+
WebviewView.handle = await this.getDriver().getWindowHandle();
48+
}
49+
50+
const view = await this.getDriver().wait(until.elementLocated(WebviewView.locators.WebView.iframe)) as WebElement;
51+
52+
await this.getDriver().switchTo().frame(view);
53+
54+
await this.getDriver().wait(until.elementLocated(WebviewView.locators.WebView.activeFrame), 5000);
55+
const frame = await this.getDriver().findElement(WebviewView.locators.WebView.activeFrame);
56+
await this.getDriver().switchTo().frame(frame);
57+
}
58+
59+
/**
60+
* Switch the underlying webdriver back to the original window
61+
*/
62+
async switchBack(): Promise<void> {
63+
if (!WebviewView.handle) {
64+
WebviewView.handle = await this.getDriver().getWindowHandle();
65+
}
66+
return this.getDriver().switchTo().window(WebviewView.handle);
67+
}
68+
69+
}

page-objects/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export * from './components/sidebar/debug/DebugView';
3636

3737
export * from './components/bottomBar/BottomBarPanel';
3838
export * from './components/bottomBar/ProblemsView';
39+
export * from './components/bottomBar/WebviewView';
3940
export * from './components/bottomBar/Views';
4041
export * from './components/statusBar/StatusBar';
4142

@@ -64,7 +65,7 @@ export * from './conditions/WaitForAttribute';
6465

6566
/**
6667
* Initialize the page objects for your tests
67-
*
68+
*
6869
* @param currentVersion version of the locators to load
6970
* @param baseVersion base version of the locators if you have multiple versions with diffs, otherwise leave the same as currentVersion
7071
* @param locatorFolder folder that contains locator files

test/test-project/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,21 @@
8181
{
8282
"command": "extension.disableCodeLens",
8383
"title": "Disable Codelens"
84+
},
85+
{
86+
"command": "extension.myPanelView.focus",
87+
"title": "Focus My Panel View"
8488
}
8589
],
90+
"viewsContainers": {
91+
"panel": [
92+
{
93+
"icon": "./media/paw-outline.svg",
94+
"id": "myPanel",
95+
"title": "My Panel"
96+
}
97+
]
98+
},
8699
"views": {
87100
"explorer": [
88101
{
@@ -93,6 +106,13 @@
93106
"id": "emptyView",
94107
"name": "Empty View"
95108
}
109+
],
110+
"myPanel": [
111+
{
112+
"id": "myPanelView",
113+
"name": "My Panel View",
114+
"type": "webview"
115+
}
96116
]
97117
},
98118
"viewsWelcome": [
@@ -122,6 +142,11 @@
122142
"group": "navigation",
123143
"when": "resourceFilename =~ /Untitled.*$/"
124144
}
145+
],
146+
"commandPalette": [
147+
{
148+
"command": "extension.myPanelView.focus"
149+
}
125150
]
126151
}
127152
},

test/test-project/src/extension.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import * as vscode from 'vscode';
21
import * as path from 'path';
3-
import { TreeView } from './treeView';
2+
import * as vscode from 'vscode';
43
import { CatScratchEditorProvider } from './catScratchEditor';
54
import { CodelensProvider } from './codelensProvider';
5+
import { TreeView } from './treeView';
66

77
export const ERROR_MESSAGE_COMMAND = 'extension.errorMsg';
88

@@ -60,7 +60,7 @@ export function activate(context: vscode.ExtensionContext) {
6060
"extension.populateTestView",
6161
() => { emptyViewNoContent = false; emitter.fire(undefined); }
6262
));
63-
63+
6464
const codelensProvider = new CodelensProvider();
6565
context.subscriptions.push(vscode.languages.registerCodeLensProvider("*", codelensProvider));
6666
context.subscriptions.push(
@@ -75,6 +75,14 @@ export function activate(context: vscode.ExtensionContext) {
7575
vscode.commands.registerCommand("extension.codelensAction", (args: any) => {
7676
vscode.window.showInformationMessage(`CodeLens action clicked with args=${args}`);
7777
}));
78+
context.subscriptions.push(
79+
vscode.window.registerWebviewViewProvider("myPanelView", new MyPanelView())
80+
);
81+
context.subscriptions.push(
82+
vscode.commands.registerCommand("extension.myPanelView.focus", async () => {
83+
await vscode.commands.executeCommand("myPanelView.focus");
84+
})
85+
);
7886
}
7987

8088
export function deactivate() {}
@@ -135,4 +143,10 @@ class TestView {
135143
</body>
136144
</html>`;
137145
}
146+
}
147+
148+
class MyPanelView implements vscode.WebviewViewProvider {
149+
resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext<unknown>, token: vscode.CancellationToken): void | Thenable<void> {
150+
webviewView.webview.html = "<!DOCTYPE html><html><head><title>My Panel View</title></head><body><div><h1>Shopping List</h1><ul><li>Apple</li><li>Banana</li></ul></div></body></html>";
151+
}
138152
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { expect } from 'chai';
2+
import { BottomBarPanel, By, InputBox, WebviewView, Workbench } from 'vscode-extension-tester';
3+
4+
describe('WebviewView', function () {
5+
6+
let webviewView: WebviewView;
7+
8+
before(async function () {
9+
const prompt = await new Workbench().openCommandPrompt() as InputBox;
10+
await prompt.setText('>Focus My Panel View');
11+
await prompt.confirm();
12+
});
13+
14+
after(async function () {
15+
if (webviewView) {
16+
await webviewView.switchBack();
17+
webviewView = undefined;
18+
}
19+
await new BottomBarPanel().toggle(false);
20+
});
21+
22+
it('contains apple and banana', async () => {
23+
webviewView = new WebviewView();
24+
await webviewView.switchToFrame();
25+
const elts = await webviewView.findWebElements(By.xpath('//div/ul/li'));
26+
console.log(JSON.stringify(elts));
27+
const listContent: string[] = [];
28+
await Promise.all(elts.map(async elt => {
29+
listContent.push(await elt.getText());
30+
}));
31+
expect(listContent).to.have.length(2);
32+
expect(listContent).to.contain('Apple');
33+
expect(listContent).to.contain('Banana');
34+
});
35+
36+
});

0 commit comments

Comments
 (0)