Skip to content

Commit

Permalink
[bidi][js] Update the capture screenshot APIs to include all paramete…
Browse files Browse the repository at this point in the history
…rs and remove scroll parameter (#13744)
  • Loading branch information
pujagani committed Mar 28, 2024
1 parent 63e8156 commit b7d831d
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 16 deletions.
30 changes: 21 additions & 9 deletions javascript/node/selenium-webdriver/bidi/browsingContext.js
Expand Up @@ -19,6 +19,7 @@ const { InvalidArgumentError, NoSuchFrameError } = require('../lib/error')
const { BrowsingContextInfo } = require('./browsingContextTypes')
const { SerializationOptions, ReferenceValue, RemoteValue } = require('./protocolValue')
const { WebElement } = require('../lib/webdriver')
const { CaptureScreenshotParameters } = require('./captureScreenshotParameters')

class Locator {
static Type = Object.freeze({
Expand Down Expand Up @@ -203,12 +204,27 @@ class BrowsingContext {
return new PrintResult(response.result.data)
}

async captureScreenshot() {
async captureScreenshot(captureScreenshotParameters = undefined) {
if (
captureScreenshotParameters !== undefined &&
!(captureScreenshotParameters instanceof CaptureScreenshotParameters)
) {
throw new InvalidArgumentError(
`Pass in a CaptureScreenshotParameters object. Received: ${captureScreenshotParameters}`,
)
}

const screenshotParams = new Map()
screenshotParams.set('context', this._id)
if (captureScreenshotParameters !== undefined) {
captureScreenshotParameters.asMap().forEach((value, key) => {
screenshotParams.set(key, value)
})
}

let params = {
method: 'browsingContext.captureScreenshot',
params: {
context: this._id,
},
params: Object.fromEntries(screenshotParams),
}

const response = await this.bidi.send(params)
Expand All @@ -231,15 +247,12 @@ class BrowsingContext {
},
}

console.log(JSON.stringify(params))

const response = await this.bidi.send(params)
console.log(JSON.stringify(response))
this.checkErrorInScreenshot(response)
return response['result']['data']
}

async captureElementScreenshot(sharedId, handle = undefined, scrollIntoView = undefined) {
async captureElementScreenshot(sharedId, handle = undefined) {
let params = {
method: 'browsingContext.captureScreenshot',
params: {
Expand All @@ -250,7 +263,6 @@ class BrowsingContext {
sharedId: sharedId,
handle: handle,
},
scrollIntoView: scrollIntoView,
},
},
}
Expand Down
@@ -0,0 +1,64 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://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.

const { BoxClipRectangle, ElementClipRectangle } = require('./clipRectangle')
const Origin = {
VIEWPORT: 'viewport',
DOCUMENT: 'document',
}

class CaptureScreenshotParameters {
#map = new Map()

origin(origin) {
if (origin !== Origin.VIEWPORT && origin !== Origin.DOCUMENT) {
throw new Error(`Origin must be one of ${Object.values(Origin)}. Received:'${origin}'`)
}
this.#map.set('origin', origin)
return this
}

imageFormat(type, quality = undefined) {
if (typeof type !== 'string') {
throw new Error(`Type must be an instance of String. Received:'${type}'`)
}

this.#map.set('type', type)

if (quality !== undefined) {
if (typeof quality !== 'number') {
throw new Error(`Quality must be a number. Received:'${quality}'`)
}
this.#map.set('quality', quality)
}
return this
}

clipRectangle(clipRectangle) {
if (!(clipRectangle instanceof BoxClipRectangle || clipRectangle instanceof ElementClipRectangle)) {
throw new Error(`ClipRectangle must be an instance of ClipRectangle. Received:'${clipRectangle}'`)
}
this.#map.set('clip', Object.fromEntries(clipRectangle.asMap()))
return this
}

asMap() {
return this.#map
}
}

module.exports = { CaptureScreenshotParameters, Origin }
87 changes: 87 additions & 0 deletions javascript/node/selenium-webdriver/bidi/clipRectangle.js
@@ -0,0 +1,87 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://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.

class ClipRectangle {
clipType

constructor(type) {
this.clipType = type
}

get type() {
return this.clipType
}

asMap() {}
}

class ElementClipRectangle extends ClipRectangle {
#sharedId
#handleId

constructor(sharedId, handleId = undefined) {
super('element')
this.#sharedId = sharedId

if (handleId !== undefined) {
this.#handleId = handleId
}
}

asMap() {
const map = new Map()
map.set('type', super.type)

const sharedReference = new Map()
sharedReference.set('sharedId', this.#sharedId)
if (this.#handleId !== undefined) {
sharedReference.set('handleId', this.#handleId)
}

map.set('element', Object.fromEntries(sharedReference))

return map
}
}

class BoxClipRectangle extends ClipRectangle {
#x
#y
#width
#height

constructor(x, y, width, height) {
super('box')
this.#x = x
this.#y = y
this.#width = width
this.#height = height
}

asMap() {
const map = new Map()
map.set('type', super.type)
map.set('x', this.#x)
map.set('y', this.#y)
map.set('width', this.#width)
map.set('height', this.#height)

return map
}
}

module.exports = { BoxClipRectangle, ElementClipRectangle }
Expand Up @@ -23,6 +23,8 @@ const { Browser, By } = require('../../')
const { Pages, suite } = require('../../lib/test')
const BrowsingContext = require('../../bidi/browsingContext')
const until = require('../../lib/until')
const { Origin, CaptureScreenshotParameters } = require('../../bidi/captureScreenshotParameters')
const { BoxClipRectangle, ElementClipRectangle } = require('../../bidi/clipRectangle')

suite(
function (env) {
Expand Down Expand Up @@ -190,19 +192,22 @@ suite(
assert.equal(base64code, pngMagicNumber)
})

it('can take box screenshot', async function () {
it('can take screenshot with all parameters for box screenshot', async function () {
const id = await driver.getWindowHandle()
const browsingContext = await BrowsingContext(driver, {
browsingContextId: id,
})

const response = await browsingContext.captureBoxScreenshot(5, 5, 10, 10)
let captureScreenshotParams = new CaptureScreenshotParameters()
captureScreenshotParams.origin(Origin.VIEWPORT).clipRectangle(new BoxClipRectangle(5, 5, 10, 10))

const response = await browsingContext.captureScreenshot(captureScreenshotParams)

const base64code = response.slice(startIndex, endIndex)
assert.equal(base64code, pngMagicNumber)
})

it('can take element screenshot', async function () {
it('can take screenshot with all parameters for element screenshot', async function () {
const id = await driver.getWindowHandle()
const browsingContext = await BrowsingContext(driver, {
browsingContextId: id,
Expand All @@ -211,22 +216,38 @@ suite(
await driver.get(Pages.formPage)
const element = await driver.findElement(By.id('checky'))
const elementId = await element.getId()
const response = await browsingContext.captureElementScreenshot(elementId)

let captureScreenshotParams = new CaptureScreenshotParameters()
captureScreenshotParams.origin(Origin.VIEWPORT).clipRectangle(new ElementClipRectangle(elementId))

const response = await browsingContext.captureScreenshot(captureScreenshotParams)

const base64code = response.slice(startIndex, endIndex)
assert.equal(base64code, pngMagicNumber)
})

it('can take box screenshot', async function () {
const id = await driver.getWindowHandle()
const browsingContext = await BrowsingContext(driver, {
browsingContextId: id,
})

const response = await browsingContext.captureBoxScreenshot(5, 5, 10, 10)

const base64code = response.slice(startIndex, endIndex)
assert.equal(base64code, pngMagicNumber)
})

it('can scroll and take element screenshot', async function () {
it('can take element screenshot', async function () {
const id = await driver.getWindowHandle()
const browsingContext = await BrowsingContext(driver, {
browsingContextId: id,
})

await driver.get(Pages.formPage)
const element = await driver.findElement(By.id('checkbox-with-label'))
const element = await driver.findElement(By.id('checky'))
const elementId = await element.getId()
const response = await browsingContext.captureElementScreenshot(elementId, undefined, true)
const response = await browsingContext.captureElementScreenshot(elementId)

const base64code = response.slice(startIndex, endIndex)
assert.equal(base64code, pngMagicNumber)
Expand Down

0 comments on commit b7d831d

Please sign in to comment.