haya_select_helpers 0.0.23 → 0.0.24

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96f76d49276820fa73aa7b62dc77f39ce6f1b92ced049fc7ba96bb99be275a34
4
- data.tar.gz: fa5bdbe4b24df0e7bf0ec7c17b358af38991822881826c96a218f3a1bf688f80
3
+ metadata.gz: d289ef00a14194f8d755422fbfb764557cc4d9942cfb09556235bd19c4cb3bed
4
+ data.tar.gz: c764a04952e83c05eac23e3199dda1e46c3986435724b305b478feac858eb027
5
5
  SHA512:
6
- metadata.gz: eaa631f02a3f44ed87a3070e341f5fd08d0cd85e518f9e6e2e217d4c7167f6491b75f6e282e681b0da5d672e78eee79ffb81a26eff637041d5b06c8a0ba7117f
7
- data.tar.gz: 9b09bc8269f204a345e18331a2dd49f1080cb0be89a52677f9aa306fde5c2a8ec19d9d389ea7cea8383580b1c27c4c6cf73ce0762f941325d13a8871f4ad5fd0
6
+ metadata.gz: cdea7c07e9ca63f5318d240086afec8754425385b7c1763b685fe9dd52c7922ae5ad39fab7aab30e00396821411dfd2a939d768f3d417d5d854210f8b868a222
7
+ data.tar.gz: 0ed2c3a90ee5c44f03e95b0c12cbffe8786456687cfc0fe56db610154deeb46491396da0ddcadf8b887ea5c06c2cac48bf6abb57821c1af8fead1800d0ebca80
data/lib/haya_select.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  # rubocop:disable Metrics/ClassLength, Style/Documentation
4
4
  class HayaSelect
5
5
  attr_reader :base_selector,
6
+ :debug,
6
7
  :not_opened_current_selected_selector,
7
8
  :opened_current_selected_selector,
8
9
  :options_selector,
@@ -19,8 +20,9 @@ class HayaSelect
19
20
  :wait_for_selector,
20
21
  to: :scope
21
22
 
22
- def initialize(id:, scope:)
23
+ def initialize(id:, scope:, debug: false)
23
24
  @base_selector = "[data-component='haya-select'][data-id='#{id}']"
25
+ @debug = debug
24
26
  @not_opened_current_selected_selector = "#{base_selector}[data-opened='false'] [data-class='current-selected']"
25
27
  @opened_current_selected_selector = "#{base_selector}[data-opened='true'] [data-class='current-selected']"
26
28
  @options_selector = "[data-class='options-container'][data-id='#{id}']"
@@ -43,12 +45,12 @@ class HayaSelect
43
45
  attempts = 0
44
46
 
45
47
  begin
48
+ wait_for_selector("#{base_selector}[data-opened='false']", wait: 3)
46
49
  click_open_target_element
47
50
  wait_for_open
48
51
  self
49
52
  rescue WaitUtil::TimeoutError, Selenium::WebDriver::Error::StaleElementReferenceError
50
53
  attempts += 1
51
- send_open_key
52
54
  retry if attempts < 3
53
55
  raise "Failed to open haya-select options for '#{base_selector}' (options container not found)"
54
56
  end
@@ -93,14 +95,19 @@ class HayaSelect
93
95
  attempts = 0
94
96
 
95
97
  begin
96
- log_select_start(label, value, allow_if_selected, attempts)
98
+ debug_log do
99
+ "select start selector=#{base_selector} " \
100
+ "label=#{label.inspect} value=#{value.inspect} " \
101
+ "allow_if_selected=#{allow_if_selected} attempts=#{attempts}"
102
+ end
97
103
  guard_already_selected(label, value, allow_if_selected) if attempts.zero?
104
+ return self if attempts.positive? && selected?(label, value)
98
105
 
