I am having trouble executing a particular javascript snippet using Watir WebDriver's browser.execute_script command:
Here is the Javascript in question:
var bodyFrame = document.getElementsByName("BodyFrame")[0];
var bodyFrameDoc = bodyFrame.contentDocument || bodyFrame.contentWindow.document;
var mainFrame = bodyFrameDoc.getElementById("MainFrame");
var mainFrameDoc = mainFrame.contentDocument || mainFrame.contentWindow.document;
var row = mainFrameDoc.getElementById("DETAIL_1");
var rowNodes = row.childNodes;
var index;
for (index = 0; index < rowNodes.length; index++) {
if (rowNodes[index].firstChild.id === 'ID1') {
rowNodes[index].firstChild.value = 'ACCRUL';
}
if (rowNodes[index].firstChild.id === 'ID2') {
rowNodes[index].firstChild.value = 'OP';
}
if (rowNodes[index].firstChild.id === 'ID3') {
rowNodes[index].firstChild.value = 'Z';
}
if (rowNodes[index].firstChild.id === 'ID4') {
rowNodes[index].firstChild.value = 'FIRST';
}
if (rowNodes[index].firstChild.id === 'ID5') {
rowNodes[index].firstChild.value = 'AUT';
}
if (rowNodes[index].firstChild.id === 'ID6') {
rowNodes[index].firstChild.value = 'AU1000';
}
if (rowNodes[index].firstChild.id === 'ID7') {
rowNodes[index].firstChild.value = 'm tax m entity m jc';
}
if (rowNodes[index].firstChild.id === 'ID8') {
rowNodes[index].firstChild.value = 'MR5001100';
}
if (rowNodes[index].firstChild.id === 'ID9') {
rowNodes[index].firstChild.value = 'T';
}
if (rowNodes[index].firstChild.id === 'ID10') {
rowNodes[index].firstChild.value = '453.36';
}
}
Why must I use such an atrocity? Well, because the product I am developing tests for is that bad. Each cell uses the same id attribute, so I cannot directly zone in on the object in the document. To make matters worse, the document is in two frames deep. Frameception.
Using the obvious solution (using watir-webdriver's API) is not feasible, since the task of filling in 32 grid rows is taking one-half!! of an hour.
I can get the Javascript above to run in the browser's developer tools. (The only browsers supported by this product are IE8, 9, in compatibility mode.) I don't have access to jQuery or any fancy javascript library. Using this javascript fills in the rows almost instantly.
When I run this using the browser.execute_script command, I get a
Selenium::WebDriver::Error::JavascriptError: JavaScript error
Curiously enough, if I just used
return document.getElementsByName("BodyFrame")[0];
Then I get an error with the Net::HTTP library, which is used by Selenium somewhere on the stack for sending commands back and forth to the browser. (More specifically: ERRNO::ERRCONNREFUSED)
Help?
EDIT: Here's the direct script that I am using to execute the code (and generate the small bit of Javascript):
def quick_fill(data = {})
reference_element.focus;
quick_filler = ->(element_id, value) {
"if (rowNodes[index].firstChild.id === '#{element_id}') {
rowNodes[index].firstChild.value = '#{value}';
}"
}
row_id = row.attribute(:id)
element_ids = Hash[data.keys.map { |key| [key, send("#{key}_element").attribute(:id)] }]
javascript = <<-JAVASCRIPT
var bodyFrame = document.getElementsByName("BodyFrame")[0];
var bodyFrameDoc = bodyFrame.contentDocument || bodyFrame.contentWindow.document;
var mainFrame = bodyFrameDoc.getElementById("MainFrame");
var mainFrameDoc = mainFrame.contentDocument || mainFrame.contentWindow.document;
var row = mainFrameDoc.getElementById("DETAIL_1");
var rowNodes = row.childNodes;
var index;
for (index = 0; index < rowNodes.length; index += 1) {
#{element_ids.map { |method, element_id| quick_filler.call(element_id, data[method]) }.join("\n") }
}
JAVASCRIPT
puts javascript
sleep(0.500)
browser.execute_script(javascript)
end
The data argument to this method is a hash of key-value pairs, where keys are the names of the methods provided to the page object accessors, and the values are the values I'm trying to fill in. As part of the intermediate process, I get the id element from the page object element (which is provided as the identifier each element).
There is a bit of a peculiar structure going on with how the page class is set up: Here's the basic idea:
class MyPage
include PageObject
table(:some_table, ...)
def add_line_item(data = {})
GridRow.new(some_table_element.last_row, browser).quick_fill(data)
end
class GridRow
include PageObject
#page object accessors
text_field(:my_field) { row.text_field_element(id: 'ID1') }
#etc
def quick_fill(data = {})
#see above
end
def initialize(row, browser, visit = false)
@row = row
super(browser, visit)
end
end
end
So the example call will be (from a cucumber test)
on_page(MyPage) do |page|
page.add_line_item(:my_detail => 'myValue')
end
In addition to this, there's the unfortunate problem that there are several 'attach_window' calls needed to get to this page (due to the page sitting in a modal dialog of sorts). The issue may be largely related to this interaction. This is the only thing that I can think of that will cause a problem.
The point of this SO question is to hopefully just have a second set of eyes who can rule out any obvious errors. I have another workaround that I'm planning on employing.