From f386f7d48470a442e7ce7a89fc8cb4ca7daea5af Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 30 May 2012 13:28:20 +0200 Subject: [PATCH] Adding the ability to switch between frames. 1. Passing JavaScript eval and other related actions to the CurrentFrame, not the MainFrame. 2. Added different methods to navigate between frames 3. With a call to "window.frames[0].focus()", the "currentFrame" changes: commands after that are sent to the new frame under focus. 4. The navigation between frames allows to walk over the "tree of frames" contained in the page. This commit also adds examples (both in JS and CoffeeScript) and Unit Test. http://code.google.com/p/phantomjs/issues/detail?id=573 --- examples/walk_through_frames.coffee | 66 +++++++++++++++++++ examples/walk_through_frames.js | 73 +++++++++++++++++++++ src/bootstrap.js | 2 +- src/utils.cpp | 1 + src/webpage.cpp | 83 ++++++++++++++++++++++-- src/webpage.h | 48 +++++++++++++- test/webpage-spec-frames/frame1-1.html | 8 +++ test/webpage-spec-frames/frame1-2.html | 8 +++ test/webpage-spec-frames/frame1.html | 9 +++ test/webpage-spec-frames/frame2-1.html | 8 +++ test/webpage-spec-frames/frame2-2.html | 8 +++ test/webpage-spec-frames/frame2-3.html | 8 +++ test/webpage-spec-frames/frame2.html | 10 +++ test/webpage-spec-frames/index.html | 9 +++ test/webpage-spec.js | 88 +++++++++++++++++++++++++- 15 files changed, 420 insertions(+), 9 deletions(-) create mode 100644 examples/walk_through_frames.coffee create mode 100644 examples/walk_through_frames.js create mode 100644 test/webpage-spec-frames/frame1-1.html create mode 100644 test/webpage-spec-frames/frame1-2.html create mode 100644 test/webpage-spec-frames/frame1.html create mode 100644 test/webpage-spec-frames/frame2-1.html create mode 100644 test/webpage-spec-frames/frame2-2.html create mode 100644 test/webpage-spec-frames/frame2-3.html create mode 100644 test/webpage-spec-frames/frame2.html create mode 100644 test/webpage-spec-frames/index.html diff --git a/examples/walk_through_frames.coffee b/examples/walk_through_frames.coffee new file mode 100644 index 00000000..1838fa27 --- /dev/null +++ b/examples/walk_through_frames.coffee @@ -0,0 +1,66 @@ +pageTitle = (page) -> + page.evaluate -> + window.document.title +setPageTitle = (page, newTitle) -> + page.evaluate ((newTitle) -> + window.document.title = newTitle + ), newTitle +p = require("webpage").create() +p.open "../test/webpage-spec-frames/index.html", (status) -> + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + console.log "p.switchToChildFrame(\"frame1\"): " + p.switchToChildFrame("frame1") + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + console.log "p.switchToChildFrame(\"frame1-2\"): " + p.switchToChildFrame("frame1-2") + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + console.log "p.switchToParentFrame(): " + p.switchToParentFrame() + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + console.log "p.switchToChildFrame(0): " + p.switchToChildFrame(0) + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + console.log "p.switchToMainFrame()" + p.switchToMainFrame() + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + console.log "p.switchToChildFrame(\"frame2\"): " + p.switchToChildFrame("frame2") + console.log "pageTitle(): " + pageTitle(p) + console.log "currentFrameName(): " + p.currentFrameName() + console.log "childFramesCount(): " + p.childFramesCount() + console.log "childFramesName(): " + p.childFramesName() + console.log "setPageTitle(CURRENT TITLE+'-visited')" + setPageTitle p, pageTitle(p) + "-visited" + console.log "" + phantom.exit() diff --git a/examples/walk_through_frames.js b/examples/walk_through_frames.js new file mode 100644 index 00000000..35c2bb9b --- /dev/null +++ b/examples/walk_through_frames.js @@ -0,0 +1,73 @@ +var p = require("webpage").create(); + +function pageTitle(page) { + return page.evaluate(function(){ + return window.document.title; + }); +} + +function setPageTitle(page, newTitle) { + page.evaluate(function(newTitle){ + window.document.title = newTitle; + }, newTitle); +} + +p.open("../test/webpage-spec-frames/index.html", function(status) { + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + console.log("p.switchToChildFrame(\"frame1\"): "+p.switchToChildFrame("frame1")); + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + console.log("p.switchToChildFrame(\"frame1-2\"): "+p.switchToChildFrame("frame1-2")); + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + console.log("p.switchToParentFrame(): "+p.switchToParentFrame()); + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + console.log("p.switchToChildFrame(0): "+p.switchToChildFrame(0)); + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + console.log("p.switchToMainFrame()"); p.switchToMainFrame(); + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + console.log("p.switchToChildFrame(\"frame2\"): "+p.switchToChildFrame("frame2")); + console.log("pageTitle(): " + pageTitle(p)); + console.log("currentFrameName(): "+p.currentFrameName()); + console.log("childFramesCount(): "+p.childFramesCount()); + console.log("childFramesName(): "+p.childFramesName()); + console.log("setPageTitle(CURRENT TITLE+'-visited')"); setPageTitle(p, pageTitle(p) + "-visited"); + console.log(""); + + phantom.exit(); +}); + diff --git a/src/bootstrap.js b/src/bootstrap.js index 4c3f2719..842141a6 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -112,7 +112,7 @@ phantom.__defineErrorSetter__(phantom, phantom.page); phantom.defaultErrorHandler = function(error) { console.log(error + "\n"); - if (error.stack) { + if (error && error.stack) { error.stack.forEach(function(item) { var message = item.sourceURL + ":" + item.line; if (item.function) diff --git a/src/utils.cpp b/src/utils.cpp index e2805821..0fa65bfc 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -74,6 +74,7 @@ void Utils::messageHandler(QtMsgType type, const char *msg) bool Utils::exceptionHandler(const char* dump_path, const char* minidump_id, void* context, bool succeeded) { + Q_UNUSED(context); fprintf(stderr, "PhantomJS has crashed. Please file a bug report at " \ "https://code.google.com/p/phantomjs/issues/entry and " \ "attach the crash dump file: %s/%s.dmp\n", diff --git a/src/webpage.cpp b/src/webpage.cpp index 06d812c3..7ffc49fa 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -131,6 +131,9 @@ protected: } void javaScriptError(const QString &message, int lineNumber, const QString &sourceID) { + Q_UNUSED(message); + Q_UNUSED(lineNumber); + Q_UNUSED(sourceID); m_webPage->emitError(); } @@ -250,7 +253,7 @@ WebPage::WebPage(QObject *parent, const Config *config, const QUrl &baseUrl) m_mainFrame = m_webPage->mainFrame(); m_mainFrame->setHtml(BLANK_HTML, baseUrl); - connect(m_mainFrame, SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(registerCallbacksHolder())); + connect(m_mainFrame, SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(handleJavaScriptWindowObjectCleared())); connect(m_mainFrame, SIGNAL(javaScriptWindowObjectCleared()), SIGNAL(initialized())); connect(m_mainFrame, SIGNAL(urlChanged(QUrl)), SIGNAL(urlChanged(QUrl))); connect(m_webPage, SIGNAL(loadStarted()), SIGNAL(loadStarted()), Qt::QueuedConnection); @@ -451,7 +454,9 @@ QVariantMap WebPage::paperSize() const QVariant WebPage::evaluateJavaScript(const QString &code) { QString function = "(" + code + ")()"; - return m_mainFrame->evaluateJavaScript(function, QString("phantomjs://webpage.evaluate()")); + return m_webPage->currentFrame()->evaluateJavaScript( + function, + QString("phantomjs://webpage.evaluate()")); } void WebPage::emitAlert(const QString &msg) @@ -877,7 +882,7 @@ QString WebPage::footer(int page, int numPages) void WebPage::uploadFile(const QString &selector, const QString &fileName) { - QWebElement el = m_mainFrame->findFirstElement(selector); + QWebElement el = m_webPage->currentFrame()->findFirstElement(selector); if (el.isNull()) return; @@ -886,11 +891,11 @@ void WebPage::uploadFile(const QString &selector, const QString &fileName) } bool WebPage::injectJs(const QString &jsFilePath) { - return Utils::injectJsInFrame(jsFilePath, m_libraryPath, m_mainFrame); + return Utils::injectJsInFrame(jsFilePath, m_libraryPath, m_webPage->currentFrame()); } void WebPage::_appendScriptElement(const QString &scriptUrl) { - m_mainFrame->evaluateJavaScript( QString(JS_APPEND_SCRIPT_ELEMENT).arg(scriptUrl), scriptUrl ); + m_webPage->currentFrame()->evaluateJavaScript(QString(JS_APPEND_SCRIPT_ELEMENT).arg(scriptUrl), scriptUrl); } QObject *WebPage::_getGenericCallback() { @@ -950,6 +955,60 @@ void WebPage::sendEvent(const QString &type, const QVariant &arg1, const QVarian } } +int WebPage::childFramesCount() +{ + return m_webPage->currentFrame()->childFrames().count(); +} + +QVariantList WebPage::childFramesName() +{ + QVariantList framesName; + + foreach(QWebFrame * f, m_webPage->currentFrame()->childFrames()) { + framesName << f->frameName(); + } + return framesName; +} + +bool WebPage::switchToChildFrame(const QString &frameName) +{ + foreach(QWebFrame * f, m_webPage->currentFrame()->childFrames()) { + if (f->frameName() == frameName) { + f->setFocus(); + return true; + } + } + return false; +} + +bool WebPage::switchToChildFrame(const int framePosition) +{ + if (framePosition >= 0 && framePosition < m_webPage->currentFrame()->childFrames().size()) { + m_webPage->currentFrame()->childFrames().at(framePosition)->setFocus(); + return true; + } + return false; +} + +void WebPage::switchToMainFrame() +{ + m_mainFrame->setFocus(); +} + +bool WebPage::switchToParentFrame() +{ + if (m_webPage->currentFrame()->parentFrame() != NULL) { + m_webPage->currentFrame()->parentFrame()->setFocus(); + return true; + } + return false; +} + +QString WebPage::currentFrameName() +{ + return m_webPage->currentFrame()->frameName(); +} + void WebPage::initCompletions() { // Add completion for the Dynamic Properties of the 'webpage' object @@ -981,13 +1040,25 @@ void WebPage::initCompletions() addCompletion("onResourceReceived"); } -void WebPage::registerCallbacksHolder() +void WebPage::handleJavaScriptWindowObjectCleared() { + // Create Callbacks Holder object, if not already present for this page if (!m_callbacks) { m_callbacks = new WebpageCallbacks(this); } + + // Reset focus on the Main Frame + m_mainFrame->setFocus(); + + // Decorate the window object in the Main Frame m_mainFrame->addToJavaScriptWindowObject(CALLBACKS_OBJECT_NAME, m_callbacks, QScriptEngine::QtOwnership); m_mainFrame->evaluateJavaScript(CALLBACKS_OBJECT_INJECTION); + + // Decorate the window object in the Main Frame's Child Frames + foreach (QWebFrame *childFrame, m_mainFrame->childFrames()) { + childFrame->addToJavaScriptWindowObject(CALLBACKS_OBJECT_NAME, m_callbacks, QScriptEngine::QtOwnership); + childFrame->evaluateJavaScript(CALLBACKS_OBJECT_INJECTION); + } } #include "webpage.moc" diff --git a/src/webpage.h b/src/webpage.h index c79fdf2c..b34e108d 100644 --- a/src/webpage.h +++ b/src/webpage.h @@ -123,6 +123,52 @@ public slots: void uploadFile(const QString &selector, const QString &fileName); void sendEvent(const QString &type, const QVariant &arg1 = QVariant(), const QVariant &arg2 = QVariant()); + /** + * Returns the number of Child Frames inside the Current Frame. + * NOTE: The Current Frame changes when focus moves (via API or JS) to a specific child frame. + * @brief childFramesCount + * @return Number of Frames inside the Current Frame + */ + int childFramesCount(); + /** + * Returns a list of Child Frames name. + * NOTE: The Current Frame changes when focus moves (via API or JS) to a specific child frame. + * @brief childFramesName + * @return List (JS Array) containing the names of the Child Frames inside the Current Frame (if any) + */ + QVariantList childFramesName(); + /** + * Switches focus from the Current Frame to a Child Frame, identified by it's name. + * @brief switchToChildFrame + * @param frameName Name of the Child frame + * @return "true" if the frame was found, "false" otherwise + */ + bool switchToChildFrame(const QString &frameName); + /** + * Switches focus from the Current Frame to a Child Frame, identified by it positional order. + * @brief switchToChildFrame + * @param framePosition Position of the Frame inside the Child Frames array (i.e. "window.frames[i]") + * @return "true" if the frame was found, "false" otherwise + */ + bool switchToChildFrame(const int framePosition); + /** + * Switches focus to the Main Frame within this Page. + * @brief switchToMainFrame + */ + void switchToMainFrame(); + /** + * Switches focus to the Parent Frame of the Current Frame (if it exists). + * @brief switchToParentFrame + * @return "true" if the Current Frame is not a Main Frame, "false" otherwise (i.e. there is no parent frame to switch to) + */ + bool switchToParentFrame(); + /** + * Returns the name of the Current Frame (if it has one) + * @brief currentFrameName + * @return Name of the Current Frame + */ + QString currentFrameName(); + signals: void initialized(); void loadStarted(); @@ -137,7 +183,7 @@ signals: private slots: void finish(bool ok); - void registerCallbacksHolder(); + void handleJavaScriptWindowObjectCleared(); private: QImage renderImage(); diff --git a/test/webpage-spec-frames/frame1-1.html b/test/webpage-spec-frames/frame1-1.html new file mode 100644 index 00000000..c80ec458 --- /dev/null +++ b/test/webpage-spec-frames/frame1-1.html @@ -0,0 +1,8 @@ + + + frame1-1 + + +

