playwright-ruby-client 1.39.0 → 1.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,417 @@
1
+ module Playwright
2
+ # ref: https://github.com/microsoft/playwright-python/blob/main/playwright/_impl/_assertions.py
3
+ define_api_implementation :LocatorAssertionsImpl do
4
+ def self._define_negation(method_name)
5
+ define_method("not_#{method_name}") do |*args, **kwargs|
6
+ if kwargs.empty? # for Ruby < 2.7
7
+ _not.public_send(method_name, *args)
8
+ else
9
+ _not.public_send(method_name, *args, **kwargs)
10
+ end
11
+ end
12
+ end
13
+
14
+ def initialize(locator, timeout, is_not, message)
15
+ @locator = locator
16
+ @timeout = timeout
17
+ @is_not = is_not
18
+ @custom_message = message
19
+ end
20
+
21
+ private def expect_impl(expression, expect_options, expected, message)
22
+ expect_options[:timeout] ||= 5000
23
+ expect_options[:isNot] = @is_not
24
+ message.gsub!("expected to", "not expected to") if @is_not
25
+ expect_options.delete(:useInnerText) if expect_options.key?(:useInnerText) && expect_options[:useInnerText].nil?
26
+
27
+ result = @locator.expect(expression, expect_options)
28
+
29
+ if result["matches"] == @is_not
30
+ actual = result["received"]
31
+
32
+ log =
33
+ if result.key?("log")
34
+ log_contents = result["log"].join("\n").strip
35
+
36
+ "\nCall log:\n #{log_contents}"
37
+ else
38
+ ""
39
+ end
40
+
41
+ out_message =
42
+ if @custom_message && expected
43
+ "\nExpected value: '#{expected}'"
44
+ elsif @custom_message
45
+ @custom_message
46
+ elsif message != "" && expected
47
+ "\n#{message} '#{expected}'"
48
+ else
49
+ "\n#{message}"
50
+ end
51
+
52
+ out = "#{out_message}\nActual value #{actual} #{log}"
53
+ raise AssertionError.new(out)
54
+ else
55
+ true
56
+ end
57
+ end
58
+
59
+ private def _not # "not" is reserved in Ruby
60
+ LocatorAssertionsImpl.new(
61
+ @locator,
62
+ @timeout,
63
+ !@is_not,
64
+ @message
65
+ )
66
+ end
67
+
68
+ private def expected_regex(pattern, match_substring, normalize_white_space, ignore_case)
69
+ regex = JavaScript::Regex.new(pattern)
70
+ expected = {
71
+ regexSource: regex.source,
72
+ regexFlags: regex.flag,
73
+ matchSubstring: match_substring,
74
+ normalizeWhiteSpace: normalize_white_space,
75
+ ignoreCase: ignore_case
76
+ }
77
+ expected.delete(:ignoreCase) if ignore_case.nil?
78
+
79
+ expected
80
+ end
81
+
82
+ private def to_expected_text_values(items, match_substring = false, normalize_white_space = false, ignore_case = false)
83
+ return [] unless items.respond_to?(:each)
84
+
85
+ items.each.with_object([]) do |item, out|
86
+ out <<
87
+ if item.is_a?(String) && ignore_case
88
+ {
89
+ string: item,
90
+ matchSubstring: match_substring,
91
+ normalizeWhiteSpace: normalize_white_space,
92
+ ignoreCase: ignore_case,
93
+ }
94
+ elsif item.is_a?(String)
95
+ {
96
+ string: item,
97
+ matchSubstring: match_substring,
98
+ normalizeWhiteSpace: normalize_white_space,
99
+ }
100
+ elsif item.is_a?(Regexp)
101
+ expected_regex(item, match_substring, normalize_white_space, ignore_case)
102
+ end
103
+ end
104
+ end
105
+
106
+ def to_contain_text(expected, ignoreCase: nil, timeout: nil, useInnerText: nil)
107
+ useInnerText = false if useInnerText.nil?
108
+
109
+ if expected.respond_to?(:each)
110
+ expected_text = to_expected_text_values(
111
+ expected,
112
+ true,
113
+ true,
114
+ ignoreCase,
115
+ )
116
+
117
+ expect_impl(
118
+ "to.contain.text.array",
119
+ {
120
+ expectedText: expected_text,
121
+ useInnerText: useInnerText,
122
+ timeout: timeout,
123
+ },
124
+ expected,
125
+ "Locator expected to contain text"
126
+ )
127
+ else
128
+ expected_text = to_expected_text_values(
129
+ [expected],
130
+ true,
131
+ true,
132
+ ignoreCase
133
+ )
134
+
135
+ expect_impl(
136
+ "to.have.text",
137
+ {
138
+ expectedText: expected_text,
139
+ useInnerText: useInnerText,
140
+ timeout: timeout,
141
+ },
142
+ expected,
143
+ "Locator expected to contain text"
144
+ )
145
+ end
146
+ end
147
+ _define_negation :to_contain_text
148
+
149
+ def to_have_attribute(name, value, timeout: nil)
150
+ expected_text = to_expected_text_values([value])
151
+ expect_impl(
152
+ "to.have.attribute.value",
153
+ {
154
+ expressionArg: name,
155
+ expectedText: expected_text,
156
+ timeout: timeout,
157
+ },
158
+ value,
159
+ "Locator expected to have attribute"
160
+ )
161
+ end
162
+ _define_negation :to_have_attribute
163
+
164
+ def to_have_class(expected, timeout: nil)
165
+ if expected.respond_to?(:each)
166
+ expected_text = to_expected_text_values(expected)
167
+ expect_impl(
168
+ "to.have.class.array",
169
+ {
170
+ expectedText: expected_text,
171
+ timeout: timeout,
172
+ },
173
+ expected,
174
+ "Locator expected to have class"
175
+ )
176
+ else
177
+ expected_text = to_expected_text_values([expected])
178
+ expect_impl(
179
+ "to.have.class",
180
+ {
181
+ expectedText: expected_text,
182
+ timeout: timeout,
183
+ },
184
+ expected,
185
+ "Locator expected to have class"
186
+ )
187
+ end
188
+ end
189
+ _define_negation :to_have_class
190
+
191
+ def to_have_count(count, timeout: nil)
192
+ expect_impl(
193
+ "to.have.count",
194
+ {
195
+ expectedNumber: count,
196
+ timeout: timeout,
197
+ },
198
+ count,
199
+ "Locator expected to have count"
200
+ )
201
+ end
202
+ _define_negation :to_have_count
203
+
204
+ def to_have_css(name, value, timeout: nil)
205
+ expected_text = to_expected_text_values([value])
206
+ expect_impl(
207
+ "to.have.css",
208
+ {
209
+ expressionArg: name,
210
+ expectedText: expected_text,
211
+ timeout: timeout,
212
+ },
213
+ value,
214
+ "Locator expected to have CSS"
215
+ )
216
+ end
217
+ _define_negation :to_have_css
218
+
219
+ def to_have_id(id, timeout: nil)
220
+ expected_text = to_expected_text_values([id])
221
+ expect_impl(
222
+ "to.have.id",
223
+ {
224
+ expectedText: expected_text,
225
+ timeout: timeout,
226
+ },
227
+ id,
228
+ "Locator expected to have ID"
229
+ )
230
+ end
231
+ _define_negation :to_have_id
232
+
233
+ def to_have_js_property(name, value, timeout: nil)
234
+ expect_impl(
235
+ "to.have.property",
236
+ {
237
+ expressionArg: name,
238
+ expectedValue: value,
239
+ timeout: timeout,
240
+ },
241
+ value,
242
+ "Locator expected to have JS Property"
243
+ )
244
+ end
245
+ _define_negation :to_have_js_property
246
+
247
+ def to_have_value(value, timeout: nil)
248
+ expected_text = to_expected_text_values([value])
249
+
250
+ expect_impl(
251
+ "to.have.value",
252
+ {
253
+ expectedText: expected_text,
254
+ timeout: timeout,
255
+ },
256
+ value,
257
+ "Locator expected to have Value"
258
+ )
259
+ end
260
+ _define_negation :to_have_value
261
+
262
+ def to_have_values(values, timeout: nil)
263
+ expected_text = to_expected_text_values(values)
264
+
265
+ expect_impl(
266
+ "to.have.values",
267
+ {
268
+ expectedText: expected_text,
269
+ timeout: timeout,
270
+ },
271
+ values,
272
+ "Locator expected to have Values"
273
+ )
274
+ end
275
+ _define_negation :to_have_values
276
+
277
+ def to_have_text(expected, ignoreCase: nil, timeout: nil, useInnerText: nil)
278
+ if expected.respond_to?(:each)
279
+ expected_text = to_expected_text_values(
280
+ expected,
281
+ true,
282
+ true,
283
+ ignoreCase,
284
+ )
285
+ expect_impl(
286
+ "to.have.text.array",
287
+ {
288
+ expectedText: expected_text,
289
+ useInnerText: useInnerText,
290
+ timeout: timeout,
291
+ },
292
+ expected,
293
+ "Locator expected to have text"
294
+ )
295
+ else
296
+ expected_text = to_expected_text_values(
297
+ [expected],
298
+ true,
299
+ true,
300
+ ignoreCase,
301
+ )
302
+ expect_impl(
303
+ "to.have.text",
304
+ {
305
+ expectedText: expected_text,
306
+ useInnerText: useInnerText,
307
+ timeout: timeout,
308
+ },
309
+ expected,
310
+ "Locator expected to have text"
311
+ )
312
+ end
313
+ end
314
+ _define_negation :to_have_text
315
+
316
+ def to_be_attached(attached: nil, timeout: nil)
317
+ expect_impl(
318
+ (attached || attached.nil?) ? "to.be.attached" : "to.be.detached",
319
+ { timeout: timeout },
320
+ nil,
321
+ "Locator expected to be attached"
322
+ )
323
+ end
324
+ _define_negation :to_be_attached
325
+
326
+ def to_be_checked(checked: nil, timeout: nil)
327
+ expect_impl(
328
+ (checked || checked.nil?) ? "to.be.checked" : "to.be.unchecked",
329
+ { timeout: timeout },
330
+ nil,
331
+ "Locator expected to be checked"
332
+ )
333
+ end
334
+ _define_negation :to_be_checked
335
+
336
+ def to_be_disabled(timeout: nil)
337
+ expect_impl(
338
+ "to.be.disabled",
339
+ { timeout: timeout },
340
+ nil,
341
+ "Locator expected to be disabled"
342
+ )
343
+ end
344
+ _define_negation :to_be_disabled
345
+
346
+ def to_be_editable(editable: nil, timeout: nil)
347
+ expect_impl(
348
+ (editable || editable.nil?) ? "to.be.editable" : "to.be.readonly",
349
+ { timeout: timeout },
350
+ nil,
351
+ "Locator expected to be editable"
352
+ )
353
+ end
354
+ _define_negation :to_be_editable
355
+
356
+ def to_be_empty(timeout: nil)
357
+ expect_impl(
358
+ "to.be.empty",
359
+ { timeout: timeout },
360
+ nil,
361
+ "Locator expected to be empty"
362
+ )
363
+ end
364
+ _define_negation :to_be_empty
365
+
366
+ def to_be_enabled(enabled: nil, timeout: nil)
367
+ expect_impl(
368
+ (enabled || enabled.nil?) ? "to.be.enabled" : "to.be.disabled",
369
+ { timeout: timeout },
370
+ nil,
371
+ "Locator expected to be enabled"
372
+ )
373
+ end
374
+ _define_negation :to_be_enabled
375
+
376
+ def to_be_hidden(timeout: nil)
377
+ expect_impl(
378
+ "to.be.hidden",
379
+ { timeout: timeout },
380
+ nil,
381
+ "Locator expected to be hidden"
382
+ )
383
+ end
384
+ _define_negation :to_be_hidden
385
+
386
+ def to_be_visible(timeout: nil, visible: nil)
387
+ expect_impl(
388
+ (visible || visible.nil?) ? "to.be.visible" : "to.be.hidden",
389
+ { timeout: timeout },
390
+ nil,
391
+ "Locator expected to be visible"
392
+ )
393
+ end
394
+ _define_negation :to_be_visible
395
+
396
+ def to_be_focused(timeout: nil)
397
+ expect_impl(
398
+ "to.be.focused",
399
+ { timeout: timeout },
400
+ nil,
401
+ "Locator expected to be focused"
402
+ )
403
+ end
404
+ _define_negation :to_be_focused
405
+
406
+ def to_be_in_viewport(ratio: nil, timeout: nil)
407
+ expect_impl(
408
+ "to.be.in.viewport",
409
+ { timeout: timeout, expectedNumber: ratio },
410
+ nil,
411
+ "Locator expected to be in viewport"
412
+ )
413
+ end
414
+ _define_negation :to_be_in_viewport
415
+
416
+ end
417
+ end
@@ -304,11 +304,7 @@ module Playwright
304
304
  end
