Skip to content

Commit

Permalink
EranMes: The Internet Explorer driver can now return an array of obje…
Browse files Browse the repository at this point in the history
…cts from executeJavascript.

r8461
  • Loading branch information
eranmes committed Feb 26, 2010
1 parent 6dbb9a8 commit 7baaede
Show file tree
Hide file tree
Showing 17 changed files with 373 additions and 108 deletions.
4 changes: 2 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,9 @@ java_test(:name => "webdriver-firefox-test",
])

java_test(:name => "webdriver-single-testsuite",
:srcs => [ "common/test/java/org/openqa/selenium/SingleTestSuite.java" ],
:srcs => [ "common/test/java/org/openqa/selenium/SingleTestSuite.java"],
:deps => [
:'webdriver-firefox',
:'webdriver-ie',
:'webdriver-common-test',
])

Expand Down
25 changes: 22 additions & 3 deletions common/test/java/org/openqa/selenium/ExecutingJavascriptTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,25 @@ public void testShouldBeAbleToExecuteSimpleJavascriptAndReturnABoolean() {

@SuppressWarnings("unchecked")
@JavascriptEnabled
@Ignore({IE, SELENESE, IPHONE})
public void testShouldBeAbleToExecuteSimpleJavascriptAndAStringsArray() {
if (!(driver instanceof JavascriptExecutor)) {
return;
}

driver.get(javascriptPage);
List<Object> expectedResult = new ArrayList<Object>();
expectedResult.add("zero");
expectedResult.add("one");
expectedResult.add("two");
Object result = ((JavascriptExecutor) driver).executeScript(
"return ['zero', 'one', 'two'];");

ExecutingJavascriptTest.compareLists(expectedResult, (List<Object>) result);
}

@SuppressWarnings("unchecked")
@JavascriptEnabled
@Ignore({SELENESE, IPHONE})
public void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray() {
if (!(driver instanceof JavascriptExecutor)) {
return;
Expand All @@ -108,12 +126,13 @@ public void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray() {
subList.add(false);
expectedResult.add(subList);
Object result = executeScript("return ['zero', [true, false]];");
assertNotNull(result);
assertTrue("result was: " + result + " (" + result.getClass() + ")", result instanceof List);
List<Object> list = (List<Object>) result;
assertTrue(compareLists(expectedResult, list));
}

private boolean compareLists(List<?> first, List<?> second) {
private static boolean compareLists(List<?> first, List<?> second) {
if (first.size() != second.size()) {
return false;
}
Expand Down Expand Up @@ -374,7 +393,7 @@ public void testShouldBeAbleToExecuteABigChunkOfJavascriptCode() throws IOExcept

@SuppressWarnings("unchecked")
@JavascriptEnabled
@Ignore({SELENESE, IE, CHROME, REMOTE, IPHONE})
@Ignore({SELENESE, CHROME, REMOTE, IPHONE})
public void testShouldBeAbleToExecuteScriptAndReturnElementsList() {
driver.get(formPage);
String scriptToExec = "return document.getElementsByName('snack');";
Expand Down
16 changes: 10 additions & 6 deletions common/test/java/org/openqa/selenium/SingleTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class SingleTestSuite extends TestCase {
private final static String SELENIUM = "org.openqa.selenium.SeleneseBackedWebDriver";

public static Test suite() throws Exception {
String driver = FIREFOX_TEST;
String driver = IE;

System.setProperty("webdriver.development", "true");
System.setProperty("jna.library.path", "..\\build;build");
Expand All @@ -51,15 +51,19 @@ public static Test suite() throws Exception {

TestSuiteBuilder builder = new TestSuiteBuilder()
.addSourceDir("common")
.addSourceDir("firefox")
.addSourceDir("jobbie")
.usingDriver(driver)
.keepDriverInstance()
.includeJavascriptTests()
.onlyRun("CorrectEventFiringTest")
// .method("testShouldBeAbleToGetTheLocationOfAnElement")
.onlyRun("ExecutingJavascriptTest")
.method("testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray")
.method("testShouldBeAbleToExecuteSimpleJavascriptAndAStringsArray")
.method("testShouldBeAbleToExecuteScriptAndReturnElementsList")
.method("testShouldBeAbleToExecuteSimpleJavascriptAndReturnAWebElement")
.exclude(ALL)
.exclude(Ignore.Driver.FIREFOX)
.leaveRunning()
.exclude(Ignore.Driver.IE)
.outputTestNames()
//.leaveRunning()
; // Yeah, this look strange :)

if (REMOTE.equals(driver) || REMOTE_IE.equals(driver)) {
Expand Down
1 change: 1 addition & 0 deletions jobbie/src/cpp/InternetExplorerDriver/DataMarshaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void DataMarshaller::resetInputs()
input_string_ = NULL;
input_safe_array_ = NULL;
scope_caller_ = NULL;
input_variant_ = NULL;
}

void DataMarshaller::resetOutputs()
Expand Down
1 change: 1 addition & 0 deletions jobbie/src/cpp/InternetExplorerDriver/DataMarshaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class DataMarshaller
long input_long_;
IHTMLElement *input_html_element_;
SAFEARRAY *input_safe_array_;
CComVariant *input_variant_;

// WARNING can only be set by worker thread
std::wstring output_string_;
Expand Down
1 change: 1 addition & 0 deletions jobbie/src/cpp/InternetExplorerDriver/IEThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ BOOL IeThread::DispatchThreadMessageEx(MSG* pMsg)
CUSTOM_MESSAGE_MAP ( _WD_CLOSEWINDOW, OnCloseWindow )
CUSTOM_MESSAGE_MAP ( _WD_SWITCHWINDOW, OnSwitchToWindow )
CUSTOM_MESSAGE_MAP ( _WD_CAPTURESCREENSHOT, OnCaptureScreenshot )
CUSTOM_MESSAGE_MAP ( _WD_GETSCRIPTRESULTOBJECTTYPE, OnGetScriptResultObjectType)

return FALSE;
}
Expand Down
2 changes: 2 additions & 0 deletions jobbie/src/cpp/InternetExplorerDriver/IEThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class IeThread
void removeScript(IHTMLDocument2* doc);
bool createAnonymousFunction(IDispatch* scriptEngine, DISPID evalId, const wchar_t *script, VARIANT* result);
void captureScreenshot(std::wstring& res);
std::wstring getStriptResultObjectType(CComVariant* scriptResult);

void OnStartIE(WPARAM, LPARAM);
void OnGetFramesCollection(WPARAM, LPARAM);
Expand Down Expand Up @@ -183,6 +184,7 @@ class IeThread
void OnAddCookie(WPARAM, LPARAM);
void OnWaitForNavigationToFinish(WPARAM, LPARAM);
void OnCaptureScreenshot(WPARAM, LPARAM);
void OnGetScriptResultObjectType(WPARAM, LPARAM);

void OnElementRelease(WPARAM, LPARAM);

Expand Down
52 changes: 48 additions & 4 deletions jobbie/src/cpp/InternetExplorerDriver/IEThreadExplorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,9 @@ void IeThread::OnCloseWindow(WPARAM w, LPARAM lp)
if (FAILED(pBody->ieThreaded->Stop())) {
LOG(INFO) << "Unable to stop IE instance";
}
if (FAILED(pBody->ieThreaded->Quit())) {
LOG(WARN) << "Unable to quit IE instance.";
HRESULT hr = pBody->ieThreaded->Quit();
if (FAILED(hr)) {
LOGHR(WARN, hr) << "Unable to quit IE instance.";
}
}

Expand Down Expand Up @@ -464,6 +465,51 @@ void IeThread::captureScreenshot(std::wstring& res)
}
}

void IeThread::OnGetScriptResultObjectType(WPARAM w, LPARAM lp)
{
SCOPETRACER
ON_THREAD_COMMON(data)

data.output_string_ = getStriptResultObjectType(data.input_variant_);
}

std::wstring IeThread::getStriptResultObjectType(CComVariant* scriptResult)
{
CComQIPtr<IHTMLElementCollection> isCol(scriptResult->pdispVal);
if (isCol) {
return L"HtmlCollection";
}

CComQIPtr<IHTMLElement> isElem(scriptResult->pdispVal);
if (isElem) {
return L"HtmlElement";
}

// Other possible interfaces: IHTMLFrameBase, IHTMLFrameElement
// The distinction is not important for now.

CComPtr<ITypeInfo> typeinfo;
HRESULT getTypeInfoRes = scriptResult->pdispVal->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo);
TYPEATTR* typeAttr;
CComBSTR name;
if (SUCCEEDED(getTypeInfoRes) && SUCCEEDED(typeinfo->GetTypeAttr(&typeAttr))
&& SUCCEEDED(typeinfo->GetDocumentation(-1, &name, 0, 0, 0))) {
// If the name is JScriptTypeInfo then *assume* this is a Javascript array.
// Note that Javascript can return functions which will have the same
// type - the only way to be sure is to run some more Javascript code to
// see if this object has a length attribute. This does not seem necessary
// now.
// (For future reference, GUID is {C59C6B12-F6C1-11CF-8835-00A0C911E8B2})
typeinfo->ReleaseTypeAttr(typeAttr);
if (name == L"JScriptTypeInfo") {
return L"JavascriptArray";
}
}

return L"Unknown";
}


bool browserMatches(const wchar_t* name, IWebBrowser2* browser)
{
CComPtr<IDispatch> dispatch;
Expand Down Expand Up @@ -559,5 +605,3 @@ void IeThread::OnSwitchToFrame(WPARAM w, LPARAM lp)

data.output_bool_ = (doc != NULL);
}


Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ limitations under the License.
#define _WD_SWITCHWINDOW WM_USER+83

#define _WD_CAPTURESCREENSHOT WM_USER+84
#define _WD_GETSCRIPTRESULTOBJECTTYPE WM_USER+85

// ==============================================================
// HEART BEATS
Expand Down
13 changes: 13 additions & 0 deletions jobbie/src/cpp/InternetExplorerDriver/InternetExplorerDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,13 @@ LPCWSTR InternetExplorerDriver::captureScreenshotAsBase64()
return data.output_string_.c_str();
}

LPCWSTR InternetExplorerDriver::getScriptResultType(CComVariant* result)
{
SCOPETRACER
SEND_MESSAGE_WITH_MARSHALLED_DATA(_WD_GETSCRIPTRESULTOBJECTTYPE, result)
return data.output_string_.c_str();
}

/////////////////////////////////////////////////////////////

bool InternetExplorerDriver::sendThreadMsg(UINT msg, DataMarshaller& data)
Expand Down Expand Up @@ -477,3 +484,9 @@ DataMarshaller& InternetExplorerDriver::prepareCmData(int v)
return data;
}

DataMarshaller& InternetExplorerDriver::prepareCmData(CComVariant *pDispatch)
{
DataMarshaller& data = prepareCmData();
data.input_variant_ = pDispatch;
return data;
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class InternetExplorerDriver
DataMarshaller& prepareCmData(LPCWSTR str);
DataMarshaller& prepareCmData(int v);
DataMarshaller& prepareCmData(IHTMLElement *pElem, LPCWSTR str);
DataMarshaller& prepareCmData(CComVariant *pDispatch);

ElementWrapper* getActiveElement();

Expand Down Expand Up @@ -84,6 +85,7 @@ class InternetExplorerDriver
int executeScript(const wchar_t *script, SAFEARRAY* args, CComVariant* result, bool tryAgain = true);

LPCWSTR captureScreenshotAsBase64();
LPCWSTR getScriptResultType(CComVariant* result);

private:

Expand Down
81 changes: 75 additions & 6 deletions jobbie/src/cpp/InternetExplorerDriver/webdriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ int wdeClick(WebElement* element)
} END_TRY;
}

int wdeGetAttribute(WebElement* element, const wchar_t* name, StringWrapper** result)
int wdeGetAttribute(WebDriver* driver, WebElement* element, const wchar_t* name, StringWrapper** result)
{
*result = NULL;
int res = verifyFresh(element); if (res != SUCCESS) { return res; }
Expand Down Expand Up @@ -649,7 +649,7 @@ int wdeGetAttribute(WebElement* element, const wchar_t* name, StringWrapper** re
}

int type;
wdGetScriptResultType(scriptResult, &type);
wdGetScriptResultType(driver, scriptResult, &type);
if (type != 5) {
const std::wstring originalString(bstr2cw(scriptResult->result.bstrVal));
size_t length = originalString.length() + 1;
Expand Down Expand Up @@ -1209,7 +1209,7 @@ int wdFindElementByXPath(WebDriver* driver, WebElement* element, const wchar_t*
// And be done
if (result == SUCCESS) {
int type = 0;
result = wdGetScriptResultType(queryResult, &type);
result = wdGetScriptResultType(driver, queryResult, &type);
if (type != 5) {
result = wdGetElementScriptResult(queryResult, driver, out);
} else {
Expand Down Expand Up @@ -1304,7 +1304,7 @@ int wdFindElementsByXPath(WebDriver* driver, WebElement* element, const wchar_t*
WebElement* e;
wdGetElementScriptResult(getElemRes, driver, &e);
elements->elements->push_back(e->element);
//TODO(eranm): Probably missing wdFreeScriptArgs
wdFreeScriptArgs(getElemArgs);
}
SafeArrayDestroy(queryArgs);

Expand Down Expand Up @@ -1428,7 +1428,7 @@ int wdExecuteScript(WebDriver* driver, const wchar_t* script, ScriptArgs* script
} END_TRY;
}

int wdGetScriptResultType(ScriptResult* result, int* type)
int wdGetScriptResultType(WebDriver* driver, ScriptResult* result, int* type)
{
if (!result) { return ENOSCRIPTRESULT; }

Expand All @@ -1448,7 +1448,19 @@ int wdGetScriptResultType(ScriptResult* result, int* type)

case VT_DISPATCH:
{
*type = 4;
LPCWSTR itemType = driver->ie->getScriptResultType(&(result->result));
std::string itemTypeStr;
cw2string(itemType, itemTypeStr);

LOG(DEBUG) << "Got type: " << itemTypeStr;
// If it's a Javascript array or an HTML Collection - type 8 will
// indicate the driver that this is ultimately an array.
if ((itemTypeStr == "JavascriptArray") ||
(itemTypeStr == "HtmlCollection")) {
*type = 8;
} else {
*type = 4;
}
}
break;

Expand Down Expand Up @@ -1535,6 +1547,63 @@ int wdGetElementScriptResult(ScriptResult* result, WebDriver* driver, WebElement
return SUCCESS;
}

int wdGetArrayLengthScriptResult(WebDriver* driver, ScriptResult* result,
int* length)
{
// Prepare an array for the Javascript execution, containing only one
// element - the original returned array from a JS execution.
SAFEARRAYBOUND lengthQuery;
lengthQuery.cElements = 1;
lengthQuery.lLbound = 0;
SAFEARRAY* lengthArgs = SafeArrayCreate(VT_VARIANT, 1, &lengthQuery);
LONG index = 0;
SafeArrayPutElement(lengthArgs, &index, &(result->result));
CComVariant lengthVar;
int lengthResult = driver->ie->executeScript(
L"(function(){return function() {return arguments[0].length;}})();",
lengthArgs, &lengthVar);
SafeArrayDestroy(lengthArgs);
if (lengthResult != SUCCESS) {
return lengthResult;
}

// Expect the return type to be an integer. A non-integer means this was
// not an array after all.
if (lengthVar.vt != VT_I4) {
return EUNEXPECTEDJSERROR;
}

*length = lengthVar.lVal;

return SUCCESS;
}

int wdGetArrayItemFromScriptResult(WebDriver* driver, ScriptResult* result,
int index, ScriptResult** arrayItem)
{
// Prepare an array for Javascript execution. The array contains the original
// array returned from a previous execution and the index of the item required
// from that array.
ScriptArgs* getItemArgs;
wdNewScriptArgs(&getItemArgs, 2);
LONG argIndex = 0;
// Original array.
SafeArrayPutElement(getItemArgs->args, &argIndex, &(result->result));
getItemArgs->currentIndex++;
// Item index
wdAddNumberScriptArg(getItemArgs, index);

int execRes = wdExecuteScript(
driver,
L"(function(){return function() {return arguments[0][arguments[1]];}})();",
getItemArgs, arrayItem);

wdFreeScriptArgs(getItemArgs);
getItemArgs = NULL;
return execRes;
}


int wdeMouseDownAt(HWND hwnd, long windowX, long windowY)
{
mouseDownAt(hwnd, windowX, windowY);
Expand Down
Loading

0 comments on commit 7baaede

Please sign in to comment.