How do you automatically confirm a modal in ruby using page-object? - ruby

I have a standard rails application with a delete link. This delete link comes up with a browser popup modal (using rails confirm option).
I am currently attempting to test the delete function with Cucumber, Selenium-Webdriver (or Watir-Webdriver, still haven't decided), and the page-object gem.
Once the modal has been triggered, anything I do on the page gives me the following error:
Modal dialog present (Selenium::WebDriver::Error::UnhandledAlertError)
I have been looking all over, but cannot find a way to handle this condition. If possible, I would like to continue using the PageFactory module in the page-object gem.
How can I dismiss/accept the modal?

I figured out a way to do this, but haven't decided upon the exact implementation.
In Javascript you can overwrite any function, which means you can overwrite the confirm
This means that you can run the following code to disable any popups.
def disable_popups
# don't return anything for alert
browser.execute_script("window.alert = function() {}")
# return some string for prompt to simulate user entering it
browser.execute_script("window.prompt = function() {return 'my name'}")
# return null for prompt to simulate clicking Cancel
browser.execute_script("window.prompt = function() {return null}")
# return true for confirm to simulate clicking OK
browser.execute_script("window.confirm = function() {return true}")
# return false for confirm to simulate clicking Cancel
browser.execute_script("window.confirm = function() {return false}")
end
If you put this inside the initalize_page function of a page-object then the dialogs are automatically removed.
def initialize_page
disable_popups
end
Or you could do it right before the pop is triggered
def delete
disable_popups
delete_link # => clicks the link
end
References:
Testing Webpages with Javascript Popups Correctly
Dismissing Pesky Javascript Dialogs with Watir

The page object gem has methods to handle javascript popups - see the original page-object gem post. In your case, I believe you want the confirm method::
(String) confirm(response, frame = nil, &block)
Override the normal confirm popup so it does not occurr.
Examples:
message = #popup.confirm(true) do
#page.button_that_causes_confirm
end
Parameters:
what (bool) — response you want to return back from the confirm popup
frame (defaults to: nil) — optional parameter used when the confirm is nested within a frame
block — a block that has the call that will cause the confirm to display
Returns:
(String) — the message that was prompted in the confirm
There is similar for the alert and prompt popups.
In your page-object, I would define:
class MyPage
link(:delete_link, :id=>'delete')
def delete()
confirm(true){ delete_link }
end
end
Then when you call page.delete, it will click the link and confirm the popup.

Related

Applescript: Truncated feed URL

I'm running into errors with this simple script. It's a validator to validate RSS feed with multiple validation sites using Safari. Everything works fine as long as the feed does not contain special characters or anything after the = sign.
The script should validate the feed that was copied to the clipboard.
For example, this feed works fine: http://thefirst.libsyn.com/rss
This feed gets truncated after ?id: https://www.npr.org/rss/podcast.php?id=510298
This is only happening on the Podbase validator site.
If I could get the script to click the Validate and Go buttons, that would be amazing, but this is pretty basic…just stuck as to why the feed is getting truncated.
set feed_url to the clipboard as string
set the podbaseurl to "http://podba.se/validate/?url=" & feed_url
set the feedvalidatorurl to "http://feedvalidator.org/check.cgi?url=" & feed_url
set the castfeedurl to "http://castfeedvalidator.com/?url=" & feed_url
tell application "Safari"
make new document
open location podbaseurl
open location feedvalidatorurl
open location castfeedurl
end tell
The problem is that https://podba.se/validate only makes it look like a single GET request is enough, whereas clicking the Go button interactively performs many individual GET requests behind the scenes whose results are pieced together on the current page (and the URL is then modified to include the submitted feed URL).
In other words: even solving the (odd) truncation problem wouldn't be enough.
Therefore, your best bet is indeed to simulate an interactive submission of a feed URL, which requires filling an input box with the feed URL and pressing the submission button.
Interactive submission must be simulated for the http://castfeedvalidator.com site as well, where pressing the submission button is sufficient, however.
(As you report, even though inspecting the request sent by the submission button shows that a variation of your URL - which only prepares the feed URL for submission - can be used to instantly submit it, doing so doesn't render the results correctly (missing styles)).
The following code implements both suggestions (the simulated-interaction approach was adapted from this answer of mine):
# Sample value
set the clipboard to "https://www.npr.org/rss/podcast.php?id=510298"
set feed_url to the clipboard as string
# Base URL only; the feed URL will be submitted by simulated interaction below.
set the podbaseurl to "http://podba.se/validate/"
set the feedvalidatorurl to "http://feedvalidator.org/check.cgi?url=" & feed_url
set the castfeedurl to "http://castfeedvalidator.com/?url=" & feed_url
tell application "Safari"
activate
set newDoc to make new document
set newWin to front window
# Simulate interactive submission of feed_url at podbaseurl.
set URL of newDoc to podbaseurl
my submitUrl(newDoc, podbaseurl, feed_url)
# The feedvalidateorurl can be opened normally.
open location feedvalidatorurl
# Simulate interactive submission of feed_url at castfeedurl.
set newTab to make new tab in newWin
set URL of newTab to castfeedurl
my submitUrl(newTab, castfeedurl, feed_url)
end tell
on submitUrl(doc, target_url, feed_url)
# Synthesize the JavaScript command.
set jsCode to "
(function () { // Use a closure to avoid polluting the global namespace.
function doIt(t) { // Helper function
if (doIt.done) return; // Already successfully called? Ignore.
try {
// Perform the desired action - may fail if invoked too early.
if (/^http:\\/\\/podba.se/.test('" & target_url & "')) {
document.querySelector('#url-input').value = '" & feed_url & "';
document.querySelector('#url-input').dispatchEvent(new Event('input'));
setTimeout(function() { document.querySelector('#go-button').click(); }, 0);
} else { // http://feedvalidator.org
document.querySelector('.btn-subscribe').click()
}
} catch(e) {
return; // Return without setting the success 'flag'.
}
doIt.done=true; // Set success 'flag' as a property on this function object.
};
// Attach a listener to the window's load event for invoking the helper function then.
window.addEventListener('load', doIt);
// If the document signals readiness -- which may still be too early, we can't know --
// also try to invoke the helper function *directly*.
if (document.readyState === 'complete') { doIt(); }
})();
"
# Execute the JavaScript command in the target page.
tell application "Safari"
tell doc to do JavaScript jsCode
end tell
end submitUrl

Mocha, chai, webdriverio testing: How can I click out of a modal pop up dialog and continue on with the regular web flow

For a web page I am trying to write a test for, a modal appears sometimes. I'll like to write the test case such that if that does happen, I'll click the x to exit out of it and continue on with the test, else just do the test.
So far I have this:
client
.init()
.url('https://www.mywebsite.com/')
.isExisting('#simple-modal.auth-modal.open.visible').then(function(value){
// Select the X button: would like to client.click('#xbutton')
//when modal finishes loading
})
.click('#mybutton')
.getText('.browse-header').then(function(value) {
console.log('Title is : ' + value);
})
.end();
It seems that if I try to split the client object into 2 sections such that I init() and declare the url, then add a semicolon, then figure if the modal exist, add semicolon, then complete the rest of the test, the client doesn't complete the test. What can I do?

SketchUp API: How to add a check box to a menu item

I don't see it anywhere in the Ruby API documentation but just in case I'm missing something...
I'm writing a plugin for SketchUp and I'm trying to add some options to the menu bar. One of my options would work best as a checkbox, but right now I have to have two separate buttons. Is there a way to create a checkbox menu item with the Ruby API?
Here's what I had to do instead:
foo = true
UI.menu("Plugins").add_item("Turn foo_option on") { #foo = true }
UI.menu("Plugins").add_item("Turn foo_option off") { #foo = false }
...and then I just use foo to change the options. Is there a cleaner way to do this?
SketchUp can have check marks in menu items. Both menu items and commands can have a validation proc. The documentation for set_validation_proc gives this example:
plugins_menu = UI.menu("Plugins")
item = plugins_menu.add_item("Test") { UI.messagebox "My Test Item"}
status = plugins_menu.set_validation_proc(item) {
if Sketchup.is_pro?
MF_ENABLED
else
MF_GRAYED
end
}
Although for checkmarks you would use the constants MF_CHECKED and MF_UNCHECKED
http://www.sketchup.com/intl/en/developer/docs/ourdoc/menu#set_validation_proc
http://www.sketchup.com/intl/en/developer/docs/ourdoc/command#set_validation_proc
I have not seen a checkbox menu item created from an extensions before, but I'm a beginning user so that's maybe why.
An other approach would be to do it like this:
unless file_loaded?(__FILE__)
plugin_menu = UI.menu("Plugin")
option_menu = plugin_menu.add_submenu("NameOfOption")
option_menu.add_item("OptionA"){ }
option_menu.add_item("OptionB"){ }
file_loaded(__FILE__)
end
The file_loaded?(_ FILE _) makes sure the menu only is created once, instead of every time you load your script. I hope this is helpfull. Maybe some experts now a way to create a checkbox menu.

Extension: How to intercept AJAX responses?

Since extensions can not access unsafeWindow, like Firefox can, to hook into DOM scripts am I looking for other ideas so I come to SO for help!
How about using some code to inject into DOM and sending the intercepted response to a background page, which then does some initial processing before calling a content script for final processing. When done, it answers to the background with a modified response, or the original (it depends), and the background page sends the response back to DOM which handles it to the DOM script response function.
There is just one problem with this, a background page cant communicate with the DOM.
I did a small test with injecting some code, where I output something to the console and an alert. The result wasnt good, as the alert fired but the console was empty - not even an error, which makes me wonder - what console received the output ?
function injectCode(fn){ // Executing an anonymous script
var script = document.createElement('script');
script.type = 'application/javascript';
script.textContent = '(' + fn + ')();';
document.documentElement.appendChild(script); // run the script
document.documentElement.removeChild(script); // clean up
}
var code = function(){
console.log('dom',window);
alert('code injected');
}
injectCode(code);
I also tried addEventListener, with DOMAttrModified DOMSubtreeModified DOMNodeInserted, on DOM elements that change when the DOM ajax response is fully parsed but all failed to fire.
Am I trying to do the impossible, by any means ?
Before continuing, make sure that you know the differences between the script contexts in an extension.
To inject a script from the background page, you have to execute a Content script, which on his turn injects the script as mentioned in your question / here.
Examples (using chrome.tabs.executeScript):
// null = current active tab
// Simple code, background:
chrome.tabs.executeScript(null, {
code: [
'var s = document.createElement("script");',
's.textContent = "console.log(window);";',
'(document.head||document.documentElement).appendChild(s);',
's.parentNode.removeChild(s);'
].join('\n')
});
I can imagine that this method is not doable for a big chuck of code. For a set of pre-defined scripts, you can then use two scripts: the code itself, and a helper script:
// config.js
var fn_code = function() {
console.log(window); ....
};
// helper.js
var s = document.createElement('script');
s.textContent = '(' + fn_code + ')();';
(document.head||document.documentElement).appendChild(s);
s.parentNode.removeChild(s);
// Background:
chrome.tabs.executeScript(null, {file: 'config.js'}, function() {
chrome.tabs.executeScript(null, {file: 'helper.js'});
});
Note: I did not directly link to "config.js", because that complicates the use when using manifest version 2, see "web_accessible_resources".
The previous method only shows how to execute code in one direction (background -> page). If there's a need to activate a background's function from the injected script, you have to define and listen to a custom event handler. See this answer + demo.
Because the code is injected, thus runs in the scope of the page, you have to check the console at the page.
When chrome.tabs.executeScript fails to execute the Content script (eg. because the extension does not have the permission to access a certain page), an error is logged at the console in the background page. This console can be accessed by following these steps.

Unable to remove element loaded by AJAX

I am trying to remove an element on AJAX success which was loaded and attached to the document during a previous AJAX call.
My code looks something like this:
$("#jobs-table-body").on("click", ".one-rc-button", function() {
var ctx = $.parseJSON($(this).siblings(".context").html());
$("#one-rc-candidate-id").val(ctx.candidateId);
$("#one-rc-job-id").val(ctx.jobId);
var loader = $("#wrapper").loader();
$.post($("#one-rc-form").attr("action"), $("#one-rc-form").serialize(), function(result) {
loader.remove();
if(result.success) {
// This works and returns 1
alert($("#candidate-row-" + result.rejectedCandidateId).length);
// This doesn't seem to be doing anything
$("#candidate-row-" + result.rejectedCandidateId).remove();
} else {
//$("#one-jc-messages").html(result.error);
}
});
});
The elements .one-rc-button and #candidate-row-<candidateId> were loaded by a previous AJAX call and they are attached to the document as I can very well see them on my page.
Now, on click of the previously generated .one-rc-button, I trigger a second AJAX call (which works fine) and on result.success, I want to delete the #candidate-row-<candidateId> (which is within the previously generated parent element).
The alert works and returns 1. So I know for sure that the selector is fine and it is matching one unique element.
What I don't understand is why it is unable to remove the element from the page.
Observations
I use Firefox 10.0.2 where this problem is reproducible.
On IE 8, it works (element gets removed)
On debugging the script on Firebug, I can verify that I have got a handle to the right eleemnt.
Try using FireBug to set a breakpoint on that line so you can see exactly what it's getting from that selector. Ideally break up the statement first, like this:
var unwantedDiv = $("#candidate-row-" + result.rejectedCandidateId);
unwantedDiv.remove(); // <-- Set a breakpoint on this line
You can then look at the unwantedDiv variable in the watch pane on the right of the firebug debugger and see what it is, what methods it has/has not got etc. I would assume that you are not getting back exactly what you think you are, possibly because of how you attached the div after the previous AJAX call. More information about JavaScript debugging with FireBug here.
Another option is to turn on strict warnings in the firebug console and see if you get any 'undefined method' errors, which don't stop the show on FireFox, but just bounce you out of that function. Do you get an error in IE?
Solved it by a really ugly workaround. I am still not sure what causes this behaviour.
if(result.success) {
var removeThis = $("#candidate-row-" + result.rejectedCandidateId);
removeThis.remove();
removeThis = $("#candidate-row-" + result.rejectedCandidateId);
if(removeThis.length != 0) {
removeThis.remove();
}
}
Now it works on both Firefox and IE.

Resources