99
- selected_value, allow_blank = select_value_and_close(label:, value:)
106
+ selected_value, allow_blank = select_value_and_close(label:, value:, allow_if_selected:)
100
107
  wait_for_selected_after_select(label, value, selected_value, allow_blank)
101
108
  self
102
109
  rescue WaitUtil::TimeoutError, Selenium::WebDriver::Error::StaleElementReferenceError
103
- log_select_retry(attempts)
110
+ debug_log { "select retry selector=#{base_selector} attempts=#{attempts}" }
104
111
  attempts += 1
105
112
  retry if attempts < 3
106
113
  raise
@@ -135,6 +142,7 @@ class HayaSelect
135
142
  def value_no_wait
136
143
  hidden_input = scope.page.first(
137
144
  "#{base_selector} [data-class='current-selected'] input[type='hidden']",
145
+ minimum: 0,
138
146
  visible: false,
139
147
  wait: 0
140
148
  )
@@ -145,6 +153,7 @@ class HayaSelect
145
153
  def label_no_wait
146
154
  current_option = scope.page.first(
147
155
  "#{base_selector} [data-class='current-selected'] [data-class='current-option']",
156
+ minimum: 0,
148
157
  wait: 0
149
158
  )
150
159
 
@@ -182,17 +191,18 @@ class HayaSelect
182
191
  retry
183
192
  end
184
193
 
185
- def select_option_value(label: nil, value: nil)
194
+ def select_option_value(label: nil, value: nil, wait_for_selection: true, allow_if_selected: false)
186
195
  raise "No 'label' or 'value' given" if label.nil? && value.nil?
187
196
 
188
197
  selector = select_option_selector(label: label, value: value)
189
- Rails.logger.debug do
190
- "[haya_select] select_option_value selector=#{base_selector} option_selector=#{selector} label=#{label.inspect} value=#{value.inspect}"
198
+ debug_log do
199
+ "select_option_value selector=#{base_selector} " \
200
+ "option_selector=#{selector} label=#{label.inspect} value=#{value.inspect}"
191
201
  end
192
202
  wait_for_option(selector)
193
203
  option = find_option_element(selector, label)
194
- Rails.logger.debug do
195
- "[haya_select] option_element selector=#{base_selector} " \
204
+ debug_log do
205
+ "option_element selector=#{base_selector} " \
196
206
  "data-value=#{option['data-value'].inspect} " \
197
207
  "data-disabled=#{option['data-disabled'].inspect} " \
198
208
  "data-selected=#{option['data-selected'].inspect}"
@@ -200,8 +210,16 @@ class HayaSelect
200
210
 
201
211
  raise "The '#{label}'-option is disabled" if option['data-disabled'] == 'true'
202
212
 
213
+ selected_option_value = selected_option_value_or_raise(
214
+ option:,
215
+ label:,
216
+ value:,
217
+ allow_if_selected:
218
+ )
219
+ return selected_option_value if selected_option_value
220
+
203
221
  option_value = option['data-value']
204
- perform_option_selection(option, label, option_value)
222
+ perform_option_selection(option, label, option_value, wait_for_selection:)
205
223
 
206
224
  option_value
207
225
  rescue Selenium::WebDriver::Error::StaleElementReferenceError
@@ -284,7 +302,10 @@ private
284
302
 
285
303
  def raise_if_label_already_selected(label, value)
286
304
  return if label.nil? || !value.nil?
287
- return if label_no_wait == label
305
+
306
+ current_label = label_no_wait
307
+ return if current_label == label
308
+ return if current_label
288
309
 
289
310
  current_value = value_no_wait
290
311
  return if current_value.nil? || current_value == ""
@@ -298,12 +319,15 @@ private
298
319
  def value_matches?(value)
299
320
  return false unless value
300
321
 
301
- scope.page.has_selector?(current_value_selector(value), visible: false)
322
+ scope.page.has_selector?(current_value_selector(value), visible: false, wait: 0)
302
323
  end
303
324
 
304
325
  def label_matches_selected_value?(label)
