Module: Bard::AttachmentField::TestHelper
- Defined in:
- lib/bard/attachment_field/cucumber.rb
Class Method Summary collapse
-
.attach_files(session, element_id, file_paths) ⇒ Object
File inputs inside shadow DOM can’t be set directly by browser automation, so we use a temp regular DOM input and transfer files via JavaScript.
- .find_field(session, label) ⇒ Object
- .get_files(field) ⇒ Object
- .resolve_fixture_path(filename) ⇒ Object
- .validation_messages(session, element) ⇒ Object
- .wait_for_files(session, element_id, minimum, timeout: 15) ⇒ Object
- .wait_for_no_files(session, element_id, selector = "attachment-file", timeout: 10) ⇒ Object
- .wait_for_upload(session, element_id, timeout: 30) ⇒ Object
Class Method Details
.attach_files(session, element_id, file_paths) ⇒ Object
File inputs inside shadow DOM can’t be set directly by browser automation, so we use a temp regular DOM input and transfer files via JavaScript.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/bard/attachment_field/cucumber.rb', line 38 def attach_files(session, element_id, file_paths) session.execute_script("document.body.insertAdjacentHTML('beforeend', '<input type=\"file\" id=\"_pw_file_helper\" multiple style=\"opacity:0;position:absolute;pointer-events:none\">')") temp_input = session.document.find("#_pw_file_helper", visible: :all) temp_input.set(file_paths) session.execute_script(<<~JS) (() => { const temp = document.getElementById('_pw_file_helper'); const host = document.getElementById('#{element_id}'); host.addFiles(temp.files); temp.remove(); })() JS end |
.find_field(session, label) ⇒ Object
26 27 28 29 30 31 32 33 34 |
# File 'lib/bard/attachment_field/cucumber.rb', line 26 def find_field(session, label) label_element = begin session.find("label", exact_text: label, visible: :all, match: :first) rescue Capybara::ElementNotFound session.find("label", text: /^#{Regexp.escape(label)}/, visible: :all, match: :first) end element_id = label_element[:for] session.find("input-attachment##{element_id}") end |
.get_files(field) ⇒ Object
94 95 96 |
# File 'lib/bard/attachment_field/cucumber.rb', line 94 def get_files(field) field.all("attachment-file").map { |e| e[:filename] } end |
.resolve_fixture_path(filename) ⇒ Object
22 23 24 |
# File 'lib/bard/attachment_field/cucumber.rb', line 22 def resolve_fixture_path(filename) File.(filename, fixtures_path.call) end |
.validation_messages(session, element) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/bard/attachment_field/cucumber.rb', line 98 def (session, element) = [] << element.evaluate_script("this.validationMessage") # Get validation errors from attachment-file elements' shadow DOM child_errors = session.evaluate_script(<<~JS, element[:id]) ((elementId) => { const host = document.getElementById(elementId); const files = host.querySelectorAll('attachment-file'); return Array.from(files).map(f => { const errorEl = f.shadowRoot?.querySelector('.validation-error'); return errorEl?.textContent || ''; }).filter(e => e); })(arguments[0]) JS .concat(child_errors) .select { |m| m && !m.empty? } end |
.wait_for_files(session, element_id, minimum, timeout: 15) ⇒ Object
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/bard/attachment_field/cucumber.rb', line 54 def wait_for_files(session, element_id, minimum, timeout: 15) start = Process.clock_gettime(Process::CLOCK_MONOTONIC) loop do count = session.evaluate_script("document.getElementById(`#{element_id}`).querySelectorAll('attachment-file').length") break if count >= minimum elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start raise "Expected at least #{minimum} attachment-file(s) in ##{element_id}, found #{count} after #{timeout}s" if elapsed > timeout sleep 0.1 end end |
.wait_for_no_files(session, element_id, selector = "attachment-file", timeout: 10) ⇒ Object
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/bard/attachment_field/cucumber.rb', line 65 def wait_for_no_files(session, element_id, selector = "attachment-file", timeout: 10) start = Process.clock_gettime(Process::CLOCK_MONOTONIC) loop do count = session.evaluate_script("document.getElementById(`#{element_id}`).querySelectorAll(`#{selector}`).length") break if count == 0 elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start raise "Expected no #{selector} in ##{element_id}, found #{count} after #{timeout}s" if elapsed > timeout sleep 0.1 end end |
.wait_for_upload(session, element_id, timeout: 30) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/bard/attachment_field/cucumber.rb', line 76 def wait_for_upload(session, element_id, timeout: 30) start = Process.clock_gettime(Process::CLOCK_MONOTONIC) loop do result = session.evaluate_script(<<~JS) (() => { const files = document.getElementById('#{element_id}').querySelectorAll('attachment-file'); return Array.from(files).map(e => ({ state: e.getAttribute('state'), value: e.value })); })() JS states_done = result.all? { |f| f["state"] == "complete" || f["state"] == "error" } values_set = result.all? { |f| f["state"] == "error" || (f["value"] && !f["value"].empty?) } break if states_done && values_set elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start raise "Uploads not complete after #{timeout}s (files=#{result})" if elapsed > timeout sleep 0.1 end end |