index > frame1 > frame1-1

+ + diff --git a/test/webpage-spec-frames/frame1-2.html b/test/webpage-spec-frames/frame1-2.html new file mode 100644 index 00000000..b0c38d2f --- /dev/null +++ b/test/webpage-spec-frames/frame1-2.html @@ -0,0 +1,8 @@ + + + frame1-2 + + +

index > frame1 > frame1-2

+ + diff --git a/test/webpage-spec-frames/frame1.html b/test/webpage-spec-frames/frame1.html new file mode 100644 index 00000000..b23c2741 --- /dev/null +++ b/test/webpage-spec-frames/frame1.html @@ -0,0 +1,9 @@ + + + frame1 + + + + + + diff --git a/test/webpage-spec-frames/frame2-1.html b/test/webpage-spec-frames/frame2-1.html new file mode 100644 index 00000000..2f7c121a --- /dev/null +++ b/test/webpage-spec-frames/frame2-1.html @@ -0,0 +1,8 @@ + + + frame2-1 + + +

index > frame2 > frame2-1

+ + diff --git a/test/webpage-spec-frames/frame2-2.html b/test/webpage-spec-frames/frame2-2.html new file mode 100644 index 00000000..99f603d7 --- /dev/null +++ b/test/webpage-spec-frames/frame2-2.html @@ -0,0 +1,8 @@ + + + frame2-2 + + +

