Rails - Capybara, populate hidden field from trix-editor - ruby-on-rails

I use Trix Editor to have WISIWIG in my form. I want to test with RSpec and Capybara but trix-editor put the field hidden.
<div class="form-group">
<trix-editor class="formatted_content form-control" placeholder="Description" input="annonce_ad_body_trix_input_annonce_ad"></trix-editor><input type="hidden" name="annonce_ad[body]" id="annonce_ad_body_trix_input_annonce_ad" />
</div>
I need to know how i can fill this hidden field with Capybara to make my test pass. I have try these attemp:
fill_in "annonce_ad[body]", with: "my value"
find(:css, 'trix-editor').set("New text")
find("trix-editor", visible: false).set("my value")
find(:xpath, "//input[#id='annonce_ad_body_trix_input_annonce_ad']", visible: false).set "my value"
find(:xpath, "//*[#id='annonce_ad_body_trix_input_annonce_ad']", visible: false).set("value")
first('input#annonce_ad_body_trix_input_annonce_ad.class', visible: false).set("your value")
None of these have work for me. Someone have any idea how i can fill my body(with trix) in this case?

When using Capybara the rule when dealing with non-standard controls is to mimic what the user would do. In this case that would be click on the visible field (trix-editor element) and then type the contents wanted. You should never be trying to fill in non-visible elements and in fact the visible option should rarely (if ever) be used when testing an app (makes some sense if using Capybara for scraping). So in your case that should be
find('trix-editor').click.set('New text')
It would probably work without the click, but it doesn't hurt to more fully replicate the user. Since you've stated that something very similar to that doesn't work for you (but not provided the actual error) I have to assume you're not actually using a JS capable driver. Since trix is a JS driven editor you need to use a JS capable driver when testing it - https://github.com/teamcapybara/capybara#drivers .
The following basic ruby snippet shows Capybara filling in the demo trix editor at trix-editor.org
require 'capybara/dsl'
require 'selenium-webdriver'
sess = Capybara::Session.new(:selenium_chrome, nil)
sess.visit('https://trix-editor.org/')
editor = sess.find('trix-editor')
editor.click.set('My test text')
editor.assert_text('My test text')

I make a mistake, this work for me.
find(:xpath, "//*[#id='annonce_ad_body_trix_input_annonce_ad']", visible: false).set("some value here")

Related

Capybara not seeing updates to the DOM made by Javascript

