I'm switching from the Poltergeist to Cuprite driver for Capybara.
I have Javascript code which sets the message for a confirmation modal which I want to check in my feature specs.
The javascript is confirm("....").
With Poltergiest I could do page.driver.browser.modal_message.
This is unsupported by Cuprite, it there another way?
Capybaras accept_confirm (which cuprite supports) returns the string from the system modal:
text = accept_confirm do
# ... the actions that triggers the modal to appear
end
Or you can pass a string to accept_confirm to have it verify the string:
accept_confirm('the text to check') do
# ... the actions that triggers the modal to appear
end
Looking at the ferrum driver which Cuprite uses under the hood I can see it is possible register a hook for a dialog appearing.
message = nil
page.driver.browser.on(:dialog) do |dialog|
message = dialog.message
end
accept_confirm do
click_on progress_tab.name
expect(message).to eq text('...')
end
It isn't pretty.
Related
I am trying to attach a file in my spec, but the capybara attach_file method does not work for me in poltergeist (it returns true but nothing gets attached). It does work in Selenium, but for other reasons I want to use poltergeist. I know that the phantomjs uploadFile method (http://phantomjs.org/api/webpage/method/upload-file.html) simulates a user interacting with the file dialog which is what I think I need to do. I cannot figure out how to use it though in my Capybara spec.
I am trying to use
def drop_files(files, css_selector)
js_script = 'fileList = Array(); '
files.count.times do |index|
# Generate a fake input selector
page.execute_script("if ($('#seleniumUpload#{index}').length == 0) { " \
"seleniumUpload#{index} = window.$('<input/>')" \
".attr({id: 'seleniumUpload#{index}', type:'file'})" \
".appendTo('body'); }")
# Attach file to the fake input selector through Capybara
attach_file("seleniumUpload#{index}", files[index], visible: false)
# Build up the fake js event
#
js_script << "fileList.push(seleniumUpload#{index}.get(0).files[0]); "
end
js_script << "e = $.Event('drop'); "
js_script << "e.dataTransfer = { files : fileList }; "
js_script << "$('#{css_selector}').trigger(e);"
# Trigger the fake drop event
page.execute_script(js_script)
end
which comes from https://github.com/teampoltergeist/poltergeist/issues/342 and works in selenium. The user that posted that says he has it working in poltergeist though.
It turns out file uploading is currently broken on Phantomjs 2.0. I downgraded v1.9.8 and attach_file works now.
When I bind a function to a button inside an accordion nothing happens upon clicking it. I have no idea what i'm doing wrong. :( Any thoughts?
def printTest():
print "The button worked!"
accord = Accordion(anim_duration=1.5, orientation='vertical')
specialButton = Button(text="click me", font_size='20sp', text_size=(1100, None), halign="center")
specialButton.bind(on_press=printTest():
item = AccordionItem(title="Hello World")
item.add_widget(specialButton)
accord.add_widget(item)
specialButton.bind(on_press=printTest():
This isn't valid syntax, is the colon a typo?
Either way, the problem is that you are calling printTest, not passing it as an argument.
Instead try
def printTest(*args):
print "The button worked!"
...and...
specialButton.bind(on_press=printTest)
The *args is important because the binding automatically passes some arguments.
I covered this in more detail here.
I have a web page that opens a div when you click a button. This div allows you to drag a file from your desktop onto its area; the file then gets uploaded to the server. I'm working with the Ruby implementation of Selenium.
By using the JavaScript debugger in Firefox, I can see that an event called "drop" is being passed to some JavaScript code "handleFileDrop(event)". I presume that if I were to create a mock event and fire it somehow that I could trigger this code.
If found an interesting article that seemed to point me in a promising direction, but I'm still short of figuring it all out. I am able to pass JavaScript to the page using Selenium's get_eval method. Calling methods using this.browserbot is getting me the elements I need.
So:
How do I build the file object that
needs to be part of the mock drop
event?
How do I fire the drop event
such that it gets picked up as if I
had dropped a file in the div?
I post an RSpec test that simulate files drag and drop using Selenium webdriver.
It use jQuery to make and trigger a fake 'drop' event.
This code simulate drag and drop of a single file. For sake of simplicity I've stripped code that allow multiple files dropping. Tell me if you need it.
describe "when user drop files", :js => true do
before do
page.execute_script("seleniumUpload = window.$('<input/>').attr({id: 'seleniumUpload', type:'file'}).appendTo('body');")
attach_file('seleniumUpload', Rails.root + 'spec/support/pdffile/pdfTest.pdf')
# Trigger the drop event
page.execute_script("e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : seleniumUpload.get(0).files } }; $('#fileDropArea').trigger(e);")
end
it "should ..." do
should have_content '...'
end
P.S.: remember to replace #fileDropArea with ID of your drop area.
P.P.S: don't use evaluate_script in place of execute_script, otherwise selenium get stuck evaluating complex jQuery objects!
UPDATE:
I've write a method you can reuse and do the stuff written above.
def drop_files files, drop_area_id
js_script = "fileList = Array();"
files.count.times do |i|
# Generate a fake input selector
page.execute_script("if ($('#seleniumUpload#{i}').length == 0) { seleniumUpload#{i} = window.$('<input/>').attr({id: 'seleniumUpload#{i}', type:'file'}).appendTo('body'); }")
# Attach file to the fake input selector through Capybara
attach_file("seleniumUpload#{i}", files[i])
# Build up the fake js event
js_script = "#{js_script} fileList.push(seleniumUpload#{i}.get(0).files[0]);"
end
# Trigger the fake drop event
page.execute_script("#{js_script} e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : fileList } }; $('##{drop_area_id}').trigger(e);")
end
Usage:
describe "when user drop files", :js => true do
before do
files = [ Rails.root + 'spec/support/pdffile/pdfTest1.pdf',
Rails.root + 'spec/support/pdffile/pdfTest2.pdf',
Rails.root + 'spec/support/pdffile/pdfTest3.pdf' ]
drop_files files, 'fileDropArea'
end
it "should ..." do
should have_content '...'
end
end
As #Shmoopy asked for it, here's a C# translation of the code provided by #micred
private void DropImage(string dropBoxId, string filePath)
{
var javascriptDriver = this.Driver as IJavaScriptExecutor;
var inputId = dropBoxId + "FileUpload";
// append input to HTML to add file path
javascriptDriver.ExecuteScript(inputId + " = window.$('<input id=\"" + inputId + "\"/>').attr({type:'file'}).appendTo('body');");
this.Driver.FindElement(By.Id(inputId)).SendKeys(filePath);
// fire mock event pointing to inserted file path
javascriptDriver.ExecuteScript("e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : " + inputId + ".get(0).files } }; $('#" + dropBoxId + "').trigger(e);");
}
You can use Blueduck Sda (http://sda.blueducktesting.com)
Is an OSS that has implemented ALL selenium functions (It works with selenium RC) but it allows you to automate Windows actions. So you can test web, and interact with the OS.
So you can make your test, and then, just tell the mouse to click on the element and drop it where you want!
Nice testing!
Note: you should also add
e.originalEvent.dataTransfer.types = [ 'Files' ];
I would like to write a request spec that verifies the loading and execution of javascript on a given page without any errors.
I know I can add something to the DOM at the end of my JavaScript file and assert the presence of this content, but that feels like a hack and forces me to pollute my code for testing reasons. I'd prefer to do something along the lines of.
visit some_path
page.should succesfully_run_javascript
You can achieve this with the following piece of code in your tests.
page.driver.console_messages.each { |error| puts error }
expect(page.driver.console_messages.length).to eq(0)
The first line prints out an errors, handy for seeing what's going on. The second line causes the test to fail.
One way that's potentially not very polluting is to collect any errors by adding something like this to head (keep in mind this will override any onerror handler you may already have):
<script>
window.errors = [];
window.onerror = function(error, url, line) {
window.errors.push(error);
};
</script>
You could then do something like:
page_errors = page.evaluate_script('window.errors')
and assert on the array being empty. You could output the errors otherwise...
Note: It's important to add the scriptlet to head (potentially as the first scriptlet/script tag) as it needs to be one the first executed scripts.
A problem with using page.driver.console_messages is that it doesn't clear between tests. So if you want to only assert that there are no errors on a particular test the spec might fail due to a previous test.
You can scope these errors to a particular spec by saving the last console.log timestamp.
Helper method:
def assert_no_js_errors
last_timestamp = page.driver.browser.manage.logs.get(:browser)
.map(&:timestamp)
.last || 0
yield
errors = page.driver.browser.manage.logs.get(:browser)
.reject { |e| e.timestamp > last_timestamp }
.reject { |e| e.level == 'WARNING' }
assert errors.length.zero?, "Expected no js errors, but these errors where found: #{errors.join(', ')}"
end
And then use it like:
def test_somthing_without_js_errors
assert_no_js_errors do
# TODO: Write test
end
end
I need to send some key-presses to a web app in an integration test that uses Capybara and WebKit. Using Selenium (WebDriver and Firefox) I can achieve it like this:
find("#element_id").native.send_keys :tab
but WebKit's native element node doesn't have a send_keys method. Actually native in WebKit returned a string containing a number. Is there another way to send keystrokes to WebKit? Maybe even some workaround using JavaScript/jQuery?
I've been trying to implement Marc's answer without any success, but I found some help from a similar question: capybara: fill in form field value with terminating enter key. And apparently there was a pull request from capybara that seems to address this issue.
What worked for me was:
before { fill_in "some_field_id", with: "\t" }
My example erases the text in the field and then presses Tab. To fill in a field with 'foobar', replace "\t" with "foobar\t". You can also use "\n" for the Enter key.
For your example, you could use:
find("#element_id").set("\t")
This worked for me with Poltergeist, to trigger the asterisk key:
find("body").native.send_key("*")
I had no luck with the other solutions; not even Syn.
This was to trigger an angular-hotkeys event.
You can do it like that:
keypress_script = "var e = $.Event('keydown', { keyCode: #{keycode} }); $('body').trigger(e);"
page.driver.browser.execute_script(keypress_script)
Now since Capybara-webkit 1.9.0 you can send key presses like enter and others using send_keys:
find("textarea#comment").send_keys(:enter)
Source: https://github.com/thoughtbot/capybara-webkit/issues/191#issuecomment-228758761
Capybara API Docs: http://www.rubydoc.info/github/jnicklas/capybara/Capybara%2FNode%2FElement%3Asend_keys
I ended up doing the following:
Capybara.current_driver = Capybara.javascript_driver
keypress_script = "$('input#my_field').val('some string').keydown();"
page.driver.browser.execute_script(keypress_script)
I discovered in Chrome, testing my JavaScript, that actually creating an $.Event with keyCode or charCode and then triggering that on my input field didn't put the characters in the input. I was testing autocompletion which required a few characters be in the input field, and it would start the autocompletion on keydown. So I set the input value manually with val, then trigger keydown to cause the autocompletion script to start.
For simple cases, triggering a keypress event in JS will work:
def press(code)
page.execute_script("$('#my-input').trigger($.Event('keypress', {keyCode: #{code}}))")
end
For a more general and robust answer, use this great library that goes through the trouble of triggering the right events (i.e. keydown, then keypress and finally keyup).
def type(string)
page.execute_script("Syn.click({}, 'my-input').wait().type(#{string.to_json})")
end
A more complex example can be found here
Here is my solution, which works with capybara 2.1.0:
fill_in('token-input-machine_tag_list', :with => 'new tag name')
page.evaluate_script("var e = $.Event('keydown', { keyCode: 13 }); $('#token-input-machine_tag_list').trigger(e);") # Press enter
Please, note, that in new capybara you have to use page.evaluate_script.
For Capybara Webkit, this is the solution I used:
def press_enter(input)
script = "var e = jQuery.Event('keypress');"
script += "e.which = 13;"
script += "$('#{input}').trigger(e);"
page.execute_script(script);
end
Then I use it cleanly in my test like:
press_enter("textarea#comment")