有什么方法可以优化/加速使用量角器向UI发送数据吗?

时间:2021-03-29 02:44:07

I have code similar to this:

我有类似的代码:

ExamPage.prototype.enterDetailsInputData = function (modifier) {
    page.sendKeys(this.modalExamName, 'Test Exam ' + modifier);
    page.sendKeys(this.modalExamVersionId, 'Test exam version ' + modifier);
    page.sendKeys(this.modalExamProductVersionId, 'Test exam product version ' + modifier);
    page.sendKeys(this.modalExamAudienceId, 'Test exam audience ' + modifier);
    page.sendKeys(this.modalExamPublishedId, '2014-06-1' + modifier);
    page.sendKeys(this.modalExamPriceId, '100' + modifier);
    page.sendKeys(this.modalExamDurationId, '6' + modifier);
};

Here's the page.sendKeys function. Note that currently this is not doing any return of promises or anything like that. If the function is not coded well then I welcome comments:

这是页面。sendKeys函数。请注意,目前这不是做任何回报的承诺或类似的事情。如果函数编码不正确,我欢迎您的评论:

// page.sendkeys function
sendKeys(id: string, text: string) {
    element(by.id(id)).sendKeys(text);
} 

I watch as it slowly fills out each field on my screen and then repeats it again and again in more tests that follow.

我看着它慢慢地填满屏幕上的每个字段,然后在接下来的更多测试中不断重复。

Is there any way that this could be optimized or do I have to wait for one field after the other to fill and have to live with tests that take a long time to run?

有什么方法可以优化它吗?或者我必须等待一个又一个字段来填充它,并且必须忍受长时间运行的测试?

I assume sendKeys is promise based. Could I for example use AngularJS $q to issue all the sendKeys at the same time and then use $q to wait for them to complete?

我认为sendKeys是基于承诺的。例如,我可以使用AngularJS $q同时发出所有的sendKeys,然后使用$q等待它们完成吗?

4 个解决方案

#1


10  

Potential Solution I think at least a little hackery is required no matter how you optimize it - protractor doesn't give you this out of the box. However would a small helper function like this suit your needs? What else do you need to speed up sides text inputs with ng-models?

潜在的解决方案我认为无论你如何优化它都至少需要一点技巧——量角器不会给你这个开箱即用的东西。但是像这样的小助手函数适合您的需要吗?你还需要什么来加速边文本输入和ng模型?

function setNgModelToString(element, value) {
    return element.getAttribute('ng-model').then(function (ngModel) {
        element.evaluate('$eval("' + ngModel + ' = \'' + value + '\'") && $digest()');
    });
}

Solution Example:

解决方案示例:

describe('angularjs homepage', function() {
  it('should have a title', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');

    var inputString1 = '';
    var inputString2 = '';
    for (var i = 0; i < 1000; i++) {
        inputString1 += '1';
        inputString2 += '2';
    }

    /* Uncomment this to see it runs much much slower when you enter each key. */
    //element(by.model('second')).sendKeys(inputString1);   

    setNgModelToString(element(by.model('second')), inputString2);

    expect(element(by.model('second')).getAttribute('value')).toEqual(inputString2);
  });
});

Why does the solution work?

为什么解决方案有效?

You need to use $eval to wrap the assignment and not just assignment as evaluate does not evaluate side effects (a nested evaluation, though... heh). Assuming that's truthy in angular expressions then $digest() is called from the &&; this causes a digest to happen, which you need to update everything since you set a value from outside the digest cycle.

您需要使用$eval来包装赋值,而不仅仅是像evaluate那样不评估副作用(不过是嵌套的评估……)哈)。假设这在角度表达式中是真实的,那么$digest()从&&;这将导致发生摘要,您需要更新所有内容,因为您从摘要周期外部设置了一个值。

Thoughts about the solution:

思考解决方案:

The whole idea behind an E2E test is to "emulate" an end-user using your app. This arguably doesn't do that as well as sending the keys one-by-one, or copy-and-paste (since pasting into elements is a valid way of entering input; it's just hard to set up due to flash, etc., see below).

E2E测试背后的整个想法是“模仿”终端用户使用你的应用程序。可以说,这不如逐个发送键或复制粘贴(因为粘贴到元素中是输入输入的有效方式;由于flash等原因,它很难设置,见下文)。

Other Potential Solutions:

其他可能的解决方案:

  • Copy and Paste: Create an element, enter text, copy it, paste the text sending Ctrl + V to the target element. This may require doing a bunch of fancy footwork, like using Flash (exposing the system clipboard is a security risk) and having "copy it" click an invisible flash player. See executeScript to evaluate functions on the target so that you have access to variables like window if you need that.

    复制粘贴:创建一个元素,输入文本,复制它,粘贴发送Ctrl + V到目标元素的文本。这可能需要做一些花哨的工作,比如使用Flash(暴露系统剪贴板是安全风险),并让“复制它”单击一个不可见的Flash播放器。请参阅executeScript,以评估目标上的函数,以便您在需要时可以访问诸如window这样的变量。

  • Parallelizing your tests. Read the official doc here and search for "shard" and then "multiple". If you're mainly worried about the duration of your entire test collection and not individual tests, scaling out your browser count is probably the way to go. However there's a good chance you are TDD-ing or something, hence needing each test to run faster.

    并行测试。在这里阅读官方文档,搜索“碎片”,然后是“多重”。如果您主要担心的是整个测试集合的持续时间,而不是单独的测试,那么扩展您的浏览器计数可能是一种方法。然而,您很有可能是tdd或其他东西,因此需要每个测试运行得更快。

#2


3  

You can do following -

你可以做以下的事

ExamPage.prototype.enterDetailsInputData = function (modifier) {
var arr=[
{id:this.modalExamName, text:'Test Exam ' + modifier},
{id:this.modalExamVersionId, text:'Test exam version ' + modifier },
{id:this.modalExamProductVersionId, text:'Test exam product version ' + modifier},
{id:this.modalExamAudienceId,text:'Test exam audience ' + modifier},
{id:this.modalExamPublishedId, text:'2014-06-1' + modifier},
{id:this.modalExamPriceId, text:'100' + modifier},
{this.modalExamDurationId, text:'6' + modifier}
];
var Q = require('q'),
    promises = [];
for (var i = 0; i < arr.length; i++) {
    promises.push(page.sendKeys(arr[i].id, arr[i].text));
}

Q.all(promises).done(function () {
    //do here whatever you want
});

};

sendKeys returns promise by default. See here -https://github.com/angular/protractor/blob/master/docs/api.md#api-webdriver-webelement-prototype-sendkeys

sendKeys默认返回承诺。在这里看到的https:/ /github.com/angular/protractor/blob/master/docs/api.md # api-webdriver-webelement-prototype-sendkeys

sendKeys(id: string, text: string) {
    return element(by.id(id)).sendKeys(text);
} 

#3


2  

If you really want to speed up the process of manipulating DOM in any way (including filling up data forms) one option to consider is to use: browser.executeScript or browser.executeAsyncScript. In such a case the webdriver let the browser execute the script on its own -- the only overhead is to send the script body to the browser, so I do not think there may be anything faster.

如果您真的希望以任何方式(包括填写数据表单)加快处理DOM的进程,您需要考虑的一个选项是:浏览器。executeScript或browser.executeAsyncScript。在这种情况下,webdriver允许浏览器自己执行脚本——惟一的开销就是将脚本主体发送到浏览器,所以我认为没有比这更快的了。

From what I see, you identify DOM elements by ids so it should smoothly work with the approach I propose.

在我看来,您可以通过id识别DOM元素,这样它就可以顺利地使用我建议的方法。

Here is a scaffold of it -- tested it and it works fine:

这是一个支架,经过测试,效果很好:

browser.get('someUrlToBeTested');
browser.waitForAngular();
browser.executeScript(function(param1, param2, param3){

        // form doc: arguments may be a boolean, number, string, or webdriver.WebElement
        // here all are strings: param1 = "someClass", param2 = "someId", param3 = "someModifier"
        var el1 = document.getElementsByClassName(param1)[0];
        var el2 = document.getElementById(param2);

        el1.setAttribute('value', 'yoohoo ' + param3);
        el2.setAttribute('value', 'hooyoo ' + param3);

        // depending on your context it will probably
        // be needed to manually digest the changes
        window.angular.element(el1).scope().$apply();


},'someClass', 'someId', 'someModifier')

small remark: If you pass webdriver.WebElement as one of your argument it will be cast down to the corresponding DOM element.

注意:如果你通过了网络司机。WebElement作为您的一个参数,将被转换为相应的DOM元素。

#4


0  

I also used browser.executeScript. But I didn't need to use $apply waitForAngular

