mirror of https://github.com/vitalif/phantomjs
Embedding GhostDriver into PhantomJS(!!!)
Finally. After so much work, this is finally a reality. To launch PhantomJS in "Remote WebDriver mode": ```bash $ phantomjs --webdriver=OPTIONAL_IP:OPTIONAL_PORT ``` Also, GhostDriver brings along support for Selenium Grid: now PhantomJS can register itself to a Selenium Grid HUB. Just launch it in Webdriver Mode with the following extra options: ```bash $ phantomjs --webdriver=OPTIONAL_IP:OPTIONAL_PORT --webdriver-selenium-grid-hub=http://url.to.selenium.grid.hub:port ``` http://code.google.com/p/phantomjs/issues/detail?id=491.8
parent
2dcccc8968
commit
ffa9fab316
|
@ -193,7 +193,7 @@ phantom.onError = phantom.defaultErrorHandler;
|
|||
var paths = [], dir;
|
||||
|
||||
if (request[0] === '.') {
|
||||
paths.push(fs.absolute(joinPath(this.dirname, request)));
|
||||
paths.push(fs.absolute(joinPath(phantom.webdriverMode ? ":/ghostdriver" : this.dirname, request)));
|
||||
} else if (request[0] === '/') {
|
||||
paths.push(fs.absolute(request));
|
||||
} else {
|
||||
|
@ -284,8 +284,11 @@ phantom.onError = phantom.defaultErrorHandler;
|
|||
cwd = fs.absolute(phantom.libraryPath);
|
||||
mainFilename = joinPath(cwd, basename(require('system').args[0]) || 'repl');
|
||||
mainModule._setFilename(mainFilename);
|
||||
// include CoffeeScript which takes care of adding .coffee extension
|
||||
require('_coffee-script');
|
||||
|
||||
// include CoffeeScript which takes care of adding .coffee extension (only if not in Webdriver mode)
|
||||
if (!phantom.webdriverMode) {
|
||||
require('_coffee-script');
|
||||
}
|
||||
}());
|
||||
}());
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ static const struct QCommandLineConfigEntry flags[] =
|
|||
{ QCommandLine::Option, '\0', "script-encoding", "Sets the encoding used for the starting script, default is 'utf8'", QCommandLine::Optional },
|
||||
{ QCommandLine::Option, '\0', "web-security", "Enables web security, 'yes' (default) or 'no'", QCommandLine::Optional },
|
||||
{ QCommandLine::Option, '\0', "ssl-protocol", "Sets the SSL protocol (supported protocols: 'SSLv3' (default), 'SSLv2', 'TLSv1', 'any')", QCommandLine::Optional },
|
||||
{ QCommandLine::Option, '\0', "webdriver", "Starts in 'Remote WebDriver mode' (embedded GhostDriver): '[[<IP>:]<PORT>]' (default '127.0.0.1:8910') ", QCommandLine::Optional },
|
||||
{ QCommandLine::Option, '\0', "webdriver-selenium-grid-hub", "URL to the Selenium Grid HUB: 'URL_TO_HUB' (default 'none') (NOTE: works only together with '--webdriver') ", QCommandLine::Optional },
|
||||
{ QCommandLine::Param, '\0', "script", "Script", QCommandLine::Flags(QCommandLine::Optional|QCommandLine::ParameterFence)},
|
||||
{ QCommandLine::Param, '\0', "argument", "Script argument", QCommandLine::OptionalMultiple },
|
||||
{ QCommandLine::Switch, 'h', "help", "Shows this message and quits", QCommandLine::Optional },
|
||||
|
@ -99,6 +101,20 @@ void Config::processArgs(const QStringList &args)
|
|||
m_cmdLine->setArguments(args);
|
||||
m_cmdLine->setConfig(flags);
|
||||
m_cmdLine->parse();
|
||||
|
||||
// Inject command line parameters to be picked up by GhostDriver
|
||||
if (isWebdriverMode()) {
|
||||
QStringList argsForGhostDriver;
|
||||
|
||||
m_scriptFile = "main.js"; //< launch script
|
||||
argsForGhostDriver << m_webdriver; //< ip:port
|
||||
if (!m_seleniumGridHub.isEmpty()) {
|
||||
argsForGhostDriver << m_seleniumGridHub; //< selenium grid url
|
||||
}
|
||||
|
||||
// Clear current args and override with those
|
||||
setScriptArgs(argsForGhostDriver);
|
||||
}
|
||||
}
|
||||
|
||||
void Config::loadJsonFile(const QString &filePath)
|
||||
|
@ -424,6 +440,48 @@ bool Config::javascriptCanCloseWindows() const
|
|||
return m_javascriptCanCloseWindows;
|
||||
}
|
||||
|
||||
void Config::setWebdriver(const QString &webdriverConfig)
|
||||
{
|
||||
// This option can be provided empty: in that case we should use the default IP:PORT configuration
|
||||
QString ip = "127.0.0.1";
|
||||
QString port = "8910";
|
||||
|
||||
// Parse and validate the configuration
|
||||
bool isValidPort;
|
||||
QStringList wdCfg = webdriverConfig.split(':');
|
||||
if (wdCfg.length() == 1 && wdCfg[0].toInt(&isValidPort) && isValidPort) {
|
||||
// Only a PORT was provided
|
||||
port = wdCfg[0];
|
||||
} else if(wdCfg.length() == 2 && !wdCfg[0].isEmpty() && wdCfg[1].toInt(&isValidPort) && isValidPort) {
|
||||
// Both IP and PORT provided
|
||||
ip = wdCfg[0];
|
||||
port = wdCfg[1];
|
||||
}
|
||||
|
||||
// Setting the "webdriver" configuration
|
||||
m_webdriver = QString("%1:%2").arg(ip).arg(port);
|
||||
}
|
||||
|
||||
QString Config::webdriver() const
|
||||
{
|
||||
return m_webdriver;
|
||||
}
|
||||
|
||||
bool Config::isWebdriverMode() const
|
||||
{
|
||||
return !m_webdriver.isEmpty();
|
||||
}
|
||||
|
||||
void Config::setSeleniumGridHub(const QString &hubUrl)
|
||||
{
|
||||
m_seleniumGridHub = hubUrl;
|
||||
}
|
||||
|
||||
QString Config::seleniumGridHub() const
|
||||
{
|
||||
return m_seleniumGridHub;
|
||||
}
|
||||
|
||||
// private:
|
||||
void Config::resetToDefaults()
|
||||
{
|
||||
|
@ -455,6 +513,8 @@ void Config::resetToDefaults()
|
|||
m_helpFlag = false;
|
||||
m_printDebugMessages = false;
|
||||
m_sslProtocol = "sslv3";
|
||||
m_webdriver = QString();
|
||||
m_seleniumGridHub = QString();
|
||||
}
|
||||
|
||||
void Config::setProxyAuthPass(const QString &value)
|
||||
|
@ -598,6 +658,12 @@ void Config::handleOption(const QString &option, const QVariant &value)
|
|||
if (option == "ssl-protocol") {
|
||||
setSslProtocol(value.toString());
|
||||
}
|
||||
if (option == "webdriver") {
|
||||
setWebdriver(value.toString());
|
||||
}
|
||||
if (option == "selenium-grid-hub") {
|
||||
setSeleniumGridHub(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void Config::handleParam(const QString& param, const QVariant &value)
|
||||
|
@ -623,4 +689,4 @@ QString Config::sslProtocol() const
|
|||
void Config::setSslProtocol(const QString& sslProtocolName)
|
||||
{
|
||||
m_sslProtocol = sslProtocolName.toLower();
|
||||
}
|
||||
}
|
||||
|
|
13
src/config.h
13
src/config.h
|
@ -38,7 +38,7 @@
|
|||
|
||||
class QCommandLine;
|
||||
|
||||
class Config: QObject
|
||||
class Config: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString cookiesFile READ cookiesFile WRITE setCookiesFile)
|
||||
|
@ -58,6 +58,8 @@ class Config: QObject
|
|||
Q_PROPERTY(bool javascriptCanOpenWindows READ javascriptCanOpenWindows WRITE setJavascriptCanOpenWindows)
|
||||
Q_PROPERTY(bool javascriptCanCloseWindows READ javascriptCanCloseWindows WRITE setJavascriptCanCloseWindows)
|
||||
Q_PROPERTY(QString sslProtocol READ sslProtocol WRITE setSslProtocol)
|
||||
Q_PROPERTY(QString webdriver READ webdriver WRITE setWebdriver)
|
||||
Q_PROPERTY(QString seleniumGridHub READ seleniumGridHub WRITE setSeleniumGridHub)
|
||||
|
||||
public:
|
||||
Config(QObject *parent = 0);
|
||||
|
@ -152,6 +154,13 @@ public:
|
|||
void setSslProtocol(const QString& sslProtocolName);
|
||||
QString sslProtocol() const;
|
||||
|
||||
void setWebdriver(const QString& webdriverConfig);
|
||||
QString webdriver() const;
|
||||
bool isWebdriverMode() const;
|
||||
|
||||
void setSeleniumGridHub(const QString& hubUrl);
|
||||
QString seleniumGridHub() const;
|
||||
|
||||
public slots:
|
||||
void handleSwitch(const QString &sw);
|
||||
void handleOption(const QString &option, const QVariant &value);
|
||||
|
@ -196,6 +205,8 @@ private:
|
|||
bool m_javascriptCanOpenWindows;
|
||||
bool m_javascriptCanCloseWindows;
|
||||
QString m_sslProtocol;
|
||||
QString m_webdriver;
|
||||
QString m_seleniumGridHub;
|
||||
};
|
||||
|
||||
#endif // CONFIG_H
|
||||
|
|
|
@ -354,7 +354,7 @@ bool FileSystem::changeWorkingDirectory(const QString &path) const
|
|||
|
||||
QString FileSystem::absolute(const QString &relativePath) const
|
||||
{
|
||||
return QFileInfo(relativePath).absoluteFilePath();
|
||||
return QFileInfo(relativePath).absoluteFilePath();
|
||||
}
|
||||
|
||||
// Files
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
#include <QWebPage>
|
||||
#include <QDebug>
|
||||
#include <QMetaObject>
|
||||
#include <QMetaProperty>
|
||||
|
||||
#include "consts.h"
|
||||
#include "terminal.h"
|
||||
|
@ -179,12 +182,37 @@ bool Phantom::execute()
|
|||
if (m_terminated)
|
||||
return false;
|
||||
|
||||
if (m_config.scriptFile().isEmpty()) {
|
||||
// REPL mode requested
|
||||
#ifndef QT_NO_DEBUG_OUTPUT
|
||||
qDebug() << "Phantom - execute: Configuration";
|
||||
const QMetaObject* configMetaObj = m_config.metaObject();
|
||||
for (int i = 0, ilen = configMetaObj->propertyCount(); i < ilen; ++i) {
|
||||
qDebug() << " " << i << configMetaObj->property(i).name() << ":" << m_config.property(configMetaObj->property(i).name()).toString();
|
||||
}
|
||||
|
||||
qDebug() << "Phantom - execute: Script & Arguments";
|
||||
qDebug() << " " << "script:" << m_config.scriptFile();
|
||||
QStringList args = m_config.scriptArgs();
|
||||
for (int i = 0, ilen = args.length(); i < ilen; ++i) {
|
||||
qDebug() << " " << i << "arg:" << args.at(i);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_config.isWebdriverMode()) { // Remote WebDriver mode requested
|
||||
qDebug() << "Phantom - execute: Starting Remote WebDriver mode";
|
||||
|
||||
Terminal::instance()->cout("PhantomJS is launching GhostDriver...");
|
||||
if (!Utils::injectJsInFrame(":/ghostdriver/main.js", m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), true)) {
|
||||
m_returnValue = -1;
|
||||
return false;
|
||||
}
|
||||
} else if (m_config.scriptFile().isEmpty()) { // REPL mode requested
|
||||
qDebug() << "Phantom - execute: Starting REPL mode";
|
||||
|
||||
// Create the REPL: it will launch itself, no need to store this variable.
|
||||
REPL::getInstance(m_page->mainFrame(), this);
|
||||
} else {
|
||||
// Load the User Script
|
||||
} else { // Load the User Script
|
||||
qDebug() << "Phantom - execute: Starting normal mode";
|
||||
|
||||
if (m_config.debug()) {
|
||||
// Debug enabled
|
||||
if (!Utils::loadJSForDebug(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), m_config.remoteDebugAutorun())) {
|
||||
|
@ -261,6 +289,11 @@ void Phantom::setCookiesEnabled(const bool value)
|
|||
}
|
||||
}
|
||||
|
||||
bool Phantom::webdriverMode() const
|
||||
{
|
||||
return m_config.isWebdriverMode();
|
||||
}
|
||||
|
||||
// public slots:
|
||||
QObject *Phantom::createWebPage()
|
||||
{
|
||||
|
@ -331,12 +364,19 @@ void Phantom::loadModule(const QString &moduleSource, const QString &filename)
|
|||
|
||||
bool Phantom::injectJs(const QString &jsFilePath)
|
||||
{
|
||||
QString pre = "";
|
||||
qDebug() << "Phantom - injectJs:" << jsFilePath;
|
||||
|
||||
// If in Remote Webdriver Mode, we need to manipulate the PATH, to point it to a resource in `ghostdriver.qrc`
|
||||
if (webdriverMode()) {
|
||||
pre = ":/ghostdriver/";
|
||||
qDebug() << "Phantom - injectJs: prepending" << pre;
|
||||
}
|
||||
|
||||
if (m_terminated)
|
||||
return false;
|
||||
|
||||
return Utils::injectJsInFrame(jsFilePath, libraryPath(), m_page->mainFrame());
|
||||
return Utils::injectJsInFrame(pre + jsFilePath, libraryPath(), m_page->mainFrame());
|
||||
}
|
||||
|
||||
void Phantom::exit(int code)
|
||||
|
|
|
@ -56,6 +56,7 @@ class Phantom: public REPLCompletable
|
|||
Q_PROPERTY(QObject *page READ page)
|
||||
Q_PROPERTY(bool cookiesEnabled READ areCookiesEnabled WRITE setCookiesEnabled)
|
||||
Q_PROPERTY(QVariantList cookies READ cookies WRITE setCookies)
|
||||
Q_PROPERTY(bool webdriverMode READ webdriverMode)
|
||||
|
||||
private:
|
||||
// Private constructor: the Phantom class is a singleton
|
||||
|
@ -99,6 +100,8 @@ public:
|
|||
bool areCookiesEnabled() const;
|
||||
void setCookiesEnabled(const bool value);
|
||||
|
||||
bool webdriverMode() const;
|
||||
|
||||
public slots:
|
||||
QObject *createWebPage();
|
||||
QObject *createWebServer();
|
||||
|
|
|
@ -85,7 +85,7 @@ public:
|
|||
Multiple = 0x04, /**< argument can be used multiple time and will produce multiple signals. */
|
||||
ParameterFence = 0x08, //**< all arguments after this point are considered parameters, not options. */
|
||||
MandatoryMultiple = Mandatory|Multiple,
|
||||
OptionalMultiple = Optional|Multiple,
|
||||
OptionalMultiple = Optional|Multiple
|
||||
} Flags;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue