@serenity-js/web
Version:
Serenity/JS Screenplay Pattern library offering a flexible, web driver-agnostic approach for interacting with web-based user interfaces and components, suitable for various testing contexts
170 lines • 5.96 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Press = void 0;
const core_1 = require("@serenity-js/core");
const io_1 = require("@serenity-js/core/lib/io");
const abilities_1 = require("../abilities");
const models_1 = require("../models");
const PageElementInteraction_1 = require("./PageElementInteraction");
/**
* Instructs an [actor](https://serenity-js.org/api/core/class/Actor/) who has the [ability](https://serenity-js.org/api/core/class/Ability/) to [`BrowseTheWeb`](https://serenity-js.org/api/web/class/BrowseTheWeb/)
* to send a key press or a sequence of keys to a Web element.
*
* **Note:** On macOS, some keyboard shortcuts might not work
* with the [`devtools` protocol](https://webdriver.io/docs/automationProtocols/#devtools-protocol).
*
* For example:
* - to *copy*, instead of [`Key.Meta`](https://serenity-js.org/api/web/class/Key/#Meta)+`C`, use [`Key.Control`](https://serenity-js.org/api/web/class/Key/#Control)+[`Key.Insert`](https://serenity-js.org/api/web/class/Key/#Insert)
* - to *cut*, instead of [`Key.Meta`](https://serenity-js.org/api/web/class/Key/#Meta)+`X`, use [`Key.Control`](https://serenity-js.org/api/web/class/Key/#Control)+[`Key.Delete`](https://serenity-js.org/api/web/class/Key/#Delete)
* - to *paste*, instead of [`Key.Meta`](https://serenity-js.org/api/web/class/Key/#Meta)+`V`, use [`Key.Shift`](https://serenity-js.org/api/web/class/Key/#Shift)+[`Key.Insert`](https://serenity-js.org/api/web/class/Key/#Insert)
*
* ## Example widget
*
* ```html
* <form>
* <input type="text" name="example" id="example" />
* </form>
* ```
*
* ## Lean Page Object describing the widget
*
* ```ts
* import { By, PageElement } from '@serenity-js/web'
*
* class Form {
* static exampleInput = () =>
* PageElement.located(By.id('example'))
* .describedAs('example input')
* }
* ```
*
* ## Pressing keys
*
* ```ts
* import { actorCalled } from '@serenity-js/core'
* import { Key, Press, Value } from '@serenity-js/web'
* import { Ensure, equals } from '@serenity-js/assertions'
*
* await actorCalled('Priyanka')
* .attemptsTo(
* Press.the('H', 'i', '!', Key.ENTER).in(Form.exampleInput()),
* Ensure.that(Value.of(Form.exampleInput), equals('Hi!')),
* )
* ```
*
* ## Learn more
*
* - [`Key`](https://serenity-js.org/api/web/class/Key/)
* - [`BrowseTheWeb`](https://serenity-js.org/api/web/class/BrowseTheWeb/)
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElement/)
*
* @group Activities
*/
class Press extends PageElementInteraction_1.PageElementInteraction {
keys;
/**
* Instantiates an [interaction](https://serenity-js.org/api/core/class/Interaction/)
* that instructs the [actor](https://serenity-js.org/api/core/class/Actor/)
* to press a sequence of [keys](https://serenity-js.org/api/web/class/Key/),
*
* When no `field` is specified, the key sequence will be sent to the currently focused element,
* and if no element is focused - to the `document.body` to handle.
*
* @param keys
* A sequence of one or more keys to press
*/
static the(...keys) {
return new Press(KeySequence.of(keys));
}
/**
* Send the key sequence to a specific element.
*
* @param field
*/
in(field) {
return new PressKeyInField(this.keys, field);
}
/**
* @param keys
* A sequence of one or more keys to press
*/
constructor(keys) {
super((0, core_1.the) `#actor presses ${keys}`);
this.keys = keys;
}
/**
* @inheritDoc
*/
async performAs(actor) {
const keys = await actor.answer(this.keys);
const page = await abilities_1.BrowseTheWeb.as(actor).currentPage();
return page.sendKeys(keys);
}
}
exports.Press = Press;
class PressKeyInField extends PageElementInteraction_1.PageElementInteraction {
keys;
field;
/**
* @param {Answerable<Array<Key | string>>} keys
* A sequence of one or more keys to press
*
* @param {Answerable<PageElement>} field
* Web element to send the keys to
*/
constructor(keys, field /* todo | Question<AlertPromise> | AlertPromise */) {
super((0, core_1.the) `#actor presses ${keys} in ${field}`, core_1.Interaction.callerLocation(3));
this.keys = keys;
this.field = field;
}
/**
* @inheritDoc
*/
async performAs(actor) {
const field = await this.resolve(actor, this.field);
const keys = await actor.answer(this.keys);
const page = await abilities_1.BrowseTheWeb.as(actor).currentPage();
// fix for protractor
await page.executeScript(
/* c8 ignore next */
function focus(element) {
element.focus();
}, await field);
return page.sendKeys(keys);
}
}
/**
* @package
*/
class KeySequence extends core_1.Question {
keys;
static of(keys) {
return new KeySequence(keys);
}
constructor(keys) {
super(KeySequence.describe(keys));
this.keys = keys;
}
async answeredBy(actor) {
const keys = await (0, io_1.asyncMap)(this.keys, key => actor.answer(key));
return keys
.flat()
.filter(key => !!key);
}
static describe(keys) {
const prefix = keys.length === 1 ? 'key' : 'keys';
const description = keys.reduce((acc, key, index) => {
const separator = models_1.Key.isKey(key) && key.isModifier
? '-'
: acc.separator;
return {
description: index === 0
? `${key}`
: `${acc.description}${acc.separator}${key}`,
separator,
};
}, { description: '', separator: ', ' }).description;
return `${prefix} ${description}`;
}
}
//# sourceMappingURL=Press.js.map