@serenity-js/core
Version:
The core Serenity/JS framework, providing the Screenplay Pattern interfaces, as well as the test reporting and integration infrastructure
534 lines • 20.8 kB
TypeScript
import type { Answerable } from '../Answerable';
import type { QuestionAdapter } from '../Question';
import type { MetaQuestion } from './MetaQuestion';
/**
* Provides methods to perform calculations on numeric values returned by other [questions](https://serenity-js.org/api/core/class/Question/).
*
* ## Example
*
* The examples in this section demonstrate interacting with an HTML widget representing a price list.
*
* ```html
* <ul id="price-list">
* <li class="item">
* <span class="name">apples</span>
* <span class="quantity">5</span>
* <span class="price">£2.25</span>
* </li>
* <li class="item">
* <span class="name">apricots</span>
* <span class="quantity">4</span>
* <span class="price">£3.70</span>
* </li>
* <li class="item">
* <span class="name">bananas</span>
* <span class="quantity">2</span>
* <span class="price">£1.50</span>
* </li>
* </ul>
* ```
*
* ### Lean Page Objects
*
* To represent our example HTML widget,
* we follow the [Lean Page Objects pattern](https://serenity-js.org/handbook/web-testing/page-objects-pattern/)
* to define the `PriceList` and `PriceListItem` classes
* and use the Serenity/JS [Page Element Query Language (PEQL)](https://serenity-js.org/handbook/web-testing/page-element-query-language/)
* to identify the elements of interest.
*
* ```ts
* // ui/price-list.ts
*
* import { PageElement, PageElements, By } from '@serenity-js/web'
*
* export class PriceList {
* static container = () =>
* PageElement.located(By.id('price-list'))
* .describedAs('price list')
*
* static items = () =>
* PageElements.located(PriceListItem.containerSelector())
* .of(this.container())
* .describedAs('items')
* }
*
* export class PriceListItem {
* static containerSelector = () =>
* By.css('.item')
*
* static container = () =>
* PageElement.located(this.containerSelector())
* .describedAs('item')
*
* static name = () =>
* PageElement.located(By.css('.name'))
* .of(this.container())
* .describedAs('name')
*
* static quantity = () =>
* PageElement.located(By.css('.quantity'))
* .of(this.container())
* .describedAs('quantity')
*
* static price = () =>
* PageElement.located(By.css('.price'))
* .of(this.container())
* .describedAs('price')
* }
* ```
*
* @group Questions
*/
export declare class Numeric {
/**
* Returns a [`Question`](https://serenity-js.org/api/core/class/Question/) that sums up the values provided
* and throws if any of the values is not a `number`.
*
* #### Adding static numbers
*
* The most basic example of using the `Numeric.sum` method is to add up a few numbers.
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum(1, 2, 3),
* equals(6),
* )
* )
* ```
*
* The numbers can be provided individually, as an array, or as a combination of both.
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum([ 1, 2 ], 3, [ 4, 5 ]),
* equals(15),
* )
* )
* ```
*
* #### Adding numbers returned by other questions
*
* Apart from adding static numbers, you can also add numbers returned by other questions.
* This is particularly useful when you need to calculate the sum of numbers extracted from a list of UI elements
* or from an API response.
*
* ```ts
* import { actorCalled, Numeric, Question } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
*
* const myNumber = () =>
* Question.about('my number', actor => 42)
*
* const myArrayOfNumbers = () =>
* Question.about('my array of numbers', actor => [ 1, 2, 3 ])
*
* const myObjectWithNumbers = () =>
* Question.about('my object with numbers', actor => ({ a: 1, b: 2, c: 3 }))
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum(
* myNumber(), // a question returning a number
* myArrayOfNumbers(), // a question returning an array of numbers
* ),
* equals(48),
* ),
* Ensure.that(
* Numeric.sum(
* myObjectWithNumbers() // a question returning an object with numbers
* .as(Object.values), // from which we extract the numeric values
* ),
* equals(6),
* ),
* )
* ```
*
* Of course, you can also mix and match static numbers with numbers returned by questions.
*
* ```ts
* import { actorCalled, Numeric, Question } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
*
* const myObjectWithNumbers = () =>
* Question.about('my object with numbers', actor => ({ a: 1, b: 2, c: 3 }))
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum(
* myObjectWithNumbers().as(Object.values),
* [ 4, 5 ],
* 6,
* ),
* equals(21),
* ),
* )
* ```
*
* #### Adding numbers extracted from a UI
*
* To add numbers extracted from a UI:
* - use the [`PageElement`](https://serenity-js.org/api/web/class/PageElement) and [`PageElements`](https://serenity-js.org/api/web/class/PageElements) classes to identify the elements of interest,
* - use the [`Text.of`](https://serenity-js.org/api/web/class/Text/) or [`Text.ofAll`](https://serenity-js.org/api/web/class/Text/) questions to extract the text of the element or elements,
* - and then interpret this text as number using either the [`.as(Number)`](https://serenity-js.org/api/core/class/Question/#as) mapping function,
* or the [`Numeric.intValue()`](https://serenity-js.org/api/core/class/Numeric/#intValue) or [`Numeric.floatValue()`](https://serenity-js.org/api/core/class/Numeric/#floatValue) meta-questions.
*
* For example, we could calculate the sum of quantities of items in our example price list by specifying each element explicitly
* and mapping its text to [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number):
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum(
* Text.of(PriceListItem.quantity().of(PriceList.items().first())).as(Number),
* Text.of(PriceListItem.quantity().of(PriceList.items().at(1))).as(Number),
* Text.of(PriceListItem.quantity().of(PriceList.items().last())).as(Number),
* ),
* equals(11),
* ),
* )
* ```
*
* A more elegant approach would be to use the Serenity/JS [Page Element Query Language (PEQL)](https://serenity-js.org/handbook/web-testing/page-element-query-language/#mapping-page-elements-in-a-collection)
* to map each item in the collection to its quantity and then calculate the sum.
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum(
* PriceList.items() // get all the li.item elements
* .eachMappedTo(
* Text.of(PriceListItem.quantity()) // extract the text of the .quantity element
* )
* .eachMappedTo( // interpret the quantity as an integer value
* Numeric.intValue(),
* ),
* ),
* equals(11), // 5 apples, 4 apricots, 2 bananas
* )
* )
* ```
*
* Using PEQL allows us to express the intent more concisely and, for example,
* introduce helper functions that limit the scope of the operation to a subset of elements.
*
* ```ts
* import { actorCalled, Expectation, Numeric, the } from '@serenity-js/core'
* import { Ensure, equals, startsWith } from '@serenity-js/assertions'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* const quantitiesOfItemsWhichName = (expectation: Expectation<string>) =>
* PriceList.items() // get all the li.item elements
* .where( // leave only those which name matches the expectation
* Text.of(PriceListItem.name()),
* expectation
* )
* .eachMappedTo(
* Text.of(PriceListItem.quantity()) // extract the text of the .quantity element
* )
* .eachMappedTo( // interpret the quantity as an integer value
* Numeric.intValue(),
* )
* .describedAs(the`quantities of items which name does ${ expectation }`)
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.sum(
* quantitiesOfItemsWhichName(startsWith('ap')), // apples and apricots
* ),
* equals(9), // 5 apples, 4 apricots
* )
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*
* @param values
*/
static sum(...values: Array<Answerable<number | number[]>>): QuestionAdapter<number>;
/**
* Returns a [`Question`](https://serenity-js.org/api/core/class/Question/) that calculates the difference between
* two numbers and throws if any of the values is not a `number`.
*
* #### Subtracting numbers
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.difference(
* Text.of(PriceListItem.quantity().of(PriceList.items().first())).as(Number), // 5 (apples)
* 2, // - 2
* ),
* equals(3),
* ),
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*
* @param minuend
* @param subtrahend
*/
static difference(minuend: Answerable<number>, subtrahend: Answerable<number>): QuestionAdapter<number>;
/**
* Returns a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that calculates
* the ceiling of a number and throws if the value is not a `number`.
*
* #### Calculating the ceiling of a number
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.ceiling().of(
* Text.of(PriceListItem.price().of(PriceList.items().first())) // '£2.25' (apples)
* .replace('£', '') // '2.25'
* .as(Number.parseFloat), // 2.25
* ),
* equals(3),
* ),
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*/
static ceiling(): MetaQuestion<number, QuestionAdapter<number>>;
/**
* Returns a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that calculates
* the floor of a number and throws if the value is not a `number`.
*
* #### Calculating the floor of a number
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.floor().of(
* Text.of(PriceListItem.price().of(PriceList.items().first())) // '£2.25' (apples)
* .replace('£', '') // '2.25'
* .as(Number.parseFloat), // 2.25
* ),
* equals(2),
* ),
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*/
static floor(): MetaQuestion<number, QuestionAdapter<number>>;
/**
* Returns a [`Question`](https://serenity-js.org/api/core/class/Question/) that calculates
* the maximum value in the lists of numbers provided and throws if any of the values is not a `number`.
*
* #### Calculating the maximum value
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.max(
* PriceList.items() // get all the li.item elements
* .eachMappedTo(
* Text.of(PriceListItem.quantity()) // extract the text of the .quantity element
* )
* .eachMappedTo( // interpret the quantity as an integer value
* Numeric.intValue(),
* ),
* ),
* equals(5), // 5 (apples)
* )
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*
* @param values
*/
static max(...values: Array<Answerable<number | number[]>>): QuestionAdapter<number>;
/**
* Returns a [`Question`](https://serenity-js.org/api/core/class/Question/) that calculates
* the minimum value in the lists of numbers provided and throws if any of the values is not a `number`.
*
* #### Calculating the minimum value
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.min(
* PriceList.items() // get all the li.item elements
* .eachMappedTo(
* Text.of(PriceListItem.quantity()) // extract the text of the .quantity element
* )
* .eachMappedTo( // interpret the quantity as an integer value
* Numeric.intValue(),
* ),
* ),
* equals(2), // 2 (bananas)
* )
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*
* @param values
*/
static min(...values: Array<Answerable<number | number[]>>): QuestionAdapter<number>;
/**
* Returns a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that parses a string `value`
* and returns an integer of the specified `base`.
* Leading whitespace in the value to parse argument is ignored.
*
* #### Parsing a string as an integer
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.intValue().of(
* Text.of( // '5' (apples)
* PriceListItem.quantity().of(
* PriceList.items().first()
* )
* )
* ),
* equals(5),
* ),
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*
* @param base
* An integer between 2 and 36 that represents the base in mathematical numeral systems of the string.
* If base is undefined or 0, it is assumed to be 10 except when the number begins with the code unit pairs 0x or 0X, in which case a radix of 16 is assumed.
*/
static intValue(base?: Answerable<number>): MetaQuestion<string, QuestionAdapter<number>>;
/**
* Returns a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that parses a string `value`
* and returns a [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
* Leading whitespace in the value to parse argument is ignored.
*
* #### Parsing a string as a bigint
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.bigIntValue().of(
* Text.of( // '5' (apples)
* PriceListItem.quantity().of(PriceList.items().first())
* )
* ),
* equals(BigInt('5')),
* ),
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*/
static bigIntValue(): MetaQuestion<string, QuestionAdapter<bigint>>;
/**
* Returns a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that parses a string `value`
* and returns a floating-point number.
*
* #### Parsing a string as a floating point number
*
* ```ts
* import { actorCalled, Numeric } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { Text } from '@serenity-js/web'
* import { PriceList, PriceListItem } from './ui/price-list'
*
* await actorCalled('Zoé').attemptsTo(
* Ensure.that(
* Numeric.floatValue().of(
* Text.of( // '£2.25'
* PriceListItem.price().of(PriceList.items().first())
* ).replace('£', '') // '2.25'
* ),
* equals(2.25),
* ),
* )
* ```
*
* #### Learn more
*
* View the [`Numeric` API documentation](https://serenity-js.org/api/core/class/Numeric) for more details
* and examples.
*/
static floatValue(): MetaQuestion<string, QuestionAdapter<number>>;
private static flatten;
private static descriptionOf;
}
//# sourceMappingURL=Numeric.d.ts.map