Skip to content

Commit de9a229

Browse files
[js][BiDi] Browsing context commands (#11473)
* [js][bidi] Browsing context commands * added suggestions Co-authored-by: Sri Harsha <12621691+harsha509@users.noreply.github.com>
1 parent c138787 commit de9a229

File tree

3 files changed

+301
-0
lines changed

3 files changed

+301
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
class BrowsingContext {
19+
constructor(driver) {
20+
this._driver = driver
21+
}
22+
23+
async init({ browsingContextId, type, referenceContext }) {
24+
if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
25+
throw Error('WebDriver instance must support BiDi protocol')
26+
}
27+
28+
if (type != undefined && !['window', 'tab'].includes(type)) {
29+
throw Error(`Valid types are 'window' & 'tab'. Received: ${type}`)
30+
}
31+
32+
this.bidi = await this._driver.getBidi()
33+
this._id =
34+
browsingContextId == undefined
35+
? (await this.create(type, referenceContext))['result']['context']
36+
: browsingContextId
37+
}
38+
39+
/**
40+
* Creates a browsing context for the given type and referenceContext
41+
*/
42+
async create(type, referenceContext) {
43+
const params = {
44+
method: 'browsingContext.create',
45+
params: {
46+
type: type,
47+
referenceContext: referenceContext,
48+
},
49+
}
50+
return await this.bidi.send(params)
51+
}
52+
53+
/**
54+
* @returns id
55+
*/
56+
get id() {
57+
return this._id
58+
}
59+
60+
/**
61+
* @param url the url to navigate to
62+
* @param readinessState type of readiness state: "none" / "interactive" / "complete"
63+
* @returns NavigateResult object
64+
*/
65+
async navigate(url, readinessState = undefined) {
66+
if (
67+
readinessState != undefined &&
68+
!['none', 'interactive', 'complete'].includes(readinessState)
69+
) {
70+
throw Error(
71+
`Valid readiness states are 'none', 'interactive' & 'complete'. Received: ${readinessState}`
72+
)
73+
}
74+
75+
const params = {
76+
method: 'browsingContext.navigate',
77+
params: {
78+
context: this._id,
79+
url: url,
80+
wait: readinessState,
81+
},
82+
}
83+
const navigateResult = (await this.bidi.send(params))['result']
84+
85+
return new NavigateResult(
86+
navigateResult['url'],
87+
navigateResult['navigation']
88+
)
89+
}
90+
91+
/**
92+
* @param maxDepth the max depth of the descendents of browsing context tree
93+
* @returns BrowsingContextInfo object
94+
*/
95+
async getTree(maxDepth = undefined) {
96+
const params = {
97+
method: 'browsingContext.getTree',
98+
params: {
99+
root: this._id,
100+
maxDepth: maxDepth,
101+
},
102+
}
103+
104+
let result = await this.bidi.send(params)
105+
if ('error' in result) {
106+
throw Error(result['error'])
107+
}
108+
109+
result = result['result']['contexts'][0]
110+
return new BrowsingContextInfo(
111+
result['context'],
112+
result['url'],
113+
result['children'],
114+
result['parent']
115+
)
116+
}
117+
118+
/**
119+
* Closes the browing context
120+
* @returns {Promise<void>}
121+
*/
122+
async close() {
123+
const params = {
124+
method: 'browsingContext.close',
125+
params: {
126+
context: this._id,
127+
},
128+
}
129+
await this.bidi.send(params)
130+
}
131+
}
132+
133+
class NavigateResult {
134+
constructor(url, navigationId) {
135+
this._url = url
136+
this._navigationId = navigationId
137+
}
138+
139+
get url() {
140+
return this._url
141+
}
142+
143+
get navigationId() {
144+
return this._navigationId
145+
}
146+
}
147+
148+
class BrowsingContextInfo {
149+
constructor(id, url, children, parentBrowsingContext) {
150+
this._id = id
151+
this._url = url
152+
this._children = children
153+
this._parentBrowsingContext = parentBrowsingContext
154+
}
155+
156+
get id() {
157+
return this._id
158+
}
159+
160+
get url() {
161+
return this._url
162+
}
163+
164+
get children() {
165+
return this._children
166+
}
167+
168+
get parentBrowsingContext() {
169+
return this._parentBrowsingContext
170+
}
171+
}
172+
173+
/**
174+
* initiate browsing context instance and return
175+
* @param driver
176+
* @param browsingContextId The browsing context of current window/tab
177+
* @param type "window" or "tab"
178+
* @param referenceContext To get a browsing context for this reference if passed
179+
* @returns {Promise<BrowsingContext>}
180+
*/
181+
async function getBrowsingContextInstance(
182+
driver,
183+
{ browsingContextId, type, referenceContext }
184+
) {
185+
let instance = new BrowsingContext(driver)
186+
await instance.init({ browsingContextId, type, referenceContext })
187+
return instance
188+
}
189+
190+
/**
191+
* API
192+
* @type {function(*, {*,*,*}): Promise<BrowsingContext>}
193+
*/
194+
module.exports = getBrowsingContextInstance

