Skip to content

Commit 6f8ba1d

Browse files
committed
[js][bidi] Add support for installing and uninstalling extension
1 parent 0a931d7 commit 6f8ba1d

File tree

4 files changed

+185
-0
lines changed

4 files changed

+185
-0
lines changed

javascript/selenium-webdriver/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ js_library(
3939
"common/*.js",
4040
"bidi/*.js",
4141
"bidi/external/*.js",
42+
"bidi/extension/*.js",
4243
]),
4344
deps = [
4445
":node_modules/@bazel/runfiles",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
const ExtensionData = require("./extensionData")
19+
20+
class Extension {
21+
constructor(driver) {
22+
this._driver = driver
23+
}
24+
25+
async init() {
26+
if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
27+
throw Error('WebDriver instance must support BiDi protocol')
28+
}
29+
30+
this.bidi = await this._driver.getBidi()
31+
}
32+
33+
async install(extensionData) {
34+
35+
if (!(extensionData instanceof ExtensionData)) {
36+
throw new Error("install() requires an ExtensionData instance")
37+
}
38+
39+
const command = {
40+
method: 'webExtension.install',
41+
params: {
42+
extensionData: extensionData.asMap()
43+
},
44+
}
45+
46+
let response = await this.bidi.send(command)
47+
return response.result.extension
48+
}
49+
50+
async uninstall(id) {
51+
const command = {
52+
method: 'webExtension.uninstall',
53+
params: {
54+
extension: id
55+
},
56+
}
57+
58+
await this.bidi.send(command)
59+
}
60+
}
61+
62+
async function getExtensionInstance(driver) {
63+
let instance = new Extension(driver)
64+
await instance.init()
65+
return instance
66+
}
67+
68+
module.exports = getExtensionInstance
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 ExtensionData {
19+
20+
#type
21+
#path
22+
23+
constructor(type, path) {
24+
this.#type = type
25+
this.#path = path
26+
}
27+
28+
static setPath(path) {
29+
return new ExtensionData("path", path)
30+
}
31+
32+
static setArchivePath(path) {
33+
return new ExtensionData("archivePath", path)
34+
}
35+
36+
static setBase64Encoded(value) {
37+
return new ExtensionData("base64", value)
38+
}
39+
40+
asMap() {
41+
let toReturn = {}
42+
toReturn["type"] = this.#type
43+
44+
if (this.#type === "base64") {
45+
toReturn["value"] = this.#path
46+
} else {
47+
toReturn["path"] = this.#path
48+
}
49+
50+
return toReturn
51+
}
52+
}
53+
54+
module.exports = ExtensionData
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict'
2+
3+
const assert = require('node:assert')
4+
const { suite } = require('../../lib/test')
5+
const Extension = require('selenium-webdriver/bidi/extension/extension')
6+
const ExtensionData = require('selenium-webdriver/bidi/extension/extensionData')
7+
const {locate} = require("../../lib/test/resources")
8+
const {Browser} = require('selenium-webdriver')
9+
const fs = require('fs')
10+
11+
suite(
12+
function (env) {
13+
let driver
14+
15+
beforeEach(async function () {
16+
driver = await env.builder().build()
17+
})
18+
19+
afterEach(async function () {
20+
await driver.quit()
21+
})
22+
23+
const WEBEXTENSION_CRX = locate('common/extensions/webextensions-selenium-example.crx')
24+
25+
const ARCHIVE_PATH= locate('common/extensions/webextensions-selenium-example.xpi')
26+
27+
describe('BiDi Module Extension', function () {
28+
it('can install extension from a given path', async function () {
29+
const extension = await Extension(driver)
30+
const id = await extension.install(ExtensionData.setPath(WEBEXTENSION_CRX))
31+
32+
assert.strictEqual(id, "[email protected]")
33+
})
34+
35+
it('can install extension from an archive path', async function () {
36+
const extension = await Extension(driver)
37+
const id = await extension.install(ExtensionData.setArchivePath(ARCHIVE_PATH))
38+
39+
assert.strictEqual(id, "[email protected]")
40+
})
41+
42+
it('can install extension from a base64 encoded path', async function () {
43+
const extension = await Extension(driver)
44+
45+
const base64Path = fs.readFileSync(ARCHIVE_PATH, { encoding: 'base64' })
46+
const id = await extension.install(ExtensionData.setBase64Encoded(base64Path))
47+
48+
assert.strictEqual(id, "[email protected]")
49+
})
50+
51+
it('can uninstall an extension', async function () {
52+
const extension = await Extension(driver)
53+
const id = await extension.install(ExtensionData.setPath(WEBEXTENSION_CRX))
54+
55+
assert.strictEqual(id, "[email protected]")
56+
57+
await extension.uninstall(id)
58+
})
59+
})
60+
},
61+
{ browsers: [Browser.FIREFOX] },
62+
)

0 commit comments

Comments
 (0)