index > frame2 > frame2-2

+ + diff --git a/test/webpage-spec-frames/frame2-3.html b/test/webpage-spec-frames/frame2-3.html new file mode 100644 index 00000000..b6f18089 --- /dev/null +++ b/test/webpage-spec-frames/frame2-3.html @@ -0,0 +1,8 @@ + + + frame2-3 + + +

index > frame2 > frame2-3

+ + diff --git a/test/webpage-spec-frames/frame2.html b/test/webpage-spec-frames/frame2.html new file mode 100644 index 00000000..d3484bd1 --- /dev/null +++ b/test/webpage-spec-frames/frame2.html @@ -0,0 +1,10 @@ + + + frame2 + + + + + + + diff --git a/test/webpage-spec-frames/index.html b/test/webpage-spec-frames/index.html new file mode 100644 index 00000000..dbe01bb5 --- /dev/null +++ b/test/webpage-spec-frames/index.html @@ -0,0 +1,9 @@ + + + index + + + + + + diff --git a/test/webpage-spec.js b/test/webpage-spec.js index fba43e43..b2984743 100644 --- a/test/webpage-spec.js +++ b/test/webpage-spec.js @@ -159,8 +159,13 @@ describe("WebPage object", function() { expectHasFunction(page, 'resourceReceived'); expectHasFunction(page, 'resourceRequested'); expectHasFunction(page, 'uploadFile'); - expectHasFunction(page, 'sendEvent'); + expectHasFunction(page, 'childFramesCount'); + expectHasFunction(page, 'childFramesName'); + expectHasFunction(page, 'switchToChildFrame'); + expectHasFunction(page, 'switchToMainFrame'); + expectHasFunction(page, 'switchToParentFrame'); + expectHasFunction(page, 'currentFrameName'); it("should handle mousedown event", function() { runs(function() { @@ -657,3 +662,84 @@ describe("WebPage construction with options", function () { }); } }); + +describe("WebPage should be able to switch frame of execution", function(){ + var p = require("webpage").create(); + + function pageTitle(page) { + return page.evaluate(function(){ + return window.document.title; + }); + } + + function setPageTitle(page, newTitle) { + page.evaluate(function(newTitle){ + window.document.title = newTitle; + }, newTitle); + } + + it("should load a page full of frames", function(){ + runs(function() { + p.open("../test/webpage-spec-frames/index.html"); + }); + waits(50); + }); + + it("should be able to detect frames at level 0", function(){ + expect(pageTitle(p)).toEqual("index"); + expect(p.currentFrameName()).toEqual(""); + expect(p.childFramesCount()).toEqual(2); + expect(p.childFramesName()).toEqual(["frame1", "frame2"]); + setPageTitle(p, pageTitle(p) + "-visited"); + }); + + it("should go down to a child frame at level 1", function(){ + expect(p.switchToChildFrame("frame1")).toBeTruthy(); + expect(pageTitle(p)).toEqual("frame1"); + expect(p.currentFrameName()).toEqual("frame1"); + expect(p.childFramesCount()).toEqual(2); + expect(p.childFramesName()).toEqual(["frame1-1", "frame1-2"]); + setPageTitle(p, pageTitle(p) + "-visited"); + }); + + it("should go down to a child frame at level 2", function(){ + expect(p.switchToChildFrame("frame1-2")).toBeTruthy(); + expect(pageTitle(p)).toEqual("frame1-2"); + expect(p.currentFrameName()).toEqual("frame1-2"); + expect(p.childFramesCount()).toEqual(0); + expect(p.childFramesName()).toEqual([]); + setPageTitle(p, pageTitle(p) + "-visited"); + }); + + it("should go up to the parent frame at level 1", function(){ + expect(p.switchToParentFrame()).toBeTruthy(); + expect(pageTitle(p)).toEqual("frame1-visited"); + expect(p.currentFrameName()).toEqual("frame1"); + expect(p.childFramesCount()).toEqual(2); + expect(p.childFramesName()).toEqual(["frame1-1", "frame1-2"]); + }); + + it("should go down to a child frame at level 2 (again)", function(){ + expect(p.switchToChildFrame(0)).toBeTruthy(); + expect(pageTitle(p)).toEqual("frame1-1"); + expect(p.currentFrameName()).toEqual("frame1-1"); + expect(p.childFramesCount()).toEqual(0); + expect(p.childFramesName()).toEqual([]); + }); + + it("should go up to the main (top) frame at level 0", function(){ + expect(p.switchToMainFrame()).toBeUndefined(); + expect(pageTitle(p)).toEqual("index-visited"); + expect(p.currentFrameName()).toEqual(""); + expect(p.childFramesCount()).toEqual(2); + expect(p.childFramesName()).toEqual(["frame1", "frame2"]); + }); + + it("should go down to (the other) child frame at level 1", function(){ + expect(p.switchToChildFrame("frame2")).toBeTruthy(); + expect(pageTitle(p)).toEqual("frame2"); + expect(p.currentFrameName()).toEqual("frame2"); + expect(p.childFramesCount()).toEqual(3); + expect(p.childFramesName()).toEqual(["frame2-1", "frame2-2", "frame2-3"]); + }); +});