305
326
  return false unless label
306
327
 
328
+ current_label = label_no_wait
329
+ return current_label == label if current_label
330
+
307
331
  current_value = value_no_wait
308
332
  return false if current_value.nil? || current_value == ""
309
333
 
@@ -321,7 +345,8 @@ private
321
345
  end
322
346
 
323
347
  def wait_for_option(selector)
324
- wait_for_selector(selector, visible: :all)
348
+ wait_for_options_visible
349
+ wait_for_selector(selector)
325
350
  end
326
351
 
327
352
  def open_and_find_option_for(label:, value:)
@@ -349,9 +374,13 @@ private
349
374
  end
350
375
 
351
376
  def wait_for_selected_value_or_label(label, value, allow_blank: false)
352
- log_wait_for_selected_start(label, value, allow_blank)
353
377
  value_input_selector = "#{base_selector} [data-class='current-selected'] input[type='hidden']"
354
- log_wait_for_selected_initial_state(value_input_selector)
378
+
379
+ if scope.page.has_selector?(value_input_selector, visible: false, wait: 0)
380
+ return wait_for_selector(current_value_selector(value), visible: false) if value
381
+ return wait_for_selector(current_value_selector(""), visible: false) if allow_blank
382
+ end
383
+
355
384
  wait_for_expect do
