UNPKG

@serenity-js/core

Version:

The core Serenity/JS framework, providing the Screenplay Pattern interfaces, as well as the test reporting and integration infrastructure

162 lines (150 loc) 7.16 kB
import { ErrorSerialiser } from '../../errors'; import { d } from '../../io'; import { stringified } from '../../io/stringified'; import { LogEntry, Name } from '../../model'; import type { UsesAbilities } from '../abilities'; import type { Answerable } from '../Answerable'; import type { Answered } from '../Answered'; import type { CollectsArtifacts } from '../artifacts'; import { Interaction } from '../Interaction'; import type { AnswersQuestions } from '../questions'; import type { DebuggingResult } from './DebuggingResult'; /** * Instructs the [actor](https://serenity-js.org/api/core/class/Actor/) to evaluate and [log](https://serenity-js.org/api/core/class/Log/) the provided [answerable](https://serenity-js.org/api/core/#Answerable) values. * * Since this interaction **accepts a callback function** that receives the evaluated results, * the best way to use it is while **running the test scenario via a Node.js debugger**. * See the links below to learn how to do it in popular IDEs. * * ## Debugging Answerable values * * [`Debug.values`](https://serenity-js.org/api/core/class/Debug/#values) accepts a callback function that receives an array of [`DebuggingResult`](https://serenity-js.org/api/core/interface/DebuggingResult/) objects, * as well as the result of evaluating each provided [answerable](https://serenity-js.org/api/core/#Answerable) with [`Actor.answer`](https://serenity-js.org/api/core/class/Actor/#answer). * * ```typescript * import { actorCalled, Debug } from '@serenity-js/core' * import { Navigate, Page } from '@serenity-js/web' * * // Running the below through the Node.js debugger makes the actor resolve the provided values * // and return them to the debugger function, where you can investigate their contents, * // or inspect any Errors the evaluation has resulted with. * await actorCalled('Debbie').attemptsTo( * Navigate.to('http://example.org'), * Debug.values((results, title, url) => { * // set a breakpoint here to view `results`, `title` and `url` in your IDE debugger * }, Page.current().title(), Page.current().url()), // accepts multiple values * ); * ``` * * ## Accessing Playwright Page * * [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) * provides features that allow for [experimenting with web UI locators](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright#tune-locators) * while the test is paused at breakpoint. * * Since this functionality is specific to [Playwright](https://serenity-js.org/api/playwright), * you can use it by passing [`PlaywrightPage.current().nativePage()`](https://serenity-js.org/api/playwright/class/PlaywrightPage/#current) * to Serenity/JS [`Debug.values`](https://serenity-js.org/api/core/class/Debug/#values). Also make sure to name the evaluated value `page`, as this is the variable name that the Playwright VSCode extension expects. * * ```typescript * import { actorCalled, Debug } from '@serenity-js/core' * import { PlaywrightPage } from '@serenity-js/playwright' * * // Running the below through the Node.js debugger makes the actor resolve the provided values * // and return them to the debugger function, where you can investigate their contents, * // or inspect any Errors the evaluation has resulted with. * await actorCalled('Debbie').attemptsTo( * Navigate.to('http://example.org'), * Debug.values((results, page) => { * // set a breakpoint here to use Playwright locator debugging features * page.locator('.example-css-class') * // note that you can change this selector while having the test paused at breakpoint * }, PlaywrightPage.current().nativePage()), * ); * ``` * * ## Learn more * - [Node.js debugging - Getting started](https://nodejs.org/en/docs/guides/debugging-getting-started/) * - [Node.js debugging in VS Code](https://code.visualstudio.com/docs/nodejs/nodejs-debugging) * - [Running and debugging Node.js in WebStorm](https://www.jetbrains.com/help/webstorm/running-and-debugging-node-js.html) * - [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) * * @group Activities */ export class Debug<Values extends Array<Answerable<unknown>>> extends Interaction { /** * Instructs the [actor](https://serenity-js.org/api/core/class/Actor/) to evaluate the provided `values`, * log the results, and then pass them to your `debuggerFunction`. * * To use this interaction, run your test scenario in the Node.js debugger * and set a breakpoint inside the `debuggerFunction`. * * @param debuggerFunction * @param values */ static values<Values extends Array<Answerable<unknown>>>(debuggerFunction: ( results: { [ Index in keyof Values ]: DebuggingResult<Values[Index]> }, ...answers: { [ Index in keyof Values ]: Answered<Values[Index]> } ) => Promise<void> | void, ...values: Values): Interaction { return new Debug<Values>( `#actor debugs ${ values.length } values`, debuggerFunction, values ); } /** * @param description * Description of this interaction * * @param debuggerFunction * Callback function to receive the results of debugging * * @param values * Value to be evaluated by the actor, and provided to debuggerFunction */ protected constructor( description: string, private readonly debuggerFunction: ( debuggingResults: { [ Index in keyof Values ]: DebuggingResult<Values[Index]> }, ...args: { [ Index in keyof Values ]: Answered<Values[Index]> } ) => Promise<void> | void, private readonly values: Values, ) { super(description); } /** * @inheritDoc */ async performAs(actor: UsesAbilities & AnswersQuestions & CollectsArtifacts): Promise<void> { const results = [] as { [ Index in keyof Values ]: DebuggingResult<Values[Index]> }; for (const value of this.values) { const result: DebuggingResult<unknown> = { description: d`${ value }`, value: undefined, error: undefined, }; try { result.value = await actor.answer(value); } catch (error) { result.error = error; } actor.collect( LogEntry.fromJSON({ data: JSON.stringify({ value: stringified(result.value), error: result.error ? ErrorSerialiser.serialise(result.error) : result.error }), }), new Name(result.description) ); results.push(result); } this.debuggerFunction( results, ...results.map(result => result.value) as { [ Index in keyof Values ]: Answered<Values[Index]> } ); } }