javascript/node/selenium-webdriver/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const until = require('./lib/until')
4040
const webdriver = require('./lib/webdriver')
4141
const select = require('./lib/select')
4242
const LogInspector = require('./bidi/logInspector')
43+
const BrowsingContext = require('./bidi/browsingContext')
4344

4445
const Browser = capabilities.Browser
4546
const Capabilities = capabilities.Capabilities
@@ -797,3 +798,4 @@ exports.promise = promise
797798
exports.until = until
798799
exports.Select = select.Select
799800
exports.LogInspector = LogInspector
801+
exports.BrowsingContext = BrowsingContext

javascript/node/selenium-webdriver/test/bidi/bidi_test.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const firefox = require('../../firefox')
2222
const { Browser } = require('../../')
2323
const { Pages, suite } = require('../../lib/test')
2424
const logInspector = require('../../bidi/logInspector')
25+
const BrowsingContext = require('../../bidi/browsingContext')
2526

2627
suite(
2728
function (env) {
@@ -104,6 +105,110 @@ suite(
104105
await inspector.close()
105106
})
106107
})
108+
109+
describe('Browsing Context', function () {
110+
it('can create a browsing context for given id', async function () {
111+
const id = await driver.getWindowHandle()
112+
const browsingContext = await BrowsingContext(driver, {
113+
browsingContextId: id,
114+
})
115+
assert.equal(browsingContext.id, id)
116+
})
117+
118+
it('can create a window', async function () {
119+
const browsingContext = await BrowsingContext(driver, {
120+
type: 'window',
121+
})
122+
assert.notEqual(browsingContext.id, null)
123+
})
124+
125+
it('can create a window with a reference context', async function () {
126+
const browsingContext = await BrowsingContext(driver, {
127+
type: 'window',
128+
referenceContext: await driver.getWindowHandle(),
129+
})
130+
assert.notEqual(browsingContext.id, null)
131+
})
132+
133+
it('can create a tab', async function () {
134+
const browsingContext = await BrowsingContext(driver, {
135+
type: 'tab',
136+
})
137+
assert.notEqual(browsingContext.id, null)
138+
})
139+
140+
it('can create a tab with a reference context', async function () {
141+
const browsingContext = await BrowsingContext(driver, {
142+
type: 'tab',
143+
referenceContext: await driver.getWindowHandle(),
144+
})
145+
assert.notEqual(browsingContext.id, null)
146+
})
147+
148+
it('can navigate to a url', async function () {
149+
const browsingContext = await BrowsingContext(driver, {
150+
type: 'tab',
151+
})
152+
153+
let info = await browsingContext.navigate(Pages.logEntryAdded)
154+
155+
assert.notEqual(browsingContext.id, null)
156+
assert.equal(info.navigationId, null)
157+
assert(info.url.includes('/bidi/logEntryAdded.html'))
158+
})
159+
160+
it('can navigate to a url with readiness state', async function () {
161+
const browsingContext = await BrowsingContext(driver, {
162+
type: 'tab',
163+
})
164+
165+
const info = await browsingContext.navigate(
166+
Pages.logEntryAdded,
167+
'complete'
168+
)
169+
170+
assert.notEqual(browsingContext.id, null)
171+
assert.equal(info.navigationId, null)
172+
assert(info.url.includes('/bidi/logEntryAdded.html'))
173+
})
174+
175+
it('can get tree with a child', async function () {
176+
const browsingContextId = await driver.getWindowHandle()
177+
const parentWindow = await BrowsingContext(driver, {
178+
browsingContextId: browsingContextId,
179+
})
180+
await parentWindow.navigate(Pages.iframePage, 'complete')
181+
182+
const contextInfo = await parentWindow.getTree()
183+
assert.equal(contextInfo.children.length, 1)
184+
assert.equal(contextInfo.id, browsingContextId)
185+
assert(contextInfo.children[0]['url'].includes('formPage.html'))
186+
})
187+
188+
it('can get tree with depth', async function () {
189+
const browsingContextId = await driver.getWindowHandle()
190+
const parentWindow = await BrowsingContext(driver, {
191+
browsingContextId: browsingContextId,
192+
})
193+
await parentWindow.navigate(Pages.iframePage, 'complete')
194+
195+
const contextInfo = await parentWindow.getTree(0)
196+
assert.equal(contextInfo.children, null)
197+
assert.equal(contextInfo.id, browsingContextId)
198+
})
199+
200+
it('can close a window', async function () {
201+
const window1 = await BrowsingContext(driver, { type: 'window' })
202+
const window2 = await BrowsingContext(driver, { type: 'window' })
203+
204+
await window2.close()
205+
206+
assert.doesNotThrow(async function () {
207+
await window1.getTree()
208+
})
209+
await assert.rejects(window2.getTree(), { message: 'no such frame' })
210+
})
211+
})
107212
},
108213
{ browsers: [Browser.FIREFOX] }
109214
)

0 commit comments

Comments
 (0)