I'm running Rails 5.x, with, Cucumber, Siteprism and Capybara through chromedriver. Most things work except..
I have a tiny bit of javascript that changes the class on an element in response to an event. But Capybara never sees the change. It only ever sees the class the element has when the page initially loaded.
Using Chrome, and debugging my Cucumber steps, I can see the element has the new class, but Capybara doesn't see it.
This must be an issue other people have encountered and solved, though I can't find the right subject title.
example coffeescript
$(document).on('focus', 'tbody#item-entry > tr > td > input', (e) ->
$(#).closest('tr').addClass('focused-row')
$(#).closest('td').addClass('focused-cell')
)
example html after the focus event has been triggered
<tr class="focused-row">
<td>ignore this </td>
</tr>
The purpose is to change the background colour of the row containing an input element that has focus. It works.
But Capybara, can't see the class, but it can see any classes added when the page is loaded. e.g.
expect(siteprism_stuff.root_element['class']).to match(/focused-row/)
Ignore the SitePrism stuff, that just gets the right element. root_element is the Capybara class for the dom node.
Now I know it's getting the right Capybara element because if I change my view to put stuff in the class for each row, then it sees that perfectly OK. What it can't see is the any new class added via Coffeescript. Although it's visible in the Chrome inspector, and changes the background color of the focused row as required.
You're specifying an "ends with" CSS attribute selector ($=)
input[class$='form-control']
which since the class attribute for the element you're interested in
<input type="search" class="form-control form-control-sm" placeholder="" aria-controls="universitiesTable">
doesn't end with 'form-control' is correctly not matching. You probably just want to use a normal CSS class selector input.form-control if continuing to do it the way you are. Any of the following options should find the search field and fill in the data you are trying to fill in.
fill_in 'Search:', with: string
fill_in type: 'search', with: string
find(:field, type: 'search').set(string)
find('input.form-control').set(string)
Note: Your question is still unclear as to whether you are seeing the class added in the inspector in test mode, and whether the line color is changing while the tests are running (or whether you're only seeing that in dev mode) - This answer assumes the JS is actually running in test mode and you're seeing the line color change while the tests are running.
You don't show how you're actually triggering the focus event but I'll assume you're clicking the element. The thing to understand when working with Capybara is that the browser works asynchronously, so when something like click has been done, the actions triggered by that click have not necessarily been done yet. Because of that, whenever doing any type of expectation with page elements you should always be using the matchers provided by Capybara rather than the basic matchers provided by RSpec. The Capybara provided matchers include waiting/retrying behavior to handle the asynchronous nature of dealing with the browser. In this case, assuming siteprism_stuff.root_element is the row element then you could be doing something like
expect(siteprism_stuff.root_element).to match_css('.focused-row')
or depending on exactly how your siteprism page objects are setup you could pass the class option to the siteprism existence checker
# `page_section` and `have_row` would need to be replaced with whatever is correct for your site prism page object
expect(page_section).to have_row(class: ['.focused-row'])

Capybara Select2 Help w/Firefox

For some reason im running into problems with Select2 and Firefox w/Geckodriver.
Select2 fields I used to be able to just say page.select 'Text', from: 'Label' however that no longer works I just get a Element <option> could not be scrolled into view (Despite being scrolled into view). Right now im doing something similar to this:
select2Fields = page.all('.select2-selection')
select2Fields[0].click
page.find('.select2-search__field').set('Text To Set')
within('.select2-results') do
page.find('li', text: 'Text To Click').click
end
It's ugly and doesn't fit with my Page Object Model method, since I have to sorta know which select2 field it is. It doesn't seem to be when finding it with a label.
Any ideas? It's very frustrating since it worked with Chrome but the latest chromedriver has issues with the newest capybara versions.
Not sure what you were using that you were able to ever use select with a select2 widget, it never should have worked, and the fact it did would have been a bug. The reason is the actual <select> element (which is what Capybaras select method works with) is non-visible on the page, and select2 replaces it with a JS driven widget. You need to do exactly what a user would do, which is click to make the widget show up then click on the <li> element which represents the correct entry. This can all be moved into a helper method and potentially some custom selectors which boils down to something like this
Capybara.add_selector(:select2) do
xpath do |locator, **options|
xpath = XPath.descendant(:select)
xpath = locate_field(xpath, locator, options)
xpath = xpath.next_sibling(:span)[XPath.attr(:class).contains_word('select2')][XPath.attr(:class).contains_word('select2-container')]
xpath
end
end
Capybara.add_selector(:select2_option) do
xpath do |locator|
# Use anywhere to escape from the current scope since select2 appends
# the choices to the end of the document
xpath = XPath.anywhere(:ul)[XPath.attr(:class).contains_word('select2-results__options')][XPath.attr(:id)]
xpath = xpath.descendant(:li)[XPath.attr(:role) == 'treeitem']
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
xpath
end
end
def select_from_select2(value, from: nil, **options)
select2 = if from
find(:select2, from, options.merge(visible: false))
else
select = find(:option, value, options).ancestor(:css, 'select', visible: false)
select.find(:xpath, XPath.next_sibling(:span)[XPath.attr(:class).contains_word('select2')][XPath.attr(:class).contains_word('select2-container')])
end
select2.click
find(:select2_option, value).click
end
That should let you call select_from_select2 just like you would call select and it will find the select2 widget associated with the given <select> element (hidden by select2) and choose the correct entry from it.
I had tested the Thomas´ answer but it doesn´t work for me. When Capybara click in the desired option, the select2 box close itself and set the 0 option. Finnaly, I made a walkaround as I check the option I want as selected and trigger the change.select2 event. I know that I dont really test the select2 box.
def self.select2 (page, datos)
page.execute_script("$('##{datos[:from]}').select2('open')")
if page.find(".select2-results li", text: datos[:texto]).click
page.execute_script("$('##{datos[:from]} option[value=\"#{datos[:valor]}\"]').prop('selected', true)")
page.execute_script("$('##{datos[:from]}').trigger('change.select2')")
end
page.find(:css, '#' + datos[:from]).value
end
As I keep my module Helper without include it in tests, I needed to include self in the name of the method and take 'page' from the capybara´test as parameter.
The variable 'datos' is a hash with the selector, the text of the option and its value.
As select2 box close when capybara click on it, I wrap the walkaround inside the if clause to be sure that some parts of the select2 box were working.
Finally, I returned the current value of the select to test it (really, it doesnt needed as I set the option with that value as 'selected')
I hope it would help anyone.

How to test file attachment on hidden input using capybara?

I have hidden input inside of label:
<label for="upload">
<input class="hidden" type="file" name="file[picture]">
</label>
When I click on the label, I attach a file and then confirm.
After that modal window pops up and I need to find appropriate div class.
How can I test this with the help of capybara?
Update: Capybara 2.12 added a make_visible option to attach_file so if using 2.12+ you can first try
attach_file('file[picture]', 'path/to/file.png', make_visible: true)
before directly using execute_script yourself
File inputs are a special case since they are so often hidden for styling reasons and use a system modal for interaction. Capybara makes it hard to fill in hidden fields on a page because users generally can't interact with them, so for file inputs normal practice is to use execute_script to make them visible and then fill them in.
execute_script("$('input[name=\"file[picture]\"]').removeClass('hidden')") # assumes you have jQuery available - if not change to valid JS for your environment
attach_file('file[picture]', 'path/to/file.png') # takes id, name or label text of field not a random selector
Using Capybara '2.7.1':
attach_file('file[picture]', 'path/to/file.png', visible: false)
You can do something along the lines of:
find('label[for=upload]').click
attach_file('input[name="file[picture]"]'), 'path/to/file.png')
within '.modal-popup' do
expect(page).to have_content '.divclass'
end

Unable to fill in input field using capybara-webkit

I have javascript feature specs that are working fine with selenium, but I'd like to switch to capybara-webkit for a number of reasons (CI, not popping up a browser window, etc).
I'm trying to switch over from selenium, but something is preventing my code from working correctly. I have the following helper:
def capybara_login(user)
visit login_path
fill_in "email", with: user.email
fill_in "password", with: user.password
click_button "Log in"
end
Here's my input field that is being outputted:
<input type="email" name="email" id="email" value="" class="form-control" placeholder="you#domain.com" autocomplete="off">
I've tested that user.email is not nil. For some reason when I use capybara-webkit, this fails and nothing is filled in (I'm checking by doing a save_and_open_page). However, this code works fine if I use regular capybara(non js) or selenium for js.
Am I missing something? Any ideas as to what could be preventing capybara webkit from filling in these fields?
EDIT: Thank you Tom for helping out. So save and open page will not show any input in those fields.
I was able to use save_and_open_screenshot to see that the page was actually just stuck on the loading image on the login screen. If I do a "sleep(1)" it logs in just fine.
Wondering what strategies people use to deal with this...waiting until the page loads to continue? Whats the reasoning behind not having Capybara do this by default? Or am I doing it wrong?
Capybara has no way of knowing when a page is "fully" loaded, since many pages will load asynchronously, dynamically as needed, etc. In fact Capybara is showing you an issue with your pages usability since a user could technically interact with elements before they're actually usable - in this case Capybara is probably quicker than any user could be, but still... Because of this you need to determine what the widgets you're using do on the page and what changes they make - For instance does a class get set on the body element when a library has finished processing the page, does an input field have a class/attribute added when it's been augmented, etc. Once you've determined that you can tell Capybara to do something like
expect(page).to have_selector('body.class_added_when_ready')
to make sure the page is fully interactable
This was totally a fluke with some javascript running on my end. At some point I thought it was a good idea to allow a 300ms delay for ladda loaders to fully animate before submitting a form. This was causing capybara to choke. I was able to isolate this problem to that specific code.
Thanks for your help, Tom!

Is it possible to interact with hidden elements with capybara?

I have a file field that has opacity: 0 and is overlaping a fake button. Its a common css technic to fake a sort of "Upload button" that displays consistently across different browsers.
Capybara doesn't allows me to call attach_file on that input. The error is Selenium::WebDriver::Error::ElementNotVisibleError: Element is not currently visible and so may not be interacted with.
Anybody knows any way to force capybara to interact with invisible elements?
The answer is still unanswered, but I've found a work around. Nothing intelligent, just make visible the element with a simple script
page.execute_script %Q{
$('#photos').css({opacity: 1, transform: 'none'});
}
I post it for the record.
You can interact with hidden elements using the visible: false property in Capybara.
If you want to click on hidden element use:
find("#photos", visible: false).click
Don't use click_button('#photo') directly
The author of Capybara recommends setting Capybara.ignore_hidden_elements immediately prior to needing to see the invisible element, and resetting it afterwards:
Capybara.ignore_hidden_elements = false
click_button 'my invisible button'
Capybara.ignore_hidden_elements = true
In general interacting with non-visible elements should not be possible when using Capybara (you can find them using the visible: false/hidden option in most finders but not actually do anything to them). However, the file input is a special case because of how common it is to hide the element and, due to security restrictions, no other way to actually add a file by interacting with the pages visible elements. Because of this attach_file has a make_visible option which can be used to have Capybara make the element visible, attach the file, and then reset the CSS to the original setting.
attach_file('photos', file_path, make_visible: true)
I ended up resolving it a different route.
execute_script() was giving me a hard time (it would freeze test execution on FireFox), so this is what I did:
I already had an appropriate javascript file. I appended the following
<% if ENV["RAILS_ENV"] == "test" %>
$('#photos').show()
<% end %>
I also had to append .erb to my javascript file for proper Rails asset handling.
And in my test file, I was already setting ENV["RAILS_ENV"] = "test"
This way I could just dumb down the UI for test, and yet maintain the look and feel for production.
Miquel, thanks for workaraund.
I have similar issue for interacting with hidden file input on C# binding for Selenium Webdriver 2.35 and Firefox 24. To make file selection working did similar trick:
((IJavaScriptExecutor)webdriver).ExecuteScript("$('#fileUploadInput').css({opacity: 1, transform: 'none'});");
IWebElement e = webdriver.FindElement(By.CssSelector("input#fileUploadInput")));
e.SendKeys("c:\\temp\\inputfile.txt");
I've done it this way with elements that has the CSS style display:none; set:
page.execute_script("$('.all-hidden-elements').show();");
all('.all-hidden-elements').first.click
If the hidden element is nested in a visible parent element (e.g. a hidden input inside a visible label), you can click on the parent instead. If you still want to find the input by ID, you can traverse to the parent like so:
find('#hidden_input').find(:xpath, '..').click

Resources