Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@serenity-js/web

Package Overview
Dependencies
Maintainers
1
Versions
122
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@serenity-js/web - npm Package Compare versions

Comparing version 3.0.0-rc.13 to 3.0.0-rc.14

6

lib/errors/index.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

5

lib/expectations/isVisible.d.ts

@@ -5,3 +5,6 @@ import { Expectation } from '@serenity-js/core';

* @desc
* Expectation that the element is present in the DOM of the page and visible.
* Expectation that the element is present in the DOM of the page and:
* - is not hidden, so doesn't have `display: none`, `visibility: hidden` or `opacity: 0`
* - is within the browser viewport
* - doesn't have its centre covered by other elements
*

@@ -8,0 +11,0 @@ * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}

@@ -7,3 +7,6 @@ "use strict";

* @desc
* Expectation that the element is present in the DOM of the page and visible.
* Expectation that the element is present in the DOM of the page and:
* - is not hidden, so doesn't have `display: none`, `visibility: hidden` or `opacity: 0`
* - is within the browser viewport
* - doesn't have its centre covered by other elements
*

@@ -10,0 +13,0 @@ * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

@@ -52,3 +52,12 @@ import { Answerable, Optional, QuestionAdapter } from '@serenity-js/core';

abstract isSelected(): Promise<boolean>;
/**
* @desc
* Checks if the PageElement:
* - is not hidden, so doesn't have CSS style like `display: none`, `visibility: hidden` or `opacity: 0`
* - is within the browser viewport
* - doesn't have its centre covered by other elements
*
* @returns {Promise<boolean>}
*/
abstract isVisible(): Promise<boolean>;
}

