扩展 Selenium WebDriver JS

7

前言

我正在尝试为selenium-webdriver编写一些扩展,如下所示:

var webdriver = require('selenium-webdriver');
var fs = require('fs');
var resumer = require('resumer');

webdriver.WebDriver.prototype.saveScreenshot = function(filename) {
    return this.takeScreenshot().then(function(data) {
        fs.writeFile(filename, data.replace(/^data:image\/png;base64,/,''), 'base64', function(err) {
            if(err) throw err;
        });
    });
};

webdriver.WebDriver.prototype.streamScreenshot = function() {
    var stream = resumer();
    this.takeScreenshot().then(function(data) {
        stream.queue(new Buffer(data.replace(/^data:image\/png;base64,/,''), 'base64')).end();
    });
    return stream;
};

module.exports = webdriver;

然后我只需包含我的扩展webdriver,而不是官方的webdriver:

var webdriver = require('./webdriver.ext');

我认为这是在Node JS中扩展内容的适当方式。
问题
我遇到的问题是添加自定义定位策略。这些策略在源代码中如下所示:
/**
 * Factory methods for the supported locator strategies.
 * @type {Object.<function(string):!webdriver.Locator>}
 */
webdriver.Locator.Strategy = {
  'className': webdriver.Locator.factory_('class name'),
  'class name': webdriver.Locator.factory_('class name'),
  'css': webdriver.Locator.factory_('css selector'),
  'id': webdriver.Locator.factory_('id'),
  'js': webdriver.Locator.factory_('js'),
  'linkText': webdriver.Locator.factory_('link text'),
  'link text': webdriver.Locator.factory_('link text'),
  'name': webdriver.Locator.factory_('name'),
  'partialLinkText': webdriver.Locator.factory_('partial link text'),
  'partial link text': webdriver.Locator.factory_('partial link text'),
  'tagName': webdriver.Locator.factory_('tag name'),
  'tag name': webdriver.Locator.factory_('tag name'),
  'xpath': webdriver.Locator.factory_('xpath')
};
goog.exportSymbol('By', webdriver.Locator.Strategy);

我正在尝试通过将其注入到该对象中来添加一个新的对象:
webdriver.By.sizzle = function(selector) {
    driver.executeScript("return typeof Sizzle==='undefined'").then(function(noSizzle) {
        if(noSizzle) driver.executeScript(fs.readFileSync('sizzle.min.js', {encoding: 'utf8'}));
    });
    return new webdriver.By.js("return Sizzle("+JSON.stringify(selector)+")[0]");
};

这对于定义了driver的简单脚本实际上运行良好(请注意,我正在使用全局变量)。

有没有一种方法可以在我的函数内部访问"当前driver"?与顶部的方法不同,这不是原型方法,因此我无法访问this

我不知道那些factory_是如何工作的;我只是猜测我可以直接注入一个函数。

2个回答

3

设置一个定制的构造函数,该函数继承自webdriver.WebDriver。在构造函数内部,您可以访问this对象,以便添加自定义定位器。

var util = require('util');
var webdriver = require('selenium-webdriver');
var WebDriver = webdriver.WebDriver
var fs = require('fs');
var resumer = require('resumer');


function CustomDriver() {
  WebDriver.call(this);
  // append your strategy here using the "this" object
  this...
}

util.inherits(WebDriver, CustomDriver);

CustomDriver.prototype.saveScreenshot = function(filename) {
  return this.takeScreenshot().then(function(data) {
    fs.writeFile(filename, data.replace(/^data:image\/png;base64,/, ''), 'base64', function(err) {
      if (err) throw err;
    });
  });
};

CustomDriver.prototype.streamScreenshot = function() {
  var stream = resumer();
  this.takeScreenshot().then(function(data) {
    stream.queue(new Buffer(data.replace(/^data:image\/png;base64,/, ''), 'base64')).end();
  });
  return stream;
};

module.exports = CustomDriver

2

另一个选择:

使用function.prototype.bind -
创建一堆函数,这些函数的编写方式就好像它们的上下文是一个驱动程序实例:

function myCustomMethod(){
this.seleniumDriverMethodOfSomeSort()
//etc.
}

接着,导出一个单一的包装函数将它们绑定到实例上并分配给方法名称:

function WrapDriverInstance(driver){
    driver.myCustomMethod = myCustomMethod.bind(driver)
}

你甚至可以将所有的方法放在一个数组中,例如[{method : 方法函数, name : '方法名称'}],然后执行以下操作:
function bindAllMyMethodsAtOnce(driver){
    methodArray.forEach(item=>{
         driver[item.name] = item.method.bind(driver)
})
}

或者更加疯狂一些,利用.bind()让你进行部分函数应用:

function customClicker(selector){
    return this.findElement(By.css(selector)).click()
}
function customSendKeys(selector,keys){
return this.findElement(By.css(selector)).sendKeys(keys)
}
var arrayOfElementSelections = [{elementCSS : 'div.myclass', name : 'boxOStuff'}] //etc
function wrapCustomActions(driver){
    arrayOfElementSelections.forEach(element=>{
        driver[element.name+'Clicker'] = customClicker.bind(driver,element.elementCSS)
        driver[element.name+'Keyer'] = customSendKeys.bind(driver,element.elementCSS)
    })
 }

现在,您拥有了一个函数,可以为特定页面上与元素交互的一系列便捷方法初始化驱动程序实例。
您必须记得在驱动程序实例上调用您的包装器,而不是在过载的构造函数上获取“自由”行为。

但是,由于.bind()的部分应用性质,您可以定义更多通用的实用方法,并在封装它们时指定其行为。

因此,您无需为每个测试扩展Driver类,只需创建几个抽象所需实际行为的包装器 - 选择元素,保存截图等等。然后,根据每个页面或功能的基础,将参数(如CSS选择器或文件路径)保存在某个地方,并逐个调用它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接