This code sets up a REST API using Express.js to enable a web application to control a Selenium WebDriver instance running on a separate server, automating browser interactions. It uses Socket.IO for communication and a custom importer
module to execute a Jupyter notebook containing Selenium server logic.
npm run import -- "Start a selenium http express server"
var importer = require('../Core')
var bodyParser = require('body-parser');
var express = require('express');
var io = require('socket.io-client');
var client = io('https://localhost:8000', {
secure: true,
rejectUnauthorized: false
});
var currentCallback = null;
client.on('result', function (name, ...args) {
// TODO: not just verify the service,
// also verify the tab id matches
if (name === 'BrowserService.prototype.chrome') {
if(currentCallback) {
currentCallback.apply(this, args);
}
}
})
function promisifyChrome(...args) {
return new Promise((resolve, reject) => {
console.log('call ' + JSON.stringify(args).substr(0, 200));
if(typeof currentCallback === 'function') {
throw new Error('Callback already defined.');
}
var cmdTimeout = setTimeout(() => {
currentCallback = null;
reject(new Error('command took too long to respond', 3000));
}, 9000)
currentCallback = (err, data) => {
currentCallback = null;
clearTimeout(cmdTimeout);
if (err !== null) {
return reject(err);
}
return resolve.apply(this, JSON.parse(data));
};
client.emit.apply(client, [
'call',
'BrowserService.prototype.chrome',
...args
]);
});
};
function response(res, promise) {
return promise.then(r => {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(r));
})
}
var tabId;
function seleniumServer() {
const selenium = express();
const server = require('http').createServer(selenium);
selenium.use(bodyParser.json()); // to support JSON-encoded bodies
selenium.use(bodyParser.urlencoded({// to support URL-encoded bodies
extended: true
}));
// TODO: import the rest of the modules in this script
return importer.getCells(__dirname + '/../Selenium/selenium server.ipynb')
.then(cells => {
for (const c of cells.slice(1)) /* exclude this cell */ {
const mod = importer.runInNewContext(c.source.join(''), {
__filename: __dirname + '/../Selenium/selenium server.ipynb',
promisifyChrome,
getTab: () => tabId,
setTab: (id) => (tabId = id),
response
}, {}, false);
if (typeof mod !== 'undefined' && typeof mod.router !== 'undefined') {
selenium.use('/wd/hub', mod.router);
}
}
return server.listen(4444);
});
};
module.exports = seleniumServer;
const express = require('express');
const bodyParser = require('body-parser');
const http = require('http');
const io = require('socket.io-client');
const importer = require('../Core');
const app = express();
const server = http.createServer(app);
const ioClient = io('https://localhost:8000', {
secure: true,
rejectUnauthorized: false,
});
// Handle results from the server
ioClient.on('result', (name,...args) => {
if (name === 'BrowserService.prototype.chrome') {
if (currentCallback) {
currentCallback.apply(this, args);
}
}
});
// Create a promise for Chrome calls
function promisifyChrome(...args) {
return new Promise((resolve, reject) => {
console.log('call'+ JSON.stringify(args).substr(0, 200));
if (currentCallback) {
throw new Error('Callback already defined.');
}
const cmdTimeout = setTimeout(() => {
currentCallback = null;
reject(new Error('Command took too long to respond.', 3000));
}, 9000);
currentCallback = (err, data) => {
currentCallback = null;
clearTimeout(cmdTimeout);
if (err!== null) {
return reject(err);
}
return resolve(JSON.parse(data));
};
ioClient.emit.apply(ioClient, [
'call',
'BrowserService.prototype.chrome',
...args,
]);
});
}
// Handle responses
function response(res, promise) {
return promise.then((r) => {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(r));
});
}
let tabId = '';
// Create a selenium server
async function seleniumServer() {
try {
// Import the selenium server cells
const cells = await importer.getCells(__dirname + '/../Selenium/selenium server.ipynb');
const router = cells.slice(1).reduce((acc, cell) => {
const mod = importer.runInNewContext(cell.source.join(''), {
__filename: __dirname + '/../Selenium/selenium server.ipynb',
promisifyChrome,
getTab: () => tabId,
setTab: (id) => (tabId = id),
response,
}, {}, false);
if (typeof mod!== 'undefined' && typeof mod.router!== 'undefined') {
acc.use('/wd/hub', mod.router);
}
return acc;
}, app);
// Start the server
server.listen(4444);
return router;
} catch (error) {
console.error(error);
}
}
module.exports = seleniumServer;
This code sets up a basic REST API server using Express.js to interact with a Selenium WebDriver instance running on a separate server.
Here's a breakdown:
Dependencies:
importer
: A custom module likely containing functions for interacting with Jupyter notebooks.body-parser
: Middleware for parsing incoming request bodies (JSON and URL-encoded).express
: Web framework for creating the API server.socket.io-client
: Client library for connecting to a Socket.IO server.Socket.IO Connection:
https://localhost:8000
.result
event.promisifyChrome
Function:
BrowserService.prototype.chrome
method on the Selenium server using a Promise.response
Function:
seleniumServer
Function:
body-parser
middleware to parse incoming request bodies.selenium server.ipynb
) using importer.getCells
.Purpose:
This code likely forms part of a larger system where a web application interacts with a Selenium server to automate browser interactions. The API server acts as a bridge between the web application and the Selenium server, allowing the application to send commands to the server and receive results.