356
385
  expect(
357
386
  selected_value_or_label_matches?(
@@ -365,13 +394,13 @@ private
365
394
  end
366
395
 
367
396
  def search_for_option(label)
368
- return unless scope.page.has_selector?(search_input_selector)
397
+ return unless scope.page.has_selector?(search_input_selector, wait: 0)
369
398
 
370
399
  search(label)
371
400
  end
372
401
 
373
402
  def options_container_updated?(search_term, previous_text)
374
- return false unless scope.page.has_selector?(no_options_selector)
403
+ return false unless scope.page.has_selector?(no_options_selector, wait: 0)
375
404
  return false unless search_input_value == search_term
376
405
  return false if previous_text.nil?
377
406
 
@@ -397,18 +426,18 @@ private
397
426
  end
398
427
 
399
428
  def close_if_open
400
- return if scope.page.has_no_selector?(options_selector, visible: :all)
429
+ return if scope.page.has_no_selector?(options_selector, visible: :all, wait: 0)
401
430
 
402
431
  close_attempts = 0
403
432
 
404
- while scope.page.has_selector?(options_selector, visible: :all) && close_attempts < 3
433
+ while scope.page.has_selector?(options_selector, visible: :all, wait: 0) && close_attempts < 3
405
434
  close_attempt
406
435
  break if wait_for_close?
407
436
 
408
437
  close_attempts += 1
409
438
  end
410
439
 
411
- return if scope.page.has_no_selector?(options_selector, visible: :all)
440
+ return if scope.page.has_no_selector?(options_selector, visible: :all, wait: 0)
412
441
 
413
442
  body = wait_for_and_find("body")
414
443
  body.send_keys(:escape)
@@ -428,9 +457,9 @@ private
428
457
 
429
458
  def click_open_target_element
430
459
  target_selector =
431
- if scope.page.has_selector?(select_container_selector)
460
+ if scope.page.has_selector?(select_container_selector, wait: 0)
432
461
  select_container_selector
433
- elsif scope.page.has_selector?(current_selected_selector)
462
+ elsif scope.page.has_selector?(current_selected_selector, wait: 0)
434
463
  current_selected_selector
435
464
  else
436
465
  base_selector
@@ -449,9 +478,7 @@ private
449
478
  end
450
479
 
451
480
  def wait_for_open
452
- wait_for_browser(message: "waiting for haya-select options container to open") do
453
- scope.page.has_selector?(options_selector, visible: :all)
454
- end
481
+ wait_for_selector(options_selector, visible: :all)
455
482
  end
456
483
 
457
484
  def click_element_safely(element)
@@ -473,7 +500,7 @@ private
473
500
  end
474
501
 
475
502
  def close_search_input
476
- return unless scope.page.has_selector?(search_input_selector)
503
+ return unless scope.page.has_selector?(search_input_selector, wait: 0)
477
504
 
478
505
  search_input = wait_for_and_find(search_input_selector)
479
506
  click_element_safely(search_input)
@@ -500,14 +527,6 @@ private
500
527
  scope.page.driver.browser.action.move_to(close_target.native).click.perform
501
528
  end
502
529
 
503
- def send_open_key
504
- return unless scope.page.has_selector?(select_container_selector)
505
-
506
- select_container = wait_for_and_find(select_container_selector)
507
- select_container.send_keys(:enter)
508
- select_container.send_keys(:space)
509
- end
510
-
511
530
  def current_option_label_selectors
512
531
  [
513
532
  "#{base_selector} [data-class='current-selected'] [data-testid='option-presentation-text']",
@@ -517,9 +536,13 @@ private
517
536
 
518
537
  def label_matches?(label)
519
538
  return false unless label
539
+ return true if scope.page.has_selector?(
540
+ "#{base_selector} [data-class='current-selected'] [data-testid='option-presentation'][data-text='#{label}']",
541
+ wait: 0
542
+ )
520
543
 
521
544
  current_option_label_selectors.any? do |selector|
522
- scope.page.has_selector?(selector, exact_text: label)
545
+ scope.page.has_selector?(selector, exact_text: label, wait: 0)
523
546
  end
524
547
  end
525
548
 
@@ -539,9 +562,25 @@ private
539
562
  "#{options_selector} [data-testid='option-presentation-text']"
540
563
  end
541
564
 
565
+ def options_visibility_selector
566
+ "#{options_selector}[data-options-visibility]"
567
+ end
568
+
569
+ def options_visible_selector
570
+ "#{options_selector}[data-options-visibility='visible']"
571
+ end
572
+
573
+ def wait_for_options_visible
574
+ if scope.page.has_selector?(options_visibility_selector, visible: :all, wait: 0)
575
+ wait_for_selector(options_visible_selector, visible: :all)
576
+ else
577
+ wait_for_selector(options_selector, visible: :all)
578
+ end
579
+ end
580
+
542
581
  def option_present?(selector, label)
543
- scope.page.has_selector?(selector, visible: :all) ||
544
- scope.page.has_selector?(option_label_selector, text: label, visible: :all)
582
+ scope.page.has_selector?(selector, visible: :all, wait: 0) ||
583
+ scope.page.has_selector?(option_label_selector, text: label, visible: :all, wait: 0)
545
584
  end
546
585
 
547
586
  def find_option_element(selector, label)
@@ -554,7 +593,7 @@ private
554
593
  return option_presentation.find(:xpath, "./ancestor::*[@data-class='select-option']")
555
594
  end
556
595
 
557
- return wait_for_and_find(selector) if scope.page.has_selector?(selector)
596
+ return wait_for_and_find(selector) if scope.page.has_selector?(selector, wait: 0)
558
597
 
559
598
  option_text = wait_for_and_find(option_label_selector, text: label)
560
599
  option_text.find(:xpath, "./ancestor::*[@data-class='select-option']")
@@ -566,6 +605,16 @@ private
566
605
  option['data-selected'] == 'true' || selected?(label, option_value)
567
606
  end
568
607
 
608
+ def selected_option_value_or_raise(option:, label:, value:, allow_if_selected:)
609
+ return unless option['data-selected'] == 'true'
610
+
611
+ option_value = option["data-value"] || value
612
+ return unless selected?(label, option_value)
613
+ return option_value if allow_if_selected
614
+
615
+ raise "The '#{label || value}'-option is already selected"
616
+ end
617
+
569
618
  def click_target_element(click_target)
570
619
  unless click_target.visible?
571
620
  scope.page.execute_script(
@@ -589,95 +638,83 @@ private
589
638
  element.click
590
639
  end
591
640
 
592
- def perform_option_selection(option, label, option_value)
593
- Rails.logger.debug do
594
- "[haya_select] perform_option_selection selector=#{base_selector} " \
641
+ def perform_option_selection(option, label, option_value, wait_for_selection: true)
642
+ debug_log do
643
+ "perform_option_selection selector=#{base_selector} " \
595
644
  "label=#{label.inspect} option_value=#{option_value.inspect} " \
596
- "data-selected=#{option['data-selected'].inspect}"
645
+ "data-selected=#{option['data-selected'].inspect} wait_for_selection=#{wait_for_selection}"
597
646
  end
598
647
  click_option_element(option)
599
- wait_for_selected_value_or_label(label, option_value)
648
+ wait_for_selected_value_or_label(label, option_value) if wait_for_selection
600
649
  end
601
650
 
602
651
  def select_option_container_selector
603
652
  "#{options_selector} [data-class='select-option']"
604
653
  end
605
654
 
606
- def log_select_start(label, value, allow_if_selected, attempts)
607
- Rails.logger.debug do
608
- "[haya_select] select start selector=#{base_selector} " \
609
- "label=#{label.inspect} value=#{value.inspect} " \
610
- "allow_if_selected=#{allow_if_selected} attempts=#{attempts}"
611
- end
612
- end
613
-
614
- def select_value_and_close(label:, value:)
655
+ def select_value_and_close(label:, value:, allow_if_selected: false)
615
656
  previous_value = value
616
- Rails.logger.debug { "[haya_select] open selector=#{base_selector}" }
657
+ debug_log { "open selector=#{base_selector}" }
617
658
  open
618
- Rails.logger.debug { "[haya_select] select_option_value selector=#{base_selector}" }
619
- selected_value = select_option_value(label:, value:)
620
- Rails.logger.debug do
621
- "[haya_select] select_option_value selector=#{base_selector} selected_value=#{selected_value.inspect}"
659
+ selected_value = select_option_value(label:, value:, wait_for_selection: false, **select_option_value_allow_args(allow_if_selected))
660
+ debug_log do
661
+ "select_option_value selector=#{base_selector} selected_value=#{selected_value.inspect}"
622
662
  end
623
663
  selected_value = "" if selected_value.nil? && value.nil?
624
664
  allow_blank = previous_value == selected_value
625
- Rails.logger.debug { "[haya_select] close_if_open selector=#{base_selector}" }
665
+ debug_log { "close_if_open selector=#{base_selector}" }
626
666
  close_if_open
627
667
  [selected_value, allow_blank]
628
668
  end
629
669
 
630
670
  def wait_for_selected_after_select(label, value, selected_value, allow_blank)
631
671
  expected_value = value || selected_value
632
- Rails.logger.debug do
633
- "[haya_select] wait_for_selected_value_or_label " \
634
- "selector=#{base_selector} label=#{label.inspect} " \
635
- "value=#{expected_value.inspect} allow_blank=#{allow_blank}"
672
+ debug_log do
673
+ "wait_for_selected_value_or_label selector=#{base_selector} " \
674
+ "label=#{label.inspect} value=#{expected_value.inspect} allow_blank=#{allow_blank}"
636
675
  end
637
676
  wait_for_selected_value_or_label(label, expected_value, allow_blank:)
638
677
  end
639
678
 
640
- def log_select_retry(attempts)
641
- Rails.logger.debug { "[haya_select] select retry selector=#{base_selector} attempts=#{attempts}" }
679
+ def debug_log(&)
680
+ return unless debug
681
+
682
+ Rails.logger.debug { "[haya_select] #{yield}" }
642
683
  end
643
684
 
644
- def log_wait_for_selected_start(label, value, allow_blank)
645
- Rails.logger.debug do
646
- "[haya_select] wait_for_selected_value_or_label start selector=#{base_selector} " \
647
- "label=#{label.inspect} value=#{value.inspect} allow_blank=#{allow_blank}"
648
- end
685
+ def selected_value_or_label_matches?(label:, value:, allow_blank:, value_input_selector:)
686
+ has_value_input = scope.page.has_selector?(value_input_selector, visible: false, wait: 0)
687
+ value_matches = current_value_matches?(value)
688
+ blank_matches = blank_value_matches?(allow_blank)
689
+ label_matches = label && label_matches?(label)
690
+ return value_matches || label_matches || blank_matches if has_value_input
691
+
692
+ selected_option_matches = selected_option_matches?(value)
693
+ label_matches || value_matches || selected_option_matches || blank_matches
649
694
  end
650
695
 
651
- def log_wait_for_selected_initial_state(value_input_selector)
652
- has_value_input_initial = scope.page.has_selector?(value_input_selector, visible: false, wait: 0)
653
- current_value_initial = scope.page.first(value_input_selector, visible: false, wait: 0)&.[](:value)
654
- current_option = scope.page.first(
655
- "#{base_selector} [data-class='current-selected'] [data-class='current-option']",
656
- minimum: 0,
696
+ def current_value_matches?(value)
697
+ value && scope.page.has_selector?(current_value_selector(value), visible: false, wait: 0)
698
+ end
699
+
700
+ def selected_option_matches?(value)
701
+ return false unless value
702
+
703
+ scope.page.has_selector?(
704
+ "#{select_option_container_selector}[data-value='#{value}'][data-selected='true']",
705
+ visible: :all,
657
706
  wait: 0
658
707
  )
659
- current_label_initial =
660
- if current_option
661
- option_text = current_option.first("[data-testid='option-presentation-text']", minimum: 0)
662
- option_text ? option_text.text : current_option.text
663
- end
708
+ end
664
709
 
665
- Rails.logger.debug do
666
- "[haya_select] wait_for_selected_value_or_label initial " \
667
- "selector=#{base_selector} has_value_input=#{has_value_input_initial} " \
668
- "current_value=#{current_value_initial.inspect} " \
669
- "current_label=#{current_label_initial.inspect}"
670
- end
710
+ def blank_value_matches?(allow_blank)
711
+ allow_blank && scope.page.has_selector?(current_value_selector(""), visible: false, wait: 0)
671
712
  end
672
713
 
673
- def selected_value_or_label_matches?(label:, value:, allow_blank:, value_input_selector:)
674
- has_value_input = scope.page.has_selector?(value_input_selector, visible: false)
675
- value_matches = value && scope.page.has_selector?(current_value_selector(value), visible: false)
676
- blank_matches = allow_blank && scope.page.has_selector?(current_value_selector(""), visible: false)
677
- return value_matches || blank_matches if has_value_input
714
+ def select_option_value_allow_args(allow_if_selected)
715
+ return {} unless allow_if_selected
678
716
 
679
- label_matches = label && label_matches?(label)
680
- label_matches || value_matches || blank_matches
717
+ {allow_if_selected:}
681
718
  end
682
719
 
683
720
  # rubocop:enable Metrics/ClassLength, Style/Documentation
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HayaSelectHelpers
4
- VERSION = "0.0.23"
4
+ VERSION = "0.0.24"
5
5
  end
@@ -3,7 +3,7 @@ require "haya_select_helpers/version"
3
3
  require "haya_select_helpers/engine"
4
4
 
5
5
  module HayaSelectHelpers
6
- def haya_select(id)
7
- HayaSelect.new(id: id, scope: self)
6
+ def haya_select(id, debug: ENV["HAYA_SELECT_DEBUG"] == "1")
7
+ HayaSelect.new(id: id, scope: self, debug:)
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haya_select_helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.23
4
+ version: 0.0.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - kaspernj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-14 00:00:00.000000000 Z
11
+ date: 2026-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails