-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathtabs.go
More file actions
237 lines (198 loc) · 7.35 KB
/
Copy pathtabs.go
File metadata and controls
237 lines (198 loc) · 7.35 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
package biloba
import (
"fmt"
"strings"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
/*
Tabs represents a slice of Biloba tabs
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
type Tabs []*Biloba
/*
Find returns the first Biloba tab matching the passed-in TabQuery (see [Biloba.TabMatching]), or nil if none match:
tab := b.AllTabs().Find(b.TabMatching().WithTitle("Dashboard"))
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (t Tabs) Find(query *TabQuery) *Biloba {
for _, tab := range t {
if query.matches(tab) {
return tab
}
}
return nil
}
/*
Filter returns all tabs matching the passed-in TabQuery (see [Biloba.TabMatching])
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (t Tabs) Filter(query *TabQuery) Tabs {
out := Tabs{}
for _, tab := range t {
if query.matches(tab) {
out = append(out, tab)
}
}
return out
}
/*
TabQuery is a chainable query over tabs. A single value plays two roles:
- a Gomega matcher you assert against a tab - read it as [Biloba.HaveSpawnedTab] (searches the tabs this tab spawned) or [Biloba.HaveTab] (searches every tab on the connection), and
- a predicate you pass to [Tabs.Find] / [Tabs.Filter] - read it as [Biloba.TabMatching] (does this one tab match?).
A tab has no single primary key, so all of its dimensions are refinements: chain WithTitle, WithURL, and WithDOMElement. Every refinement applies to the same tab.
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
type TabQuery struct {
b *Biloba
scopeSpawned bool
titleMatcher types.GomegaMatcher
urlMatcher types.GomegaMatcher
hasDOMElement bool
domSelector any
observed Tabs
}
/*
TabMatching() returns a [TabQuery]. Refine it with WithTitle/WithURL/WithDOMElement. Use this spelling when the query reads as a predicate - i.e. when handing it to [Tabs.Find] / [Tabs.Filter]:
tab := b.AllSpawnedTabs().Find(b.TabMatching().WithURL(ContainSubstring("dom.html")))
When you're asserting against a tab, the [Biloba.HaveSpawnedTab] / [Biloba.HaveTab] aliases read more naturally. They return the same query (HaveSpawnedTab additionally narrows the searched population to spawned tabs).
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (b *Biloba) TabMatching() *TabQuery {
return &TabQuery{b: b}
}
/*
HaveSpawnedTab() returns a [TabQuery] whose matcher searches the tabs spawned by this tab (tabs the browser opened in response to user/JavaScript interaction). Refine it and poll to wait for a tab to open:
b.Click("a[target='_blank']")
Eventually(b).Should(b.HaveSpawnedTab().WithTitle("hello there"))
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (b *Biloba) HaveSpawnedTab() *TabQuery {
return &TabQuery{b: b, scopeSpawned: true}
}
/*
HaveTab() returns a [TabQuery] whose matcher searches every tab associated with this tab's Chrome connection - not just tabs this tab spawned. You generally use this on the reusable root tab, but it is allowed on any tab; the result is the same.
Eventually(b).Should(b.HaveTab().WithTitle("Dashboard"))
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (b *Biloba) HaveTab() *TabQuery {
return &TabQuery{b: b}
}
/*
WithTitle() refines the [TabQuery] to also require the tab's window title to match. title may be a string (exact match) or a Gomega matcher.
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (q *TabQuery) WithTitle(title any) *TabQuery {
out := *q
out.titleMatcher = matcherOrEqual(title)
return &out
}
/*
WithURL() refines the [TabQuery] to also require the tab's URL ([Biloba.Location]) to match. url may be a string (exact match) or a Gomega matcher.
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (q *TabQuery) WithURL(url any) *TabQuery {
out := *q
out.urlMatcher = matcherOrEqual(url)
return &out
}
/*
WithDOMElement() refines the [TabQuery] to also require the tab to have at least one element matching selector.
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
Read https://onsi.github.io/biloba/#working-with-the-dom to learn more about selectors and handling the DOM
*/
func (q *TabQuery) WithDOMElement(selector any) *TabQuery {
out := *q
out.hasDOMElement = true
out.domSelector = selector
return &out
}
// matches is the predicate role: does this single tab satisfy every constraint?
func (q *TabQuery) matches(tab *Biloba) bool {
if q.titleMatcher != nil {
title, _ := tab.title()
if match, _ := q.titleMatcher.Match(title); !match {
return false
}
}
if q.urlMatcher != nil {
location, _ := tab.location()
if match, _ := q.urlMatcher.Match(location); !match {
return false
}
}
if q.hasDOMElement && !tab.HasElement(q.domSelector) {
return false
}
return true
}
// Match is the Gomega matcher role: does the searched population have any tab that matches?
func (q *TabQuery) Match(actual any) (bool, error) {
if _, ok := actual.(*Biloba); !ok {
return false, fmt.Errorf("HaveTab/HaveSpawnedTab must be passed a Biloba tab. Got:\n%s", format.Object(actual, 1))
}
if q.scopeSpawned {
q.observed = q.b.AllSpawnedTabs()
} else {
q.observed = q.b.AllTabs()
}
return q.observed.Find(q) != nil, nil
}
func (q *TabQuery) scopeNoun() string {
if q.scopeSpawned {
return "spawned tab"
}
return "tab"
}
func (q *TabQuery) description() string {
clauses := []string{}
if q.titleMatcher != nil {
clauses = append(clauses, fmt.Sprintf("Title matching %s", q.titleMatcher.FailureMessage("")))
}
if q.urlMatcher != nil {
clauses = append(clauses, fmt.Sprintf("URL matching %s", q.urlMatcher.FailureMessage("")))
}
if q.hasDOMElement {
clauses = append(clauses, fmt.Sprintf("a DOM element matching %v", q.domSelector))
}
if len(clauses) == 0 {
return "have a " + q.scopeNoun()
}
return normalizeWhitespace("have a " + q.scopeNoun() + " with " + strings.Join(clauses, "\nand "))
}
func (q *TabQuery) presentTabs() string {
if len(q.observed) == 0 {
return "There were no tabs to search."
}
out := &strings.Builder{}
out.WriteString("The tabs that were searched were:")
for _, tab := range q.observed {
title, _ := tab.title()
location, _ := tab.location()
fmt.Fprintf(out, "\n%s (%s)", title, location)
}
return out.String()
}
func (q *TabQuery) FailureMessage(actual any) string {
return fmt.Sprintf("Expected to %s.\n%s", q.description(), q.presentTabs())
}
func (q *TabQuery) NegatedFailureMessage(actual any) string {
return fmt.Sprintf("Expected not to %s, but there was one.", q.description())
}
/*
AllSpawnedTabs() returns all tabs that were spawned by the current tab. Spawned tabs are tabs that were created by the browser in response to some sort of user/javascript interaction. They are not tabs that you create explicitly with NewTab()
Read https://onsi.github.io/biloba/#managing-tabs to learn more about managing tabs
*/
func (b *Biloba) AllSpawnedTabs() Tabs {
b.guardConfig("AllSpawnedTabs")
out := Tabs{}
for _, tab := range b.AllTabs() {
if b.isSiblingTab(tab) {
out = append(out, tab)
}
}
return out
}
func (b *Biloba) isSiblingTab(tab *Biloba) bool {
return tab.browserContextID == b.browserContextID && tab != b
}