305
305
 
306
306
  def all
307
- Enumerator.new do |out|
308
- count.times do |i|
309
- out << nth(i)
310
- end
311
- end
307
+ count.times.map { |i| nth(i) }
312
308
  end
313
309
 
314
310
  def count
@@ -488,5 +484,28 @@ module Playwright
488
484
  def highlight
489
485
  @frame.highlight(@selector)
490
486
  end
487
+
488
+ def expect(expression, options)
489
+ if options.key?(:expectedValue)
490
+ options[:expectedValue] = JavaScript::ValueSerializer
491
+ .new(options[:expectedValue])
492
+ .serialize
493
+ end
494
+
495
+ result = @frame.channel.send_message_to_server_result(
496
+ "expect",
497
+ {
498
+ selector: @selector,
499
+ expression: expression,
500
+ **options,
501
+ }
502
+ )
503
+
504
+ if result.key?('received')
505
+ result['received'] = JavaScript::ValueParser.new(result['received']).parse
506
+ end
507
+
508
+ result
509
+ end
491
510
  end
492
511
  end
@@ -0,0 +1,68 @@
1
+ module Playwright
2
+ # this module is responsible for running playwright assertions and integrating
3
+ # with test frameworks.
4
+ module Test
5
+ # ref: https://github.com/microsoft/playwright-python/blob/main/playwright/sync_api/__init__.py#L90
6
+ class Expect
7
+ def initialize
8
+ @timeout_settings = TimeoutSettings.new
9
+ end
10
+
11
+ def call(actual, message = nil)
12
+ if actual.is_a?(Locator)
13
+ LocatorAssertions.new(
14
+ LocatorAssertionsImpl.new(
15
+ actual,
16
+ @timeout_settings.timeout,
17
+ false,
18
+ message,
19
+ )
20
+ )
21
+ else
22
+ raise NotImplementedError.new("Only locator assertions are currently implemented")
23
+ end
24
+ end
25
+ end
26
+
27
+ module Matchers
28
+ class PlaywrightMatcher
29
+ def initialize(expectation_method, *args, **kwargs)
30
+ @method = expectation_method
31
+ @args = args
32
+ @kwargs = kwargs
33
+ end
34
+
35
+ def matches?(actual)
36
+ Expect.new.call(actual).send(@method, *@args, **@kwargs)
37
+ true
38
+ rescue AssertionError => e
39
+ @failure_message = e.full_message
40
+ false
41
+ end
42
+
43
+ def failure_message
44
+ @failure_message
45
+ end
46
+
47
+ # we have to invert the message again here because RSpec wants to control
48
+ # its own negation
49
+ def failure_message_when_negated
50
+ @failure_message.gsub("expected to", "not expected to")
51
+ end
52
+ end
53
+ end
54
+
55
+ ALL_ASSERTIONS = LocatorAssertions.instance_methods(false)
56
+
57
+ ALL_ASSERTIONS
58
+ .map(&:to_s)
59
+ .each do |method_name|
60
+ # to_be_visible => be_visible
61
+ # not_to_be_visible => not_be_visible
62
+ root_method_name = method_name.gsub("to_", "")
63
+ Matchers.define_method(root_method_name) do |*args, **kwargs|
64
+ Matchers::PlaywrightMatcher.new(method_name, *args, **kwargs)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '1.39.0'
4
+ VERSION = '1.39.1'
5
5
  COMPATIBLE_PLAYWRIGHT_VERSION = '1.39.0'
6
6
  end
@@ -1175,6 +1175,11 @@ module Playwright
1175
1175
  wrap_impl(@impl.wait_for(state: unwrap_impl(state), timeout: unwrap_impl(timeout)))
1176
1176
  end
1177
1177
 
1178
+ # @nodoc
1179
+ def expect(expression, options)
1180
+ wrap_impl(@impl.expect(unwrap_impl(expression), unwrap_impl(options)))
1181
+ end
1182
+
1178
1183
  # @nodoc
1179
1184
  def to_s
1180
1185
  wrap_impl(@impl.to_s)