1 package playwright
2
3 import (
4 "errors"
5 "fmt"
6 "regexp"
7 "strconv"
8 )
9
10 type locatorImpl struct {
11 frame *frameImpl
12 selector string
13 options *LocatorLocatorOptions
14 }
15
16 func newLocator(frame *frameImpl, selector string, options ...LocatorLocatorOptions) (*locatorImpl, error) {
17 var option *LocatorLocatorOptions
18 if len(options) == 1 {
19 option = &options[0]
20 if option.HasText != nil {
21 switch hasText := option.HasText.(type) {
22 case *regexp.Regexp:
23 selector += fmt.Sprintf(" >> :scope:text-matches(%s)", convertRegexp(hasText))
24 case string:
25 selector += fmt.Sprintf(" >> :scope:has-text('%s')", hasText)
26 }
27 }
28 if option.Has != nil {
29 has := option.Has.(*locatorImpl)
30 if frame != has.frame {
31 return nil, errors.New("inner 'has' locator must belong to the same frame")
32 }
33 selector += " >> has=" + has.selector
34 }
35 }
36
37 return &locatorImpl{frame: frame, selector: selector, options: option}, nil
38 }
39
40 func convertRegexp(reg *regexp.Regexp) string {
41 matches := regexp.MustCompile(`\(\?([imsU]+)\)(.+)`).FindStringSubmatch(reg.String())
42
43 var pattern, flags string
44 if len(matches) == 3 {
45 pattern = matches[2]
46 flags = matches[1]
47 } else {
48 pattern = reg.String()
49 }
50 return fmt.Sprintf("'%s', '%s'", pattern, flags)
51 }
52
53 func (l *locatorImpl) AllInnerTexts() ([]string, error) {
54 innerTexts, err := l.frame.EvalOnSelectorAll(l.selector, "ee => ee.map(e => e.innerText)")
55 if err != nil {
56 return nil, err
57 }
58 texts := innerTexts.([]interface{})
59 result := make([]string, len(texts))
60 for i := range texts {
61 result[i] = texts[i].(string)
62 }
63 return result, nil
64 }
65
66 func (l *locatorImpl) AllTextContents() ([]string, error) {
67 textContents, err := l.frame.EvalOnSelectorAll(l.selector, "ee => ee.map(e => e.textContent || '')")
68 if err != nil {
69 return nil, err
70 }
71 texts := textContents.([]interface{})
72 result := make([]string, len(texts))
73 for i := range texts {
74 result[i] = texts[i].(string)
75 }
76 return result, nil
77 }
78
79 func (l *locatorImpl) BoundingBox(options ...LocatorBoundingBoxOptions) (*Rect, error) {
80 var option PageWaitForSelectorOptions
81 if len(options) == 1 {
82 option.Timeout = options[0].Timeout
83 }
84
85 result, err := l.withElement(func(handle ElementHandle) (interface{}, error) {
86 return handle.BoundingBox()
87 }, option)
88
89 if err != nil {
90 return nil, err
91 }
92
93 return result.(*Rect), nil
94 }
95
96 func (l *locatorImpl) Check(options ...FrameCheckOptions) error {
97 return l.frame.Check(l.selector, options...)
98 }
99
100 func (l *locatorImpl) Click(options ...PageClickOptions) error {
101 return l.frame.Click(l.selector, options...)
102 }
103
104 func (l *locatorImpl) Count() (int, error) {
105 return l.frame.queryCount(l.selector)
106 }
107
108 func (l *locatorImpl) Dblclick(options ...FrameDblclickOptions) error {
109 return l.frame.Dblclick(l.selector, options...)
110 }
111
112 func (l *locatorImpl) DispatchEvent(typ string, eventInit interface{}, options ...PageDispatchEventOptions) error {
113 return l.frame.DispatchEvent(l.selector, typ, eventInit, options...)
114 }
115
116 func (l *locatorImpl) DragTo(target Locator, options ...FrameDragAndDropOptions) error {
117 if len(options) == 1 {
118 options[0].Strict = Bool(true)
119 }
120 return l.frame.DragAndDrop(l.selector, target.(*locatorImpl).selector, options...)
121 }
122
123 func (l *locatorImpl) ElementHandle(options ...LocatorElementHandleOptions) (ElementHandle, error) {
124 option := PageWaitForSelectorOptions{
125 State: WaitForSelectorStateAttached,
126 Strict: Bool(true),
127 }
128 if len(options) == 1 {
129 option.Timeout = options[0].Timeout
130 }
131 return l.frame.WaitForSelector(l.selector, option)
132 }
133
134 func (l *locatorImpl) ElementHandles() ([]ElementHandle, error) {
135 return l.frame.QuerySelectorAll(l.selector)
136 }
137
138 func (l *locatorImpl) Evaluate(expression string, arg interface{}, options ...LocatorEvaluateOptions) (interface{}, error) {
139 var option PageWaitForSelectorOptions
140 if len(options) == 1 {
141 option.Timeout = options[0].Timeout
142 }
143
144 return l.withElement(func(handle ElementHandle) (interface{}, error) {
145 return handle.Evaluate(expression, arg)
146 }, option)
147 }
148
149 func (l *locatorImpl) EvaluateAll(expression string, options ...interface{}) (interface{}, error) {
150 return l.frame.EvalOnSelectorAll(l.selector, expression, options...)
151 }
152
153 func (l *locatorImpl) EvaluateHandle(expression string, arg interface{}, options ...LocatorEvaluateHandleOptions) (interface{}, error) {
154 var option PageWaitForSelectorOptions
155 if len(options) == 1 {
156 option.Timeout = options[0].Timeout
157 }
158
159 return l.withElement(func(handle ElementHandle) (interface{}, error) {
160 return handle.EvaluateHandle(expression, arg)
161 }, option)
162 }
163
164 func (l *locatorImpl) Fill(value string, options ...FrameFillOptions) error {
165 if len(options) == 1 {
166 options[0].Strict = Bool(true)
167 }
168 return l.frame.Fill(l.selector, value, options...)
169 }
170
171 func (l *locatorImpl) First() (Locator, error) {
172 return newLocator(l.frame, l.selector+" >> nth=0")
173 }
174
175 func (l *locatorImpl) Focus(options ...FrameFocusOptions) error {
176 if len(options) == 1 {
177 options[0].Strict = Bool(true)
178 }
179 return l.frame.Focus(l.selector, options...)
180 }
181
182 func (l *locatorImpl) GetAttribute(name string, options ...PageGetAttributeOptions) (string, error) {
183 if len(options) == 1 {
184 options[0].Strict = Bool(true)
185 }
186 return l.frame.GetAttribute(l.selector, name, options...)
187 }
188
189 func (l *locatorImpl) Highlight() error {
190 return l.frame.highlight(l.selector)
191 }
192
193 func (l *locatorImpl) Hover(options ...PageHoverOptions) error {
194 if len(options) == 1 {
195 options[0].Strict = Bool(true)
196 }
197 return l.frame.Hover(l.selector, options...)
198 }
199
200 func (l *locatorImpl) InnerHTML(options ...PageInnerHTMLOptions) (string, error) {
201 if len(options) == 1 {
202 options[0].Strict = Bool(true)
203 }
204 return l.frame.InnerHTML(l.selector, options...)
205 }
206
207 func (l *locatorImpl) InnerText(options ...PageInnerTextOptions) (string, error) {
208 if len(options) == 1 {
209 options[0].Strict = Bool(true)
210 }
211 return l.frame.InnerText(l.selector, options...)
212 }
213
214 func (l *locatorImpl) InputValue(options ...FrameInputValueOptions) (string, error) {
215 if len(options) == 1 {
216 options[0].Strict = Bool(true)
217 }
218 return l.frame.InputValue(l.selector, options...)
219 }
220
221 func (l *locatorImpl) IsChecked(options ...FrameIsCheckedOptions) (bool, error) {
222 if len(options) == 1 {
223 options[0].Strict = Bool(true)
224 }
225 return l.frame.IsChecked(l.selector, options...)
226 }
227
228 func (l *locatorImpl) IsDisabled(options ...FrameIsDisabledOptions) (bool, error) {
229 if len(options) == 1 {
230 options[0].Strict = Bool(true)
231 }
232 return l.frame.IsDisabled(l.selector, options...)
233 }
234
235 func (l *locatorImpl) IsEditable(options ...FrameIsEditableOptions) (bool, error) {
236 if len(options) == 1 {
237 options[0].Strict = Bool(true)
238 }
239 return l.frame.IsEditable(l.selector, options...)
240 }
241
242 func (l *locatorImpl) IsEnabled(options ...FrameIsEnabledOptions) (bool, error) {
243 if len(options) == 1 {
244 options[0].Strict = Bool(true)
245 }
246 return l.frame.IsEnabled(l.selector, options...)
247 }
248
249 func (l *locatorImpl) IsHidden(options ...FrameIsHiddenOptions) (bool, error) {
250 if len(options) == 1 {
251 options[0].Strict = Bool(true)
252 }
253 return l.frame.IsHidden(l.selector, options...)
254 }
255
256 func (l *locatorImpl) IsVisible(options ...FrameIsVisibleOptions) (bool, error) {
257 if len(options) == 1 {
258 options[0].Strict = Bool(true)
259 }
260 return l.frame.IsVisible(l.selector, options...)
261 }
262
263 func (l *locatorImpl) Last() (Locator, error) {
264 return newLocator(l.frame, l.selector+" >> nth=-1")
265 }
266
267 func (l *locatorImpl) Locator(selector string) (Locator, error) {
268 return newLocator(l.frame, l.selector+" >> "+selector)
269 }
270
271 func (l *locatorImpl) Nth(index int) (Locator, error) {
272 return newLocator(l.frame, l.selector+" >> nth="+strconv.Itoa(index))
273 }
274
275 func (l *locatorImpl) Page() Page {
276 return l.frame.Page()
277 }
278
279 func (l *locatorImpl) Press(key string, options ...PagePressOptions) error {
280 if len(options) == 1 {
281 options[0].Strict = Bool(true)
282 }
283 return l.frame.Press(l.selector, key, options...)
284 }
285
286 func (l *locatorImpl) Screenshot(options ...LocatorScreenshotOptions) ([]byte, error) {
287 var option PageWaitForSelectorOptions
288 if len(options) == 1 {
289 option.Timeout = options[0].Timeout
290 }
291
292 result, err := l.withElement(func(handle ElementHandle) (interface{}, error) {
293 var screenshotOption ElementHandleScreenshotOptions
294 if len(options) == 1 {
295 screenshotOption = ElementHandleScreenshotOptions(options[0])
296 }
297 return handle.Screenshot(screenshotOption)
298 }, option)
299
300 if err != nil {
301 return nil, err
302 }
303
304 return result.([]byte), nil
305 }
306
307 func (l *locatorImpl) ScrollIntoViewIfNeeded(options ...LocatorScrollIntoViewIfNeededOptions) error {
308 var option PageWaitForSelectorOptions
309 if len(options) == 1 {
310 option.Timeout = options[0].Timeout
311 }
312
313 _, err := l.withElement(func(handle ElementHandle) (interface{}, error) {
314 var opt ElementHandleScrollIntoViewIfNeededOptions
315 if len(options) == 1 {
316 opt.Timeout = options[0].Timeout
317 }
318 return nil, handle.ScrollIntoViewIfNeeded(opt)
319 }, option)
320
321 return err
322 }
323
324 func (l *locatorImpl) SelectOption(values SelectOptionValues, options ...FrameSelectOptionOptions) ([]string, error) {
325 if len(options) == 1 {
326 options[0].Strict = Bool(true)
327 }
328 return l.frame.SelectOption(l.selector, values, options...)
329 }
330
331 func (l *locatorImpl) SelectText(options ...LocatorSelectTextOptions) error {
332 var option PageWaitForSelectorOptions
333 if len(options) == 1 {
334 option.Timeout = options[0].Timeout
335 }
336
337 _, err := l.withElement(func(handle ElementHandle) (interface{}, error) {
338 var opt ElementHandleSelectTextOptions
339 if len(options) == 1 {
340 opt = ElementHandleSelectTextOptions(options[0])
341 }
342 return nil, handle.SelectText(opt)
343 }, option)
344
345 return err
346 }
347
348 func (l *locatorImpl) SetChecked(checked bool, options ...FrameSetCheckedOptions) error {
349 if len(options) == 1 {
350 options[0].Strict = Bool(true)
351 }
352 return l.frame.SetChecked(l.selector, checked, options...)
353 }
354
355 func (l *locatorImpl) SetInputFiles(files []InputFile, options ...FrameSetInputFilesOptions) error {
356 if len(options) == 1 {
357 options[0].Strict = Bool(true)
358 }
359 return l.frame.SetInputFiles(l.selector, files, options...)
360 }
361
362 func (l *locatorImpl) Tap(options ...FrameTapOptions) error {
363 if len(options) == 1 {
364 options[0].Strict = Bool(true)
365 }
366 return l.frame.Tap(l.selector, options...)
367 }
368
369 func (l *locatorImpl) TextContent(options ...FrameTextContentOptions) (string, error) {
370 if len(options) == 1 {
371 options[0].Strict = Bool(true)
372 }
373 return l.frame.TextContent(l.selector, options...)
374 }
375
376 func (l *locatorImpl) Type(text string, options ...PageTypeOptions) error {
377 if len(options) == 1 {
378 options[0].Strict = Bool(true)
379 }
380 return l.frame.Type(l.selector, text, options...)
381 }
382
383 func (l *locatorImpl) Uncheck(options ...FrameUncheckOptions) error {
384 if len(options) == 1 {
385 options[0].Strict = Bool(true)
386 }
387 return l.frame.Uncheck(l.selector, options...)
388 }
389
390 func (l *locatorImpl) WaitFor(options ...PageWaitForSelectorOptions) error {
391 if len(options) == 1 {
392 options[0].Strict = Bool(true)
393 }
394 _, err := l.frame.WaitForSelector(l.selector, options...)
395 return err
396 }
397
398 func (l *locatorImpl) withElement(
399 callback func(handle ElementHandle) (interface{}, error),
400 options ...PageWaitForSelectorOptions,
401 ) (interface{}, error) {
402 handle, err := l.frame.WaitForSelector(l.selector, options...)
403 if err != nil {
404 return nil, err
405 }
406
407 result, err := callback(handle)
408 if err != nil {
409 return nil, err
410 }
411 return result, nil
412 }
413
View as plain text