@@ -18,5 +18,4 @@ "use strict";

}
// todo: review usages and consider removing if not used
static of(childElement, parentElement) {
return core_1.Question.about((0, core_1.d) `${childElement} of ${parentElement})`, async (actor) => {
return core_1.Question.about((0, core_1.d) `${childElement} of ${parentElement}`, async (actor) => {
const child = await actor.answer(childElement);

@@ -23,0 +22,0 @@ const parent = await actor.answer(parentElement);

@@ -19,3 +19,3 @@ "use strict";

return new PageElements(relativeToParent(this.locator, parent))
.describedAs(`<<${this.toString()}>>` + (0, core_1.f) `.of(${parent})`);
.describedAs(`${this.toString()} of ${parent}`);
}

@@ -37,3 +37,3 @@ }

function relativeToParent(relativeLocator, parent) {
return core_1.Question.about(relativeLocator.toString() + (0, core_1.f) `.of${parent}`, async (actor) => {
return core_1.Question.about(`${relativeLocator.toString()} of ${parent}`, async (actor) => {
const locator = await actor.answer(relativeLocator);

@@ -40,0 +40,0 @@ const parentElement = await actor.answer(parent);

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

@@ -1,8 +0,6 @@

import { Answerable, AnswersQuestions, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
import { Answerable, AnswersQuestions, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
import { PageElement } from '../models';
import { ElementQuestion } from './ElementQuestion';
/**
* @desc
* Returns the value of the given HTML attribute of a given {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>
* Returns the value of the specified HTML attribute of a given {@link PageElement}.
*

@@ -16,33 +14,56 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve a HTML attribute of a given WebElement</caption>
* @example <caption>Retrieve an HTML attribute of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { Attribute, by, BrowseTheWeb, Target } from '@serenity-js/webdriverio';
* import { Attribute, By, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* Target.the('shopping list').located(by.id('shopping-list'))
* PageElement.located(By.id('shopping-list')).describedAs('shopping list');
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(Attribute.called('data-items-left').of(shoppingList()), equals('2')),
* Ensure.that(
* Attribute.called('data-items-left').of(shoppingList()),
* equals('2')
* ),
* )
*
* @example <caption>Find WebElements with a given attribute</caption>
* @example <caption>Using Attribute as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { Attribute, By, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* Attribute.called('id').of(shoppingList()).toLocaleUpperCase(),
* equals('SHOPPING-LIST')
* ),
* )
*
* @example <caption>Find PageElements with a given attribute</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, includes } from '@serenity-js/assertions';
* import { Attribute, BrowseTheWeb, by, Target } from '@serenity-js/webdriverio';
* import { Attribute, By, PageElements } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* class ShoppingList {
* static items = () =>
* Target.all('items')
* .located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items');
*
* static outstandingItems = () =>
* ShoppingList.items
* .where(Attribute.called('data-state'), includes('buy'))
* ShoppingList.items()
* .where(Attribute.called('data-state'), includes('buy'));
* }
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -55,9 +76,13 @@ * Ensure.that(

*
* @extends {ElementQuestion}
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
export declare class Attribute extends ElementQuestion<Promise<string>> implements MetaQuestion<Answerable<PageElement>, Promise<string>> {
export declare class Attribute extends Question<Promise<string>> implements MetaQuestion<Answerable<PageElement>, Promise<string>> {
private readonly name;
private readonly element?;
/**
* @private
*/
private subject;
/**
* @param {Answerable<string>} name

@@ -71,16 +96,28 @@ * @returns {Attribute}

*/
constructor(name: Answerable<string>, element?: Answerable<PageElement>);
protected constructor(name: Answerable<string>, element?: Answerable<PageElement>);
/**
* @desc
* Resolves to the value of a HTML attribute of the `target` element,
* located in the context of a `parent` element.
* Resolves to the value of an HTML attribute of the `target` element,
* located within the `parent` element.
*
* @param {Answerable<PageElement>} parent
* @returns {Question<Promise<string[]>>}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*
* @see {@link Target.all}
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
of(parent: Answerable<PageElement>): Question<Promise<string>>;
of(parent: Answerable<PageElement>): QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>>;
answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string>;
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this;
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string;
}

@@ -6,7 +6,5 @@ "use strict";

const models_1 = require("../models");
const ElementQuestion_1 = require("./ElementQuestion");
/**
* @desc
* Returns the value of the given HTML attribute of a given {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>
* Returns the value of the specified HTML attribute of a given {@link PageElement}.
*

@@ -20,33 +18,56 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve a HTML attribute of a given WebElement</caption>
* @example <caption>Retrieve an HTML attribute of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { Attribute, by, BrowseTheWeb, Target } from '@serenity-js/webdriverio';
* import { Attribute, By, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* Target.the('shopping list').located(by.id('shopping-list'))
* PageElement.located(By.id('shopping-list')).describedAs('shopping list');
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(Attribute.called('data-items-left').of(shoppingList()), equals('2')),
* Ensure.that(
* Attribute.called('data-items-left').of(shoppingList()),
* equals('2')
* ),
* )
*
* @example <caption>Find WebElements with a given attribute</caption>
* @example <caption>Using Attribute as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { Attribute, By, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* Attribute.called('id').of(shoppingList()).toLocaleUpperCase(),
* equals('SHOPPING-LIST')
* ),
* )
*
* @example <caption>Find PageElements with a given attribute</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, includes } from '@serenity-js/assertions';
* import { Attribute, BrowseTheWeb, by, Target } from '@serenity-js/webdriverio';
* import { Attribute, By, PageElements } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* class ShoppingList {
* static items = () =>
* Target.all('items')
* .located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items');
*
* static outstandingItems = () =>
* ShoppingList.items
* .where(Attribute.called('data-state'), includes('buy'))
* ShoppingList.items()
* .where(Attribute.called('data-state'), includes('buy'));
* }
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -59,6 +80,6 @@ * Ensure.that(

*
* @extends {ElementQuestion}
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
class Attribute extends ElementQuestion_1.ElementQuestion {
class Attribute extends core_1.Question {
/**

@@ -69,5 +90,8 @@ * @param {Answerable<string>} name

constructor(name, element) {
super(`"${name}" attribute of ${element}`);
super();
this.name = name;
this.element = element;
this.subject = element
? (0, core_1.d) `${name} attribute of ${element}`
: (0, core_1.d) `${name} attribute`;
}

@@ -83,26 +107,43 @@ /**

* @desc
* Resolves to the value of a HTML attribute of the `target` element,
* located in the context of a `parent` element.
* Resolves to the value of an HTML attribute of the `target` element,
* located within the `parent` element.
*
* @param {Answerable<PageElement>} parent
* @returns {Question<Promise<string[]>>}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*
* @see {@link Target.all}
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
of(parent) {
return new Attribute(this.name, this.element
return core_1.Question.createAdapter(new Attribute(this.name, this.element
? models_1.PageElement.of(this.element, parent)
: parent);
: parent));
}
async answeredBy(actor) {
const name = await actor.answer(this.name);
if (!this.element) {
throw new core_1.LogicError(`Target not specified`); // todo: better error message?
throw new core_1.LogicError((0, core_1.d) `Couldn't read attribute ${name} of an unspecified page element.`);
}
const element = await actor.answer(this.element);
const name = await actor.answer(this.name);
return element.attribute(name);
}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject) {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString() {
return this.subject;
}
}
exports.Attribute = Attribute;
//# sourceMappingURL=Attribute.js.map

@@ -1,8 +0,7 @@

import { Answerable, AnswersQuestions, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
import { Answerable, AnswersQuestions, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
import { PageElement } from '../models';
import { ElementQuestion } from './ElementQuestion';
/**
* @desc
* Resolves to an array of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
* of a given {@link WebElement}, represented by Answerable<{@link @wdio/types~Element}>.
* of a given {@link PageElement}.
*

@@ -16,28 +15,55 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve CSS classes of a given WebElement</caption>
* @example <caption>Retrieve CSS classes of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* Target.the('shopping list').located(by.id('shopping-list'))
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(CssClasses.of(shoppingList()), equals([ 'active', 'favourite' ])),
* Ensure.that(
* CssClasses.of(shoppingList()),
* equals([ 'active', 'favourite' ])
* ),
* )
*
* @example <caption>Find WebElements with a given class</caption>
* @example <caption>Using CssClasses as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* CssClasses.of(shoppingList()).length,
* equals(2)
* ),
* Ensure.that(
* CssClasses.of(shoppingList())[0],
* equals('active')
* ),
* )
*
* @example <caption>Find PageElements with a given class</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, contain } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* class ShoppingList {
* static items = () =>
* Target.all('items')
* .located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items')
*
* static outstandingItems = () =>
* ShoppingList.items
* ShoppingList.items()
* .where(CssClasses, contain('buy'))

@@ -47,3 +73,3 @@ * }

* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -56,25 +82,30 @@ * Ensure.that(

*
* @extends {ElementQuestion}
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
export declare class CssClasses extends ElementQuestion<Promise<string[]>> implements MetaQuestion<Answerable<PageElement>, Promise<string[]>> {
private readonly target;
export declare class CssClasses extends Question<Promise<string[]>> implements MetaQuestion<Answerable<PageElement>, Promise<string[]>> {
private readonly pageElement;
/**
* @param {Question<PageElement> | PageElement} target
* @returns {CssClasses}
* @private
*/
static of(target: Answerable<PageElement>): CssClasses;
private subject;
/**
* @param {Question<PageElement> | PageElement} target
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string[]>}
*
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
constructor(target: Answerable<PageElement>);
static of(pageElement: Answerable<PageElement>): QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>;
/**
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
*/
protected constructor(pageElement: Answerable<PageElement>);
/**
* @desc
* Resolves to an array of CSS classes of the `target` element,
* located in the context of a `parent` element.
* Resolves to an array of CSS classes of the `pageElement`,
* located within the `parent` element.
*
* @param {@serenity-js/core/lib/screenplay~Answerable<Element>} parent
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {Question<Promise<string[]>>}
*
* @see {@link Target.all}
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

@@ -96,2 +127,15 @@ */

answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string[]>;
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this;
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CssClasses = void 0;
const io_1 = require("@serenity-js/core/lib/io");
const core_1 = require("@serenity-js/core");
const models_1 = require("../models");
const ElementQuestion_1 = require("./ElementQuestion");
/**
* @desc
* Resolves to an array of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
* of a given {@link WebElement}, represented by Answerable<{@link @wdio/types~Element}>.
* of a given {@link PageElement}.
*

@@ -19,28 +18,55 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve CSS classes of a given WebElement</caption>
* @example <caption>Retrieve CSS classes of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* Target.the('shopping list').located(by.id('shopping-list'))
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(CssClasses.of(shoppingList()), equals([ 'active', 'favourite' ])),
* Ensure.that(
* CssClasses.of(shoppingList()),
* equals([ 'active', 'favourite' ])
* ),
* )
*
* @example <caption>Find WebElements with a given class</caption>
* @example <caption>Using CssClasses as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* CssClasses.of(shoppingList()).length,
* equals(2)
* ),
* Ensure.that(
* CssClasses.of(shoppingList())[0],
* equals('active')
* ),
* )
*
* @example <caption>Find PageElements with a given class</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, contain } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* class ShoppingList {
* static items = () =>
* Target.all('items')
* .located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items')
*
* static outstandingItems = () =>
* ShoppingList.items
* ShoppingList.items()
* .where(CssClasses, contain('buy'))

@@ -50,3 +76,3 @@ * }

* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -59,33 +85,35 @@ * Ensure.that(

*
* @extends {ElementQuestion}
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
class CssClasses extends ElementQuestion_1.ElementQuestion {
class CssClasses extends core_1.Question {
/**
* @param {Question<PageElement> | PageElement} target
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
*/
constructor(target) {
super((0, io_1.formatted) `CSS classes of ${target}`);
this.target = target;
constructor(pageElement) {
super();
this.pageElement = pageElement;
this.subject = (0, core_1.d) `CSS classes of ${pageElement}`;
}
/**
* @param {Question<PageElement> | PageElement} target
* @returns {CssClasses}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string[]>}
*
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
static of(target) {
return new CssClasses(target);
static of(pageElement) {
return core_1.Question.createAdapter(new CssClasses(pageElement));
}
/**
* @desc
* Resolves to an array of CSS classes of the `target` element,
* located in the context of a `parent` element.
* Resolves to an array of CSS classes of the `pageElement`,
* located within the `parent` element.
*
* @param {@serenity-js/core/lib/screenplay~Answerable<Element>} parent
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {Question<Promise<string[]>>}
*
* @see {@link Target.all}
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
of(parent) {
return new CssClasses(models_1.PageElement.of(this.target, parent));
return new CssClasses(models_1.PageElement.of(this.pageElement, parent));
}

@@ -105,3 +133,3 @@ /**

async answeredBy(actor) {
const element = await this.resolve(actor, this.target);
const element = await actor.answer(this.pageElement);
return element.attribute('class')

@@ -115,4 +143,22 @@ .then(attribute => attribute !== null && attribute !== void 0 ? attribute : '')

}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject) {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString() {
return this.subject;
}
}
exports.CssClasses = CssClasses;
//# sourceMappingURL=CssClasses.js.map
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

@@ -1,2 +0,2 @@

import { Answerable, Question } from '@serenity-js/core';
import { Answerable, List, Question } from '@serenity-js/core';
import { PageElement } from '../models';

@@ -90,7 +90,7 @@ /**

*
* @returns {Question<Promise<string[]>>}
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
*
* @see {@link Select.values}
*/
static valuesOf(pageElement: Answerable<PageElement>): Question<Promise<string[]>>;
static valuesOf(pageElement: Answerable<PageElement>): List<string>;
/**

@@ -181,7 +181,7 @@ * @desc

*
* @returns {Question<Promise<string[]>>}
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
*
* @see {@link Select.options}
*/
static optionsIn(pageElement: Answerable<PageElement>): Question<Promise<string[]>>;
static optionsIn(pageElement: Answerable<PageElement>): List<string>;
}

@@ -100,3 +100,3 @@ "use strict";

*
* @returns {Question<Promise<string[]>>}
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
*

@@ -201,3 +201,3 @@ * @see {@link Select.values}

*
* @returns {Question<Promise<string[]>>}
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
*

@@ -204,0 +204,0 @@ * @see {@link Select.options}

@@ -6,4 +6,4 @@ import { Answerable, MetaQuestion, QuestionAdapter } from '@serenity-js/core';

* Resolves to the visible (i.e. not hidden by CSS) `innerText` of:
* - a given {@link WebElement}, represented by Answerable<{@link @wdio/types~Element}>
* - a group of {@link WebElement}s, represented by Answerable<{@link @wdio/types~ElementList}>
* - a given {@link PageElement}
* - a group of {@link PageElements}
*

@@ -23,9 +23,11 @@ * The result includes the visible text of any sub-elements, without any leading or trailing whitespace.

* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Target, Text } from '@serenity-js/webdriverio';
* import { By, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const header = () =>
* Target.the('header').located(by.tagName('h1'))
* PageElement.located(By.css('h1'))
* .describedAs('header')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -38,9 +40,11 @@ * Ensure.that(Text.of(header()), equals('Shopping list')),

* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Target, Text } from '@serenity-js/webdriverio';
* import { By, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingListItems = () =>
* Target.the('shopping list items').located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('shopping list items')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -56,6 +60,8 @@ * Ensure.that(

* import { contain, Ensure } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target, Text } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingListItemCalled = (name: string) =>
* Target.the('shopping list items').located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('shopping list items')
* .where(Text, equals(name))

@@ -65,3 +71,3 @@ * .first()

* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -80,7 +86,6 @@ * Ensure.that(

* @desc
* Retrieves text of a single {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>.
* Retrieves text of a single {@link PageElement}.
*
* @param {Answerable<PageElement>} element
* @returns {Question<Promise<string>> & MetaQuestion<Answerable<PageElement>, Promise<string>>}
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*

@@ -93,7 +98,6 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

* @desc
* Retrieves text of a group of {@link WebElement}s,
* represented by Answerable<{@link @wdio/types~ElementList}>
* Retrieves text of a group of {@link PageElements}.
*
* @param {Answerable<PageElement[]>} elements
* @returns {Question<Promise<string[]>> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>}
* @param {Answerable<PageElements | PageElement[]>} elements
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string[]>}
*

@@ -100,0 +104,0 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

@@ -7,8 +7,7 @@ "use strict";

const models_1 = require("../models");
const ElementQuestion_1 = require("./ElementQuestion");
/**
* @desc
* Resolves to the visible (i.e. not hidden by CSS) `innerText` of:
* - a given {@link WebElement}, represented by Answerable<{@link @wdio/types~Element}>
* - a group of {@link WebElement}s, represented by Answerable<{@link @wdio/types~ElementList}>
* - a given {@link PageElement}
* - a group of {@link PageElements}
*

@@ -28,9 +27,11 @@ * The result includes the visible text of any sub-elements, without any leading or trailing whitespace.

* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Target, Text } from '@serenity-js/webdriverio';
* import { By, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const header = () =>
* Target.the('header').located(by.tagName('h1'))
* PageElement.located(By.css('h1'))
* .describedAs('header')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -43,9 +44,11 @@ * Ensure.that(Text.of(header()), equals('Shopping list')),

* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Target, Text } from '@serenity-js/webdriverio';
* import { By, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingListItems = () =>
* Target.the('shopping list items').located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('shopping list items')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -61,6 +64,8 @@ * Ensure.that(

* import { contain, Ensure } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target, Text } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingListItemCalled = (name: string) =>
* Target.the('shopping list items').located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('shopping list items')
* .where(Text, equals(name))

@@ -70,3 +75,3 @@ * .first()

* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -85,7 +90,6 @@ * Ensure.that(

* @desc
* Retrieves text of a single {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>.
* Retrieves text of a single {@link PageElement}.
*
* @param {Answerable<PageElement>} element
* @returns {Question<Promise<string>> & MetaQuestion<Answerable<PageElement>, Promise<string>>}
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*

@@ -108,6 +112,7 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

exports.Text = Text;
class TextOfSingleElement extends ElementQuestion_1.ElementQuestion {
class TextOfSingleElement extends core_1.Question {
constructor(element) {
super(`the text of ${element}`);
super();
this.element = element;
this.subject = (0, core_1.d) `the text of ${element}`;
}

@@ -124,7 +129,26 @@ static of(element) {

}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject) {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString() {
return this.subject;
}
}
class TextOfMultipleElements extends ElementQuestion_1.ElementQuestion {
class TextOfMultipleElements extends core_1.Question {
constructor(elements) {
super((0, core_1.d) `the text of ${elements}`);
super();
this.elements = elements;
this.subject = (0, core_1.d) `the text of ${elements}`;
}

@@ -141,3 +165,21 @@ static of(elements) {

}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject) {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString() {
return this.subject;
}
}
//# sourceMappingURL=Text.js.map

@@ -1,8 +0,6 @@

import { Answerable, AnswersQuestions, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
import { Answerable, AnswersQuestions, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
import { PageElement } from '../models';
import { ElementQuestion } from './ElementQuestion';
/**
* @desc
* Returns the `value` attribute of a given {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>
* Retrieves the `value` attribute of a given {@link PageElement}.
*

@@ -12,12 +10,13 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve CSS classes of a given WebElement</caption>
* @example <caption>Retrieve the `value` of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Value, Target } from '@serenity-js/webdriverio';
* import { By, PageElement, Value } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const usernameField = () =>
* Target.the('username field').located(by.id('username'))
* PageElement.located(By.id('username')).describedAs('username field')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -27,13 +26,40 @@ * Ensure.that(Value.of(usernameField), equals('Alice')),

*
* @example <caption>Using Value as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { By, PageElement, Value } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const usernameField = () =>
* PageElement.located(By.id('username')).describedAs('username field')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* Value.of(usernameField).toLocaleLowerCase()[0],
* equals('a') // [a]lice
* ),
* )
*
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
export declare class Value extends ElementQuestion<Promise<string>> implements MetaQuestion<Answerable<PageElement>, Promise<string>> {
export declare class Value extends Question<Promise<string>> implements MetaQuestion<Answerable<PageElement>, Promise<string>> {
private readonly element;
/**
* @param {Answerable<PageElement>} element
* @returns {Value}
* @private
*/
static of(element: Answerable<PageElement>): Question<Promise<string>> & MetaQuestion<Answerable<PageElement>, Promise<string>>;
private subject;
/**
* @desc
* Retrieves the `value` attribute of a given {@link PageElement}.
*
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} element
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
static of(element: Answerable<PageElement>): QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>>;
/**
* @param {Answerable<PageElement>} element

@@ -44,7 +70,7 @@ */

* @desc
* Resolves to the value of a given [`input`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
* {@link WebElement}, located in the context of a `parent` element.
* Retrieves the `value` attribute of a given {@link PageElement}.
* located within the `parent` element.
*
* @param {Answerable<PageElement>} parent
* @returns {Question<Promise<string>>}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*

@@ -67,2 +93,15 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string>;
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this;
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Value = void 0;
const io_1 = require("@serenity-js/core/lib/io");
const core_1 = require("@serenity-js/core");
const models_1 = require("../models");
const ElementQuestion_1 = require("./ElementQuestion");
/**
* @desc
* Returns the `value` attribute of a given {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>
* Retrieves the `value` attribute of a given {@link PageElement}.
*

@@ -15,12 +13,13 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve CSS classes of a given WebElement</caption>
* @example <caption>Retrieve the `value` of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Value, Target } from '@serenity-js/webdriverio';
* import { By, PageElement, Value } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const usernameField = () =>
* Target.the('username field').located(by.id('username'))
* PageElement.located(By.id('username')).describedAs('username field')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -30,6 +29,24 @@ * Ensure.that(Value.of(usernameField), equals('Alice')),

*
* @example <caption>Using Value as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { By, PageElement, Value } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const usernameField = () =>
* PageElement.located(By.id('username')).describedAs('username field')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* Value.of(usernameField).toLocaleLowerCase()[0],
* equals('a') // [a]lice
* ),
* )
*
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
class Value extends ElementQuestion_1.ElementQuestion {
class Value extends core_1.Question {
/**

@@ -39,19 +56,25 @@ * @param {Answerable<PageElement>} element

constructor(element) {
super((0, io_1.formatted) `the value of ${element}`);
super();
this.element = element;
this.subject = (0, core_1.d) `the value of ${element}`;
}
/**
* @param {Answerable<PageElement>} element
* @returns {Value}
* @desc
* Retrieves the `value` attribute of a given {@link PageElement}.
*
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} element
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
static of(element) {
return new Value(element);
return core_1.Question.createAdapter(new Value(element));
}
/**
* @desc
* Resolves to the value of a given [`input`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
* {@link WebElement}, located in the context of a `parent` element.
* Retrieves the `value` attribute of a given {@link PageElement}.
* located within the `parent` element.
*
* @param {Answerable<PageElement>} parent
* @returns {Question<Promise<string>>}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*

@@ -76,7 +99,25 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

async answeredBy(actor) {
const element = await this.resolve(actor, this.element);
const element = await actor.answer(this.element);
return element.value();
}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject) {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString() {
return this.subject;
}
}
exports.Value = Value;
//# sourceMappingURL=Value.js.map
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

{
"name": "@serenity-js/web",
"version": "3.0.0-rc.13",
"version": "3.0.0-rc.14",
"description": "Serenity/JS Screenplay Pattern APIs for the Web",

@@ -50,4 +50,4 @@ "author": {

"dependencies": {
"@serenity-js/assertions": "3.0.0-rc.13",
"@serenity-js/core": "3.0.0-rc.13",
"@serenity-js/assertions": "3.0.0-rc.14",
"@serenity-js/core": "3.0.0-rc.14",
"tiny-types": "^1.17.0"

@@ -61,6 +61,6 @@ },

"mocha": "^9.1.3",
"ts-node": "^10.5.0",
"typescript": "^4.5.5"
"ts-node": "^10.7.0",
"typescript": "^4.6.2"
},
"gitHead": "9fb7df72c7f33cb10504843db890f7855b8c355a"
"gitHead": "b4aa801512b45db287e10fa44c9ec1d6f08126c3"
}

@@ -8,3 +8,6 @@ import { Expectation } from '@serenity-js/core';

* @desc
* Expectation that the element is present in the DOM of the page and visible.
* Expectation that the element is present in the DOM of the page and:
* - is not hidden, so doesn't have `display: none`, `visibility: hidden` or `opacity: 0`
* - is within the browser viewport
* - doesn't have its centre covered by other elements
*

@@ -11,0 +14,0 @@ * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}

@@ -19,5 +19,4 @@ import { Answerable, d, Optional, Question, QuestionAdapter } from '@serenity-js/core';

// todo: review usages and consider removing if not used
static of<NET>(childElement: Answerable<PageElement<NET>>, parentElement: Answerable<PageElement<NET>>): QuestionAdapter<PageElement<NET>> {
return Question.about(d`${ childElement } of ${ parentElement })`, async actor => {
return Question.about(d`${ childElement } of ${ parentElement }`, async actor => {
const child = await actor.answer(childElement);

@@ -87,3 +86,13 @@ const parent = await actor.answer(parentElement);

abstract isSelected(): Promise<boolean>;
/**
* @desc
* Checks if the PageElement:
* - is not hidden, so doesn't have CSS style like `display: none`, `visibility: hidden` or `opacity: 0`
* - is within the browser viewport
* - doesn't have its centre covered by other elements
*
* @returns {Promise<boolean>}
*/
abstract isVisible(): Promise<boolean>;
}

@@ -1,2 +0,2 @@

import { Answerable, f, List, MetaQuestion, Question } from '@serenity-js/core';
import { Answerable, List, MetaQuestion, Question } from '@serenity-js/core';

@@ -25,3 +25,3 @@ import { BrowseTheWeb } from '../abilities';

return new PageElements<Native_Element_Type>(relativeToParent(this.locator, parent))
.describedAs(`<<${this.toString()}>>` + f`.of(${ parent })`);
.describedAs(`${ this.toString() } of ${ parent }`);
}

@@ -44,3 +44,3 @@ }

function relativeToParent<Native_Element_Type>(relativeLocator: Answerable<Locator<Native_Element_Type>>, parent: Answerable<PageElement<Native_Element_Type>>): Question<Promise<Locator<Native_Element_Type>>> {
return Question.about(relativeLocator.toString() + f`.of${ parent }`, async actor => {
return Question.about(`${ relativeLocator.toString() } of ${ parent }`, async actor => {
const locator: Locator<Native_Element_Type> = await actor.answer(relativeLocator);

@@ -47,0 +47,0 @@ const parentElement: PageElement<Native_Element_Type> = await actor.answer(parent);

@@ -1,10 +0,8 @@

import { Answerable, AnswersQuestions, LogicError, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
import { Answerable, AnswersQuestions, d, LogicError, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
import { PageElement } from '../models';
import { ElementQuestion } from './ElementQuestion';
/**
* @desc
* Returns the value of the given HTML attribute of a given {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>
* Returns the value of the specified HTML attribute of a given {@link PageElement}.
*

@@ -18,33 +16,56 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve a HTML attribute of a given WebElement</caption>
* @example <caption>Retrieve an HTML attribute of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { Attribute, by, BrowseTheWeb, Target } from '@serenity-js/webdriverio';
* import { Attribute, By, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* Target.the('shopping list').located(by.id('shopping-list'))
* PageElement.located(By.id('shopping-list')).describedAs('shopping list');
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(Attribute.called('data-items-left').of(shoppingList()), equals('2')),
* Ensure.that(
* Attribute.called('data-items-left').of(shoppingList()),
* equals('2')
* ),
* )
*
* @example <caption>Find WebElements with a given attribute</caption>
* @example <caption>Using Attribute as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { Attribute, By, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* Attribute.called('id').of(shoppingList()).toLocaleUpperCase(),
* equals('SHOPPING-LIST')
* ),
* )
*
* @example <caption>Find PageElements with a given attribute</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, includes } from '@serenity-js/assertions';
* import { Attribute, BrowseTheWeb, by, Target } from '@serenity-js/webdriverio';
* import { Attribute, By, PageElements } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* class ShoppingList {
* static items = () =>
* Target.all('items')
* .located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items');
*
* static outstandingItems = () =>
* ShoppingList.items
* .where(Attribute.called('data-state'), includes('buy'))
* ShoppingList.items()
* .where(Attribute.called('data-state'), includes('buy'));
* }
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -57,10 +78,15 @@ * Ensure.that(

*
* @extends {ElementQuestion}
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
export class Attribute
extends ElementQuestion<Promise<string>>
extends Question<Promise<string>>
implements MetaQuestion<Answerable<PageElement>, Promise<string>>
{
/**
* @private
*/
private subject: string;
/**
* @param {Answerable<string>} name

@@ -77,7 +103,10 @@ * @returns {Attribute}

*/
constructor(
protected constructor(
private readonly name: Answerable<string>,
private readonly element?: Answerable<PageElement>,
) {
super(`"${ name }" attribute of ${ element }`);
super();
this.subject = element
? d`${ name } attribute of ${ element }`
: d`${ name } attribute`
}

@@ -87,30 +116,52 @@

* @desc
* Resolves to the value of a HTML attribute of the `target` element,
* located in the context of a `parent` element.
* Resolves to the value of an HTML attribute of the `target` element,
* located within the `parent` element.
*
* @param {Answerable<PageElement>} parent
* @returns {Question<Promise<string[]>>}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*
* @see {@link Target.all}
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
of(parent: Answerable<PageElement>): Question<Promise<string>> {
return new Attribute(
this.name,
this.element
? PageElement.of(this.element, parent)
: parent
);
of(parent: Answerable<PageElement>): QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>> {
return Question.createAdapter(
new Attribute(
this.name,
this.element
? PageElement.of(this.element, parent)
: parent
)
) as QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>>;
}
async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string> {
const name = await actor.answer(this.name);
if (! this.element) {
throw new LogicError(`Target not specified`); // todo: better error message?
throw new LogicError(d`Couldn't read attribute ${ name } of an unspecified page element.`);
}
const element = await actor.answer(this.element);
const name = await actor.answer(this.name);
return element.attribute(name);
}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string {
return this.subject;
}
}

@@ -1,6 +0,4 @@

import { Answerable, AnswersQuestions, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
import { formatted } from '@serenity-js/core/lib/io';
import { Answerable, AnswersQuestions, d, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
import { PageElement } from '../models';
import { ElementQuestion } from './ElementQuestion';

@@ -10,3 +8,3 @@ /**

* Resolves to an array of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
* of a given {@link WebElement}, represented by Answerable<{@link @wdio/types~Element}>.
* of a given {@link PageElement}.
*

@@ -20,28 +18,55 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve CSS classes of a given WebElement</caption>
* @example <caption>Retrieve CSS classes of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* Target.the('shopping list').located(by.id('shopping-list'))
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(CssClasses.of(shoppingList()), equals([ 'active', 'favourite' ])),
* Ensure.that(
* CssClasses.of(shoppingList()),
* equals([ 'active', 'favourite' ])
* ),
* )
*
* @example <caption>Find WebElements with a given class</caption>
* @example <caption>Using CssClasses as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list')).describedAs('shopping list')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* CssClasses.of(shoppingList()).length,
* equals(2)
* ),
* Ensure.that(
* CssClasses.of(shoppingList())[0],
* equals('active')
* ),
* )
*
* @example <caption>Find PageElements with a given class</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, contain } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* class ShoppingList {
* static items = () =>
* Target.all('items')
* .located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items')
*
* static outstandingItems = () =>
* ShoppingList.items
* ShoppingList.items()
* .where(CssClasses, contain('buy'))

@@ -51,3 +76,3 @@ * }

* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -60,22 +85,30 @@ * Ensure.that(

*
* @extends {ElementQuestion}
* @extends {@serenity-js/core/lib/screenplay~Question}
* @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
export class CssClasses
extends ElementQuestion<Promise<string[]>>
extends Question<Promise<string[]>>
implements MetaQuestion<Answerable<PageElement>, Promise<string[]>>
{
/**
* @param {Question<PageElement> | PageElement} target
* @returns {CssClasses}
* @private
*/
static of(target: Answerable<PageElement>): CssClasses {
return new CssClasses(target);
private subject: string;
/**
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string[]>}
*
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
static of(pageElement: Answerable<PageElement>): QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>> {
return Question.createAdapter(new CssClasses(pageElement)) as QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>;
}
/**
* @param {Question<PageElement> | PageElement} target
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} pageElement
*/
constructor(private readonly target: Answerable<PageElement>) {
super(formatted `CSS classes of ${ target}`);
protected constructor(private readonly pageElement: Answerable<PageElement>) {
super();
this.subject = d`CSS classes of ${ pageElement}`;
}

@@ -85,13 +118,12 @@

* @desc
* Resolves to an array of CSS classes of the `target` element,
* located in the context of a `parent` element.
* Resolves to an array of CSS classes of the `pageElement`,
* located within the `parent` element.
*
* @param {@serenity-js/core/lib/screenplay~Answerable<Element>} parent
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {Question<Promise<string[]>>}
*
* @see {@link Target.all}
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
of(parent: Answerable<PageElement>): Question<Promise<string[]>> {
return new CssClasses(PageElement.of(this.target, parent));
return new CssClasses(PageElement.of(this.pageElement, parent));
}

@@ -112,3 +144,3 @@

async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string[]> {
const element = await this.resolve(actor, this.target);
const element = await actor.answer(this.pageElement);

@@ -124,2 +156,22 @@ return element.attribute('class')

}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string {
return this.subject;
}
}

@@ -1,2 +0,2 @@

import { Answerable, Question } from '@serenity-js/core';
import { Answerable, List, Question } from '@serenity-js/core';
import { formatted } from '@serenity-js/core/lib/io';

@@ -102,11 +102,11 @@

*
* @returns {Question<Promise<string[]>>}
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
*
* @see {@link Select.values}
*/
static valuesOf(pageElement: Answerable<PageElement>): Question<Promise<string[]>> {
static valuesOf(pageElement: Answerable<PageElement>): List<string> {
return PageElements.located(By.css('option:checked'))
.of(pageElement)
.eachMappedTo(Value)
.describedAs(formatted `values selected in ${ pageElement }`) as Question<Promise<string[]>>;
.describedAs(formatted `values selected in ${ pageElement }`);
}

@@ -205,12 +205,12 @@

*
* @returns {Question<Promise<string[]>>}
* @returns {@serenity-js/core/lib/screenplay/questions~List<string>}
*
* @see {@link Select.options}
*/
static optionsIn(pageElement: Answerable<PageElement>): Question<Promise<string[]>> {
static optionsIn(pageElement: Answerable<PageElement>): List<string> {
return PageElements.located(By.css('option:checked'))
.of(pageElement)
.eachMappedTo(Text)
.describedAs(formatted `options selected in ${ pageElement }`) as Question<Promise<string[]>>;
.describedAs(formatted `options selected in ${ pageElement }`);
}
}

@@ -5,3 +5,2 @@ import { Answerable, AnswersQuestions, d, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';

import { PageElement, PageElements } from '../models';
import { ElementQuestion } from './ElementQuestion';

@@ -11,4 +10,4 @@ /**

* Resolves to the visible (i.e. not hidden by CSS) `innerText` of:
* - a given {@link WebElement}, represented by Answerable<{@link @wdio/types~Element}>
* - a group of {@link WebElement}s, represented by Answerable<{@link @wdio/types~ElementList}>
* - a given {@link PageElement}
* - a group of {@link PageElements}
*

@@ -28,9 +27,11 @@ * The result includes the visible text of any sub-elements, without any leading or trailing whitespace.

* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Target, Text } from '@serenity-js/webdriverio';
* import { By, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const header = () =>
* Target.the('header').located(by.tagName('h1'))
* PageElement.located(By.css('h1'))
* .describedAs('header')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -43,9 +44,11 @@ * Ensure.that(Text.of(header()), equals('Shopping list')),

* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Target, Text } from '@serenity-js/webdriverio';
* import { By, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingListItems = () =>
* Target.the('shopping list items').located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('shopping list items')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -61,6 +64,8 @@ * Ensure.that(

* import { contain, Ensure } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, CssClasses, Target, Text } from '@serenity-js/webdriverio';
* import { By, CssClasses, PageElement, Text } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const shoppingListItemCalled = (name: string) =>
* Target.the('shopping list items').located(by.css('#shopping-list li'))
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('shopping list items')
* .where(Text, equals(name))

@@ -70,3 +75,3 @@ * .first()

* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -86,7 +91,6 @@ * Ensure.that(

* @desc
* Retrieves text of a single {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>.
* Retrieves text of a single {@link PageElement}.
*
* @param {Answerable<PageElement>} element
* @returns {Question<Promise<string>> & MetaQuestion<Answerable<PageElement>, Promise<string>>}
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*

@@ -104,7 +108,6 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

* @desc
* Retrieves text of a group of {@link WebElement}s,
* represented by Answerable<{@link @wdio/types~ElementList}>
* Retrieves text of a group of {@link PageElements}.
*
* @param {Answerable<PageElement[]>} elements
* @returns {Question<Promise<string[]>> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>}
* @param {Answerable<PageElements | PageElement[]>} elements
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string[]>}
*

@@ -129,5 +132,10 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

class TextOfSingleElement
extends ElementQuestion<Promise<string>>
extends Question<Promise<string>>
implements MetaQuestion<Answerable<PageElement>, Promise<string>>
{
/**
* @private
*/
private subject: string;
static of(element: Answerable<PageElement>): QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>> {

@@ -137,4 +145,5 @@ return Question.createAdapter(new TextOfSingleElement(element)) as QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>>;

constructor(private readonly element: Answerable<PageElement>) {
super(`the text of ${ element }`);
protected constructor(private readonly element: Answerable<PageElement>) {
super();
this.subject = d`the text of ${ element }`;
}

@@ -151,8 +160,33 @@

}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string {
return this.subject;
}
}
class TextOfMultipleElements
extends ElementQuestion<Promise<string[]>>
extends Question<Promise<string[]>>
implements MetaQuestion<Answerable<PageElement>, Promise<string[]>>
{
/**
* @private
*/
private subject: string;
static of(elements: PageElements): QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>> {

@@ -162,4 +196,5 @@ return Question.createAdapter(new TextOfMultipleElements(elements)) as QuestionAdapter<string[]> & MetaQuestion<Answerable<PageElement>, Promise<string[]>>;

constructor(private readonly elements: PageElements) {
super(d`the text of ${ elements }`);
protected constructor(private readonly elements: PageElements) {
super();
this.subject = d`the text of ${ elements }`;
}

@@ -176,2 +211,22 @@

}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string {
return this.subject;
}
}

@@ -1,11 +0,8 @@

import { Answerable, AnswersQuestions, MetaQuestion, Question, UsesAbilities } from '@serenity-js/core';
import { formatted } from '@serenity-js/core/lib/io';
import { Answerable, AnswersQuestions, d, MetaQuestion, Question, QuestionAdapter, UsesAbilities } from '@serenity-js/core';
import { PageElement } from '../models';
import { ElementQuestion } from './ElementQuestion';
/**
* @desc
* Returns the `value` attribute of a given {@link WebElement},
* represented by Answerable<{@link @wdio/types~Element}>
* Retrieves the `value` attribute of a given {@link PageElement}.
*

@@ -15,12 +12,13 @@ * @example <caption>Example widget</caption>

*
* @example <caption>Retrieve CSS classes of a given WebElement</caption>
* @example <caption>Retrieve the `value` of a given PageElement</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { BrowseTheWeb, by, Value, Target } from '@serenity-js/webdriverio';
* import { By, PageElement, Value } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const usernameField = () =>
* Target.the('username field').located(by.id('username'))
* PageElement.located(By.id('username')).describedAs('username field')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWeb.using(browser))
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(

@@ -30,2 +28,20 @@ * Ensure.that(Value.of(usernameField), equals('Alice')),

*
* @example <caption>Using Value as QuestionAdapter</caption>
* import { actorCalled } from '@serenity-js/core';
* import { Ensure, equals } from '@serenity-js/assertions';
* import { By, PageElement, Value } from '@serenity-js/web';
* import { BrowseTheWebWithWebdriverIO } from '@serenity-js/webdriverio';
*
* const usernameField = () =>
* PageElement.located(By.id('username')).describedAs('username field')
*
* actorCalled('Lisa')
* .whoCan(BrowseTheWebWithWebdriverIO.using(browser))
* .attemptsTo(
* Ensure.that(
* Value.of(usernameField).toLocaleLowerCase()[0],
* equals('a') // [a]lice
* ),
* )
*
* @extends {@serenity-js/core/lib/screenplay~Question}

@@ -35,11 +51,21 @@ * @implements {@serenity-js/core/lib/screenplay/questions~MetaQuestion}

export class Value
extends ElementQuestion<Promise<string>>
extends Question<Promise<string>>
implements MetaQuestion<Answerable<PageElement>, Promise<string>>
{
/**
* @param {Answerable<PageElement>} element
* @returns {Value}
* @private
*/
static of(element: Answerable<PageElement>): Question<Promise<string>> & MetaQuestion<Answerable<PageElement>, Promise<string>> {
return new Value(element);
private subject: string;
/**
* @desc
* Retrieves the `value` attribute of a given {@link PageElement}.
*
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} element
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*
* @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}
*/
static of(element: Answerable<PageElement>): QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>> {
return Question.createAdapter(new Value(element)) as QuestionAdapter<string> & MetaQuestion<Answerable<PageElement>, Promise<string>>;
}

@@ -51,3 +77,4 @@

constructor(private readonly element: Answerable<PageElement>) {
super(formatted`the value of ${ element }`);
super();
this.subject = d`the value of ${ element }`;
}

@@ -57,7 +84,7 @@

* @desc
* Resolves to the value of a given [`input`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
* {@link WebElement}, located in the context of a `parent` element.
* Retrieves the `value` attribute of a given {@link PageElement}.
* located within the `parent` element.
*
* @param {Answerable<PageElement>} parent
* @returns {Question<Promise<string>>}
* @param {@serenity-js/core/lib/screenplay~Answerable<PageElement>} parent
* @returns {@serenity-js/core/lib/screenplay~QuestionAdapter<string>}
*

@@ -83,6 +110,26 @@ * @see {@link @serenity-js/core/lib/screenplay/questions~MetaQuestion}

async answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<string> {
const element = await this.resolve(actor, this.element);
const element = await actor.answer(this.element);
return element.value();
}
/**
* @desc
* Changes the description of this question's subject.
*
* @param {string} subject
* @returns {Question<T>}
*/
describedAs(subject: string): this {
this.subject = subject;
return this;
}
/**
* @returns {string}
* Returns a human-readable representation of this {@link @serenity-js/core/lib/screenplay~Question}.
*/
toString(): string {
return this.subject;
}
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc