blob: 640cbdebfca28cbd190c402e79570e2165eed8c0 [file] [log] [blame]
// 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.
'use strict'
const assert = require('node:assert')
const command = require('selenium-webdriver/lib/command')
const error = require('selenium-webdriver/lib/error')
const input = require('selenium-webdriver/lib/input')
const { WebElement } = require('selenium-webdriver/lib/webdriver')
describe('input.Actions', function () {
class StubExecutor {
constructor(...responses) {
this.responses = responses
this.commands = []
}
execute(command) {
const name = command.getName()
const parameters = command.getParameters()
this.commands.push({ name, parameters })
return this.responses.shift() || Promise.reject(new Error('unexpected command: ' + command.getName()))
}
}
describe('perform()', function () {
it('omits idle devices', async function () {
let executor = new StubExecutor(Promise.resolve(), Promise.resolve(), Promise.resolve(), Promise.resolve())
await new input.Actions(executor).perform()
assert.deepStrictEqual(executor.commands, [])
await new input.Actions(executor).pause().perform()
assert.deepStrictEqual(executor.commands, [])
await new input.Actions(executor).pause(1).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'pause', duration: 1 }],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [{ type: 'pause', duration: 1 }],
},
{
id: 'default wheel',
type: 'wheel',
actions: [{ type: 'pause', duration: 1 }],
},
],
},
},
])
executor.commands.length = 0
let actions = new input.Actions(executor)
await actions.pause(1, actions.keyboard()).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'pause', duration: 1 }],
},
],
},
},
])
})
it('can be called multiple times', async function () {
const executor = new StubExecutor(Promise.resolve(), Promise.resolve())
const actions = new input.Actions(executor).keyDown(input.Key.SHIFT)
const expected = {
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'keyDown', value: input.Key.SHIFT }],
},
],
},
}
await actions.perform()
assert.deepStrictEqual(executor.commands, [expected])
await actions.perform()
assert.deepStrictEqual(executor.commands, [expected, expected])
})
})
describe('pause()', function () {
it('defaults to all devices', async function () {
const executor = new StubExecutor(Promise.resolve())
await new input.Actions(executor).pause(3).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'pause', duration: 3 }],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [{ type: 'pause', duration: 3 }],
},
{
id: 'default wheel',
type: 'wheel',
actions: [
{
duration: 3,
type: 'pause',
},
],
},
],
},
},
])
})
it('duration defaults to 0', async function () {
const executor = new StubExecutor(Promise.resolve())
await new input.Actions(executor).pause().pause(3).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 3 },
],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 3 },
],
},
{
id: 'default wheel',
type: 'wheel',
actions: [
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 3 },
],
},
],
},
},
])
})
it('single device w/ synchronization', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.pause(100, actions.keyboard()).pause(100, actions.mouse()).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'pause', duration: 100 },
{ type: 'pause', duration: 0 },
],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 100 },
],
},
],
},
},
])
})
it('single device w/o synchronization', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor, { async: true })
await actions.pause(100, actions.keyboard()).pause(100, actions.mouse()).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'pause', duration: 100 }],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [{ type: 'pause', duration: 100 }],
},
],
},
},
])
})
it('pause a single device multiple times by specifying it multiple times', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.pause(100, actions.keyboard(), actions.keyboard()).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'pause', duration: 100 },
{ type: 'pause', duration: 100 },
],
},
],
},
},
])
})
})
describe('keyDown()', function () {
it('sends normalized code point', async function () {
let executor = new StubExecutor(Promise.resolve())
await new input.Actions(executor).keyDown('\u0041\u030a').perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'keyDown', value: '\u00c5' }],
},
],
},
},
])
})
it('rejects keys that are not a single code point', function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
assert.throws(() => actions.keyDown('\u1E9B\u0323'), error.InvalidArgumentError)
})
})
describe('keyUp()', function () {
it('sends normalized code point', async function () {
let executor = new StubExecutor(Promise.resolve())
await new input.Actions(executor).keyUp('\u0041\u030a').perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [{ type: 'keyUp', value: '\u00c5' }],
},
],
},
},
])
})
it('rejects keys that are not a single code point', function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
assert.throws(() => actions.keyUp('\u1E9B\u0323'), error.InvalidArgumentError)
})
})
describe('sendKeys()', function () {
it('sends down/up for single key', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.sendKeys('a').perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'keyDown', value: 'a' },
{ type: 'keyUp', value: 'a' },
],
},
],
},
},
])
})
it('sends down/up for vararg keys', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.sendKeys('a', 'b').perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'keyDown', value: 'a' },
{ type: 'keyUp', value: 'a' },
{ type: 'keyDown', value: 'b' },
{ type: 'keyUp', value: 'b' },
],
},
],
},
},
])
})
it('sends down/up for multichar strings in varargs', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.sendKeys('a', 'bc', 'd').perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'keyDown', value: 'a' },
{ type: 'keyUp', value: 'a' },
{ type: 'keyDown', value: 'b' },
{ type: 'keyUp', value: 'b' },
{ type: 'keyDown', value: 'c' },
{ type: 'keyUp', value: 'c' },
{ type: 'keyDown', value: 'd' },
{ type: 'keyUp', value: 'd' },
],
},
],
},
},
])
})
it('synchronizes with other devices', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.sendKeys('ab').pause(100, actions.mouse()).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'keyDown', value: 'a' },
{ type: 'keyUp', value: 'a' },
{ type: 'keyDown', value: 'b' },
{ type: 'keyUp', value: 'b' },
{ type: 'pause', duration: 0 },
],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 100 },
],
},
],
},
},
])
})
it('without device synchronization', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor, { async: true })
await actions.sendKeys('ab').pause(100, actions.mouse()).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'keyDown', value: 'a' },
{ type: 'keyUp', value: 'a' },
{ type: 'keyDown', value: 'b' },
{ type: 'keyUp', value: 'b' },
],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [{ type: 'pause', duration: 100 }],
},
],
},
},
])
})
it('string length > 500', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor, { async: true })
let str = ''
for (let i = 0; i < 501; i++) {
str += i
}
const executionResult = await actions
.sendKeys(str)
.perform()
.then(() => true)
.catch(() => false)
assert.strictEqual(executionResult, true)
})
})
describe('click()', function () {
it('clicks immediately if no element provided', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
await actions.click().perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{
type: 'pointerDown',
button: input.Button.LEFT,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{ type: 'pointerUp', button: input.Button.LEFT },
],
},
],
},
},
])
})
it('moves to target element before clicking', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
const fakeElement = {}
await actions.click(fakeElement).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{
type: 'pointerMove',
origin: fakeElement,
duration: 100,
x: 0,
y: 0,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{
type: 'pointerDown',
button: input.Button.LEFT,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{ type: 'pointerUp', button: input.Button.LEFT },
],
},
],
},
},
])
})
it('synchronizes with other devices', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
const fakeElement = {}
await actions.click(fakeElement).sendKeys('a').perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default keyboard',
type: 'key',
actions: [
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 0 },
{ type: 'keyDown', value: 'a' },
{ type: 'keyUp', value: 'a' },
],
},
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{
type: 'pointerMove',
origin: fakeElement,
duration: 100,
x: 0,
y: 0,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{
type: 'pointerDown',
button: input.Button.LEFT,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{ type: 'pointerUp', button: input.Button.LEFT },
{ type: 'pause', duration: 0 },
{ type: 'pause', duration: 0 },
],
},
],
},
},
])
})
})
describe('dragAndDrop', function () {
it('dragAndDrop(fromEl, toEl)', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
const e1 = new WebElement(null, 'abc123')
const e2 = new WebElement(null, 'def456')
await actions.dragAndDrop(e1, e2).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{
type: 'pointerMove',
duration: 100,
origin: e1,
x: 0,
y: 0,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{
type: 'pointerDown',
button: input.Button.LEFT,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{
type: 'pointerMove',
duration: 100,
origin: e2,
x: 0,
y: 0,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{ type: 'pointerUp', button: input.Button.LEFT },
],
},
],
},
},
])
})
it('dragAndDrop(el, offset)', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
const e1 = new WebElement(null, 'abc123')
await actions.dragAndDrop(e1, { x: 30, y: 40 }).perform()
assert.deepStrictEqual(executor.commands, [
{
name: command.Name.ACTIONS,
parameters: {
actions: [
{
id: 'default mouse',
type: 'pointer',
parameters: { pointerType: 'mouse' },
actions: [
{
type: 'pointerMove',
duration: 100,
origin: e1,
x: 0,
y: 0,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{
type: 'pointerDown',
button: input.Button.LEFT,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{
type: 'pointerMove',
duration: 100,
origin: input.Origin.POINTER,
x: 30,
y: 40,
altitudeAngle: 0,
azimuthAngle: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
},
{ type: 'pointerUp', button: input.Button.LEFT },
],
},
],
},
},
])
})
it('throws if target is invalid', async function () {
const executor = new StubExecutor(Promise.resolve())
const actions = new input.Actions(executor)
const e = new WebElement(null, 'abc123')
assert.throws(() => actions.dragAndDrop(e), error.InvalidArgumentError)
assert.throws(() => actions.dragAndDrop(e, null), error.InvalidArgumentError)
assert.throws(() => actions.dragAndDrop(e, {}), error.InvalidArgumentError)
assert.throws(() => actions.dragAndDrop(e, { x: 0 }), error.InvalidArgumentError)
assert.throws(() => actions.dragAndDrop(e, { y: 0 }), error.InvalidArgumentError)
assert.throws(() => actions.dragAndDrop(e, { x: 0, y: 'a' }), error.InvalidArgumentError)
assert.throws(() => actions.dragAndDrop(e, { x: 'a', y: 0 }), error.InvalidArgumentError)
})
})
})