我也用browser.executeScript。但是我不需要使用$apply waitforangle

I add a script when E2E tests are running, and put a function on global scope, don't worry it's only during E2E tests, and you can namespace if you want to.

当E2E测试运行时,我添加了一个脚本,并在全局范围内放置一个函数,不要担心它只是在E2E测试期间,如果您愿意,您可以命名空间。

'use strict';
function fastSendKeys(testId, value) {
    //test id is just an attribute we add in our elements
    //you can use whatever selector you want, test-id is just easy and repeatable
    var element = jQuery('[test-id=' + '"' + testId + '"' + ']');
    element.val(value);
    element.trigger('input');
}

Then in your protractor test something like this (in a page object):

然后在量角器测试中(在页面对象中):

this.enterText = function (testId, value) {
        var call = 'fastSendKeys(' + '"' + testId + '"' + ',' + '"' + value + '"' + ')';
        console.log('call is ', call);
        browser.executeScript(call);
    }; 

#1


10  

Potential Solution I think at least a little hackery is required no matter how you optimize it - protractor doesn't give you this out of the box. However would a small helper function like this suit your needs? What else do you need to speed up sides text inputs with ng-models?

潜在的解决方案我认为无论你如何优化它都至少需要一点技巧——量角器不会给你这个开箱即用的东西。但是像这样的小助手函数适合您的需要吗?你还需要什么来加速边文本输入和ng模型?

function setNgModelToString(element, value) {
    return element.getAttribute('ng-model').then(function (ngModel) {
        element.evaluate('$eval("' + ngModel + ' = \'' + value + '\'") && $digest()');
    });
}

Solution Example:

解决方案示例:

describe('angularjs homepage', function() {
  it('should have a title', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');

    var inputString1 = '';
    var inputString2 = '';
    for (var i = 0; i < 1000; i++) {
        inputString1 += '1';
        inputString2 += '2';
    }

    /* Uncomment this to see it runs much much slower when you enter each key. */
    //element(by.model('second')).sendKeys(inputString1);   

    setNgModelToString(element(by.model('second')), inputString2);

    expect(element(by.model('second')).getAttribute('value')).toEqual(inputString2);
  });
});

Why does the solution work?

为什么解决方案有效?

You need to use $eval to wrap the assignment and not just assignment as evaluate does not evaluate side effects (a nested evaluation, though... heh). Assuming that's truthy in angular expressions then $digest() is called from the &&; this causes a digest to happen, which you need to update everything since you set a value from outside the digest cycle.

您需要使用$eval来包装赋值,而不仅仅是像evaluate那样不评估副作用(不过是嵌套的评估……)哈)。假设这在角度表达式中是真实的,那么$digest()从&&;这将导致发生摘要,您需要更新所有内容,因为您从摘要周期外部设置了一个值。

Thoughts about the solution:

思考解决方案:

The whole idea behind an E2E test is to "emulate" an end-user using your app. This arguably doesn't do that as well as sending the keys one-by-one, or copy-and-paste (since pasting into elements is a valid way of entering input; it's just hard to set up due to flash, etc., see below).

E2E测试背后的整个想法是“模仿”终端用户使用你的应用程序。可以说,这不如逐个发送键或复制粘贴(因为粘贴到元素中是输入输入的有效方式;由于flash等原因,它很难设置,见下文)。

Other Potential Solutions:

其他可能的解决方案:

  • Copy and Paste: Create an element, enter text, copy it, paste the text sending Ctrl + V to the target element. This may require doing a bunch of fancy footwork, like using Flash (exposing the system clipboard is a security risk) and having "copy it" click an invisible flash player. See executeScript to evaluate functions on the target so that you have access to variables like window if you need that.

    复制粘贴:创建一个元素,输入文本,复制它,粘贴发送Ctrl + V到目标元素的文本。这可能需要做一些花哨的工作,比如使用Flash(暴露系统剪贴板是安全风险),并让“复制它”单击一个不可见的Flash播放器。请参阅executeScript,以评估目标上的函数,以便您在需要时可以访问诸如window这样的变量。

  • Parallelizing your tests. Read the official doc here and search for "shard" and then "multiple". If you're mainly worried about the duration of your entire test collection and not individual tests, scaling out your browser count is probably the way to go. However there's a good chance you are TDD-ing or something, hence needing each test to run faster.

    并行测试。在这里阅读官方文档,搜索“碎片”,然后是“多重”。如果您主要担心的是整个测试集合的持续时间,而不是单独的测试,那么扩展您的浏览器计数可能是一种方法。然而,您很有可能是tdd或其他东西,因此需要每个测试运行得更快。

