/
CssClasses.ts
130 lines (126 loc) · 3.83 KB
/
CssClasses.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import type { Answerable,MetaQuestionAdapter, QuestionAdapter } from '@serenity-js/core';
import { d, Question } from '@serenity-js/core';
import { PageElement } from '../models';
/**
* Uses the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
* a list of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
* of a given {@apilink PageElement}.
*
* ## Example widget
*
* ```html
* <ul id="shopping-list" class="active favourite">
* <li class="bought">Coffee<li>
* <li class="buy">Honey<li>
* <li class="buy">Chocolate<li>
* </ul>
* ```
*
* ## Retrieve CSS classes of a given {@apilink PageElement}
*
* ```ts
* import { actorCalled } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { By, CssClasses, PageElement } from '@serenity-js/web'
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list'))
* .describedAs('shopping list')
*
* await actorCalled('Lisa')
* .attemptsTo(
* Ensure.that(
* CssClasses.of(shoppingList()),
* equals([ 'active', 'favourite' ])
* ),
* )
* ```
*
* ## Using CssClasses as {@apilink QuestionAdapter}
*
* ```ts
* import { actorCalled } from '@serenity-js/core'
* import { Ensure, equals } from '@serenity-js/assertions'
* import { By, CssClasses, PageElement } from '@serenity-js/web'
*
* const shoppingList = () =>
* PageElement.located(By.css('#shopping-list'))
* .describedAs('shopping list')
*
* await actorCalled('Lisa')
* .attemptsTo(
* Ensure.that(
* CssClasses.of(shoppingList()).length,
* equals(2)
* ),
* Ensure.that(
* CssClasses.of(shoppingList())[0],
* equals('active')
* ),
* )
* ```
*
* ## Using as filter in {@apilink PageElements|Page Element Query Language}
*
* ```ts
* import { actorCalled } from '@serenity-js/core'
* import { Ensure, contain } from '@serenity-js/assertions'
* import { By, CssClasses, PageElement } from '@serenity-js/web'
*
* class ShoppingList {
* static items = () =>
* PageElements.located(By.css('#shopping-list li'))
* .describedAs('items')
*
* static outstandingItems = () =>
* ShoppingList.items()
* .where(CssClasses, contain('buy'))
* }
*
* await actorCalled('Lisa')
* .attemptsTo(
* Ensure.that(
* Text.ofAll(ShoppingList.outstandingItems()),
* equals([ 'Honey', 'Chocolate' ])
* ),
* )
* ```
*
* ## Learn more
* - {@apilink BrowseTheWeb}
* - {@apilink MetaQuestion}
* - {@apilink QuestionAdapter}
* - {@apilink Question}
*
* @group Questions
*/
export class CssClasses {
/**
* Instantiates a {@apilink Question} that uses
* the {@apilink Actor|actor's} {@apilink Ability|ability} to {@apilink BrowseTheWeb} to retrieve
* a list of [CSS classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-class)
* of a given {@apilink PageElement}.
*
* #### Learn more
* - {@apilink MetaQuestion}
*
* @param pageElement
*/
static of(pageElement: QuestionAdapter<PageElement> | PageElement): MetaQuestionAdapter<PageElement, string[]> {
return Question.about(d`CSS classes of ${ pageElement }`,
async actor => {
const element = await actor.answer(pageElement);
return element.attribute('class')
.then(attribute => attribute ?? '')
.then(attribute => attribute
.replace(/\s+/, ' ')
.trim()
.split(' ')
.filter(cssClass => !! cssClass),
);
},
(parent: Answerable<PageElement>) =>
CssClasses.of(PageElement.of(pageElement, parent))
);
}
}