import { Answerable , AnswersQuestions , Question } from '@serenity-js/core' ; import { commaSeparated , formatted } from '@serenity-js/core/lib/io' ; import { inspected } from '@serenity-js/core/lib/io/inspected' ; import { Interaction , UsesAbilities } from '@serenity-js/core/lib/screenplay' ; import { by , ElementFinder } from 'protractor' ; import { promise } from 'selenium-webdriver' ; import { promiseOf } from '../../promiseOf' ; import { Value } from '../questions' ; import { withAnswerOf } from '../withAnswerOf' ; import { SelectBuilder } from './SelectBuilder' ; desc Instructs the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to
 *  select an option from a [HTML `<select>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select),
 *  either by its display name, or by value.
 *
 *  see Selected export class Select { desc Instantiates this  {@link @serenity-js/core/lib/screenplay~Interaction} *  with a [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#attr-value)
     *  of a single [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select.
     *
     *  example example example param { string | Answerable < string > } value A value of the [`option` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select
     *
     *  returns { SelectBuilder } see Selected . valueOf see BrowseTheWeb see Target see {@link @serenity-js/assertions~Ensure} see {@link @serenity-js/assertions/lib/expectations~equals} static value ( value ) { return { from : ( target ) : Interaction => new SelectValue ( value , target ) } ; } desc Instantiates this  {@link @serenity-js/core/lib/screenplay~Interaction} *  with [`value`s](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#attr-value)
     *  of multiple [`<option>` elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select.
     *
     *  example example example param { Array < Answerable < string [ ] | string > > } values Values of the [`option` elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select
     *
     *  returns { SelectBuilder } see Selected . valuesOf see BrowseTheWeb see Target see {@link @serenity-js/assertions~Ensure} see {@link @serenity-js/assertions/lib/expectations~equals} static values ( ... values ) { return { from : ( target ) : Interaction => new SelectValues ( values , target ) } ; } desc Instantiates this  {@link @serenity-js/core/lib/screenplay~Interaction} *  with a single [`option`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select.
     *
     *  example example example param { string | Answerable < string > } value Text of the [`option` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select
     *
     *  returns { SelectBuilder } see Selected . optionIn see BrowseTheWeb see Target see {@link @serenity-js/assertions~Ensure} see {@link @serenity-js/assertions/lib/expectations~equals} static option ( value ) { return { from : ( target ) : Interaction => new SelectOption ( value , target ) } ; } desc Instantiates this  {@link @serenity-js/core/lib/screenplay~Interaction} *  with [`option`s](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select.
     *
     *  example example example param { Array < Answerable < string [ ] | string > > } values Text of the [`option` elements  ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     *  for the  {@link @serenity-js/core/lib/screenplay/actor~Actor} to select
     *
     *  returns { SelectBuilder } see Selected . optionsIn see BrowseTheWeb see Target see {@link @serenity-js/assertions~Ensure} see {@link @serenity-js/assertions/lib/expectations~equals} static options ( ... values ) { return { from : ( target ) : Interaction => new SelectOptions ( values , target ) } ; } } package class SelectValue implements Interaction { constructor ( private readonly value , private readonly target ) { } performAs ( actor ) { return actor . answer ( this . value ) . then ( value => withAnswerOf ( actor , this . target , ( element ) => element . element ( by . css ( `option[value=${ value }]` ) ) ) . click ( ) ) ; } toString ( ) { return formatted `#actor selects value ${ this . value } from ${ this . target }` ; } } package class SelectValues implements Interaction { constructor ( private readonly values , private readonly target ) { } performAs ( actor ) { return Promise . all ( this . values . map ( value => actor . answer ( value ) ) ) . then ( flatten ) . then ( values => { const hasRequiredValue = ( option ) => Value . of ( option ) . answeredBy ( actor ) . then ( value => ! ! ~ values . indexOf ( value ) ) ; return promiseOf ( withAnswerOf ( actor , this . target , ( element ) => element . all ( by . css ( 'option' ) ) . filter ( optionsToSelect ( hasRequiredValue ) ) . each ( select ) ) ) ; } ) ; } toString ( ) { return `#actor selects values ${ commaSeparated ( flatten ( this . values ) , inspected ) } from ${ this . target }` ; } } package class SelectOption implements Interaction { constructor ( private readonly value , private readonly target ) { } performAs ( actor ) { return actor . answer ( this . value ) . then ( value => { return promiseOf ( withAnswerOf ( actor , this . target , ( element ) => element . element ( by . cssContainingText ( 'option' , value ) ) ) . click ( ) ) ; } ) ; } toString ( ) { return formatted `#actor selects ${ this . value } from ${ this . target }` ; } } package class SelectOptions implements Interaction { constructor ( private readonly values , private readonly target ) { } performAs ( actor ) { return Promise . all ( this . values . map ( value => actor . answer ( value ) ) ) . then ( flatten ) . then ( values => { const hasRequiredText = ( option ) => option . getText ( ) . then ( value => ! ! ~ values . indexOf ( value ) ) ; return promiseOf ( withAnswerOf ( actor , this . target , ( element ) => element . all ( by . css ( 'option' ) ) . filter ( optionsToSelect ( hasRequiredText ) ) . each ( select ) ) ) ; } ) ; } toString ( ) { return `#actor selects ${ commaSeparated ( flatten ( this . values ) , inspected ) } from ${ this . target }` ; } } package function flatten < T > ( listOfLists ) { return listOfLists . map ( item => [ ] . concat ( item ) ) . reduce ( ( acc , list ) => acc . concat ( list ) , [ ] ) ; } package function optionsToSelect ( criterion ) { return ( option ) => isAlreadySelected ( option ) . then ( alreadySelected => criterion ( option ) . then ( criterionMet => xor ( alreadySelected , criterionMet ) ) ) ; } package function select ( option ) { return option . click ( ) ; } package function isAlreadySelected ( option ) { return option . isSelected ( ) ; } package function xor ( first , second ) { return first !== second ; }