#2


3  

You can do following -

你可以做以下的事

ExamPage.prototype.enterDetailsInputData = function (modifier) {
var arr=[
{id:this.modalExamName, text:'Test Exam ' + modifier},
{id:this.modalExamVersionId, text:'Test exam version ' + modifier },
{id:this.modalExamProductVersionId, text:'Test exam product version ' + modifier},
{id:this.modalExamAudienceId,text:'Test exam audience ' + modifier},
{id:this.modalExamPublishedId, text:'2014-06-1' + modifier},
{id:this.modalExamPriceId, text:'100' + modifier},
{this.modalExamDurationId, text:'6' + modifier}
];
var Q = require('q'),
    promises = [];
for (var i = 0; i < arr.length; i++) {
    promises.push(page.sendKeys(arr[i].id, arr[i].text));
}

Q.all(promises).done(function () {
    //do here whatever you want
});

};

sendKeys returns promise by default. See here -https://github.com/angular/protractor/blob/master/docs/api.md#api-webdriver-webelement-prototype-sendkeys

sendKeys默认返回承诺。在这里看到的https:/ /github.com/angular/protractor/blob/master/docs/api.md # api-webdriver-webelement-prototype-sendkeys

sendKeys(id: string, text: string) {
    return element(by.id(id)).sendKeys(text);
} 

#3


2  

If you really want to speed up the process of manipulating DOM in any way (including filling up data forms) one option to consider is to use: browser.executeScript or browser.executeAsyncScript. In such a case the webdriver let the browser execute the script on its own -- the only overhead is to send the script body to the browser, so I do not think there may be anything faster.

如果您真的希望以任何方式(包括填写数据表单)加快处理DOM的进程,您需要考虑的一个选项是:浏览器。executeScript或browser.executeAsyncScript。在这种情况下,webdriver允许浏览器自己执行脚本——惟一的开销就是将脚本主体发送到浏览器,所以我认为没有比这更快的了。

From what I see, you identify DOM elements by ids so it should smoothly work with the approach I propose.

在我看来,您可以通过id识别DOM元素,这样它就可以顺利地使用我建议的方法。

Here is a scaffold of it -- tested it and it works fine:

这是一个支架,经过测试,效果很好:

browser.get('someUrlToBeTested');
browser.waitForAngular();
browser.executeScript(function(param1, param2, param3){

        // form doc: arguments may be a boolean, number, string, or webdriver.WebElement
        // here all are strings: param1 = "someClass", param2 = "someId", param3 = "someModifier"
        var el1 = document.getElementsByClassName(param1)[0];
        var el2 = document.getElementById(param2);

        el1.setAttribute('value', 'yoohoo ' + param3);
        el2.setAttribute('value', 'hooyoo ' + param3);

        // depending on your context it will probably
        // be needed to manually digest the changes
        window.angular.element(el1).scope().$apply();


},'someClass', 'someId', 'someModifier')

small remark: If you pass webdriver.WebElement as one of your argument it will be cast down to the corresponding DOM element.

注意:如果你通过了网络司机。WebElement作为您的一个参数,将被转换为相应的DOM元素。

#4


0  

I also used browser.executeScript. But I didn't need to use $apply waitForAngular

我也用browser.executeScript。但是我不需要使用$apply waitforangle

I add a script when E2E tests are running, and put a function on global scope, don't worry it's only during E2E tests, and you can namespace if you want to.

当E2E测试运行时,我添加了一个脚本,并在全局范围内放置一个函数,不要担心它只是在E2E测试期间,如果您愿意,您可以命名空间。

'use strict';
function fastSendKeys(testId, value) {
    //test id is just an attribute we add in our elements
    //you can use whatever selector you want, test-id is just easy and repeatable
    var element = jQuery('[test-id=' + '"' + testId + '"' + ']');
    element.val(value);
    element.trigger('input');
}

Then in your protractor test something like this (in a page object):

然后在量角器测试中(在页面对象中):

this.enterText = function (testId, value) {
        var call = 'fastSendKeys(' + '"' + testId + '"' + ',' + '"' + value + '"' + ')';
        console.log('call is ', call);
        browser.executeScript(call);
    };