Nob*_*fer 2 javascript automated-tests angularjs protractor
我正在使用 Protractor 为 AngularJS 应用程序开发自动化测试套件。
在开发我的测试脚本时,我一直在使用,browser.pause()因此在我执行脚本时,我必须手动告诉它继续执行测试的每个步骤。我现在很高兴我的测试正确执行,并希望删除对 的调用browser.pause(),这样我就可以让脚本自行运行完成。
但是,我知道,如果browser.pause()不添加任何内容以允许我的测试在执行下一步之前暂停/等待浏览器加载(目前,我需要花费的时间来告诉browser.pause()运行调用后继续的脚本足以让浏览器加载下一步测试所需的元素)。
我正在尝试使用browser.wait()此方法,将每个测试的最后一行作为参数传递给browser.wait(),以及超时值(即 10 秒)。例如:
it('should log the user in to the application', function() {
browser.wait(loginAsUser(username, password), 10000);
});
Run Code Online (Sandbox Code Playgroud)
该测试最初只是:
browser.pause();
it('should log the user in to the application', function() {
loginAsUser(username, password);
});
Run Code Online (Sandbox Code Playgroud)
即对browser.pause()测试用例外部的调用将导致浏览器在每个测试的每个步骤之间暂停。
该loginAsUser()函数定义为:
function loginAsUser(username, password) {
usernameInputField.sendKeys(username);
passwordInputField.sendKeys(password);
loginBtn.click();
}
Run Code Online (Sandbox Code Playgroud)
当我当前运行我的测试脚本时,browser.wait()与登录测试一样添加到每个测试的最后一个可执行行后,我在第一个测试(上面的日志)中遇到以下失败,其余测试都失败了,因为它们取决于那个通过:
失败:等待条件必须是类似承诺的对象、函数或条件对象
我不明白为什么我会收到这个错误,因为wait条件是一个函数……它被赋予了loginAsUser()我在上面定义的函数……?谁能向我解释我在这里做错了什么?
编辑
因此,似乎问题实际上出在我其余的测试中(即login首先运行测试,然后在它之后依次运行一些其他测试)。
使用我最初的登录测试,测试用例当前正确登录,但是下一个要运行的测试失败,给出错误:
失败:使用定位器找不到元素:By(link text, Pages)
这似乎失败了,因为页面在登录后没有时间加载,当我运行测试并调用browser.pause().
要运行的下一个测试是:
it('should display the Pages menu', function() {
browser.waitForAngularEnabled(false);
browser.actions().mouseMove(pagesMenuBtn).perform();
expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
browser.actions().mouseMove(userCircle).perform();
expect(pageTagBrowserBtn.isDisplayed()).toBeFalsy();
browser.waitForAngularEnabled(true);
});
Run Code Online (Sandbox Code Playgroud)
该pagesMenuBtn定义为一个全局变量:
var pagesMenuBtn = element(by.linkText("Pages"));
Run Code Online (Sandbox Code Playgroud)
因此,在运行下一个测试之前,似乎我需要以某种方式为我的应用程序在登录后加载页面提供时间,否则将找不到该元素。
编辑
我尝试browser.wait()在“页面”测试中添加一个调用,以便测试在将光标悬停在按钮上之前等待按钮显示:
it('should display the Pages menu', function() {
browser.waitForAngularEnabled(false);
browser.wait(function() {
pagesMenuBtn.isDisplayed().then(function(isDisplayed){
/* if(!isDisplayed) {
console.log("Display Pages menu test returning false ");
return false;
}
console.log("Display Pages menu test returning true "); */
//return true;
browser.actions().mouseMove(pagesMenuBtn).perform().then(function(isDisplayed){
expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
});
browser.actions().mouseMove(userCircle).perform().then(function(isDisplayed){
expect(pageTagBrowserBtn.isDisplayed()).toBeFalsy();
});
});
}, 5000);
});
Run Code Online (Sandbox Code Playgroud)
但我仍然得到同样的错误:
失败:使用定位器找不到元素:By(link text, Pages)
表示无法找到测试试图悬停的按钮(即,在测试脚本试图点击它时,它似乎没有加载到浏览器中)。
编辑
好的,所以我再次更新了我的测试 - 现在看起来像这样:
it('should display the Pages menu', function() {
browser.waitForAngularEnabled(false);
browser.wait(EC.visibilityOf(pagesMenuBtn), 5000).then(
browser.actions().mouseMove(pagesMenuBtn).perform().then(function(){
expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
})).then(
browser.actions().mouseMove(userCircle).perform().then(function(){
expect(pageTagBrowserBtn.isDisplayed()).toBeFalsy();
}));
});
Run Code Online (Sandbox Code Playgroud)
我的意图是浏览器会等待pagesMenuBtn元素被显示,然后,一旦显示,光标就会移动到按钮上,一旦发生,它应该检查pageTagBrowserBtn元素是否被显示(期望它返回一个“真”值)。然后光标将移动到页面上的另一个元素 ( userCircle),并再次检查 是否pageTagBrowserBtn已显示(这次期望它返回一个“假”值)。
但是,当我现在运行测试时,它失败了,说明:
预期假为真
我不确定这是为什么......?据我了解,测试应该EC.visibilityOf(pagesMenuBtn)在尝试继续测试之前等待返回 true的条件......所以我不希望它因为值是假而失败 - 如果值是假,它应该等到它是真的再继续 - 至少这是我写的意图。
谁能向我解释这里出了什么问题?
通常,您永远不必等待一段硬编码的时间。
至少等待应该与预期条件配对,以便在满足条件时立即挣脱。
示例助手方法:
public static async waitForPresenceOf(element: ElementFinder, waitMs?: number): Promise<boolean> {
return browser.wait(EC.presenceOf(element), waitMs || 5000).then(() => true, () => false);
}
Run Code Online (Sandbox Code Playgroud)
可以在同一个庄园里等待元素的可见性等。
我将发布我当前的辅助方法集合,以防您可以使用其中的一个或多个(用 TypeScript 编写):
import * as webdriver from 'selenium-webdriver';
import By = webdriver.By;
import {browser, element, protractor, by, WebElement, ElementFinder, ElementArrayFinder} from "protractor";
import * as _ from 'lodash';
import {expect} from "./asserts.config";
let EC = protractor.ExpectedConditions;
export default class BrowserHelper {
public static isAuthenticated: boolean;
public static async getCurrentUrl(): Promise<string> {
return browser.getCurrentUrl();
}
public static async getDesignId(): Promise<string> {
let url = await BrowserHelper.getCurrentUrl();
let designId = '';
let resources = _.split(url, '/');
if (_.includes(resources, 'design')) {
designId = resources[_.indexOf(resources, 'design') + 1];
}
return designId;
}
public static async scrollTo(x: number | string, y: number | string): Promise<void> {
await browser.executeScript(`window.scrollTo(${x},${y});`);
}
public static async scrollToTop(): Promise<void> {
await BrowserHelper.scrollTo(0, 0);
}
public static async scrollToBottom(): Promise<void> {
await BrowserHelper.scrollTo(0, 'document.body.scrollHeight');
}
public static async scrollToLocator(locator: By | Function): Promise<void> {
await browser.executeScript('arguments[0].scrollIntoView(false);', element(locator).getWebElement());
}
public static async scrollToElement(element: ElementFinder): Promise<void> {
await browser.executeScript('arguments[0].scrollIntoView(false);', element);
}
public static async isElementPresent(element: ElementFinder, waitMs?: number): Promise<boolean> {
return browser.wait(element.isPresent(), waitMs || 1000).then(() => true, () => false);
}
public static async getElement(locator: By | Function, waitMs?: number): Promise<ElementFinder | any> {
let isPresent = await BrowserHelper.waitForPresenceOf(element(locator), waitMs || 1000);
return isPresent ? element(locator) : undefined;
}
public static getParent(childElement: ElementFinder, levels?: number) {
let xpath = levels ? '' : '..';
for (let i = 1; i<=levels; i++) {
xpath += (i<levels) ? '../' : '..';
}
return childElement.element(by.xpath(xpath));
}
public static async urlContains(str: string, waitMs?: number): Promise<boolean> {
return browser.wait(EC.urlContains(str), waitMs || 5000).then(() => true, () => false);
}
public static async waitForPresenceOf(element: ElementFinder, waitMs?: number): Promise<boolean> {
return browser.wait(EC.presenceOf(element), waitMs || 5000).then(() => true, () => false);
}
public static async waitForVisibilityOf(element: ElementFinder, waitMs?: number): Promise<boolean> {
return browser.wait(EC.visibilityOf(element), waitMs || 5000).then(() => true, (e) => false);
}
public static async waitForInvisiblityOf(element: ElementFinder, waitMs?: number): Promise<any> {
await browser.wait(EC.invisibilityOf(element), waitMs || 5000);
}
public static async isElementDisplayed(element: ElementFinder): Promise<boolean> {
return element.isDisplayed().then(() => true, () => false);
}
public static async hasElement(locator: By | Function, waitMs?: number): Promise<boolean> {
return !!BrowserHelper.getElement(locator, waitMs || 5000);
}
public static async hasClass(element: ElementFinder, className: string, waitMs?: number): Promise<boolean> {
await BrowserHelper.isElementPresent(element, waitMs || 5000);
return new Promise<boolean>((resolve) => {
element.getAttribute('class').then(function (classes) {
let hasClass = classes.split(' ').indexOf(className) !== -1;
resolve(hasClass);
});
})
}
public static async sendKeys(locator: By | Function, keys: string, clear?: boolean, waitMs?: number): Promise<void> {
let element: ElementFinder = await BrowserHelper.getElement(locator, waitMs);
if (clear) {
await element.clear();
}
await element.sendKeys(keys);
}
public static async click(locator: By | Function, waitMs?: number): Promise<void> {
let element = await BrowserHelper.getElement(locator, waitMs);
await element.click();
}
public static async all(locator: By | Function, waitMs?: number): Promise<ElementFinder[]> {
// verify presence while allowing it to take a short while for element to appear
let isPresent: boolean = await BrowserHelper.waitForPresenceOf(element.all(locator).first(), waitMs || 5000);
if (isPresent) {
return element.all(locator);
}
throw new Error('Could not find Elements matching locator: ' + locator)
}
public static async allSub(parentElement: ElementFinder, locator: By | Function): Promise<ElementFinder[]> {
// assuming element is present (e.g. found using getElement)
return parentElement.all(locator);
}
public static async getElementText(element: ElementFinder): Promise<string> {
return element.getText().then((text) => text, (e) => "");
}
public static async getText(locator: By | Function, waitMs?: number): Promise<string> {
let element: ElementFinder = await BrowserHelper.getElement(locator, waitMs || 5000);
return element.getText();
}
public static async allText(locator: By | Function, waitMs?: number): Promise<string[]> {
let textElements: ElementFinder[] = await BrowserHelper.all(locator, waitMs || 5000);
return BrowserHelper.elementText(...textElements);
}
public static async elementText(...elements: ElementFinder[]): Promise<string[]> {
return Promise.all(_.map(elements, (element: ElementFinder) => {
return BrowserHelper.getElementText(element);
}));
}
public static async clickElementByLabel(clickablesLocator: By | Function, labelsLocator: By | Function, labelToClick: string, waitMs?: number): Promise<void> {
let labels: string[] = await BrowserHelper.allText(labelsLocator);
// remove leading and trailing whitespaces
labels = _.map<string, string>(labels, (label) => _.trim(label));
let elements: ElementFinder[] = await BrowserHelper.all(clickablesLocator);
expect(labels.length).to.be.eq(elements.length, `clickElementByLabel must have equal amount of clickables and labels`);
let clickIndex = _.indexOf(labels, labelToClick);
if (clickIndex >= 0) {
let elem = elements[clickIndex];
await BrowserHelper.waitForVisibilityOf(elem, waitMs || 5000);
await elem.click();
}
else {
throw new Error('Did not find Element with label:' + labelToClick)
}
}
public static async clickElementIfPresent(locator: By | Function): Promise<void> {
let isClickable = await BrowserHelper.waitForVisibilityOf(element(locator), 10);
if (isClickable) {
await element(locator).click();
}
}
public static async getSessionKey(key: string): Promise<any> {
return browser.driver.executeScript(`return window.sessionStorage.getItem("${key}");`);
}
// browser.driver.actions() does currently not properly add typings, so wrapping here for convenience
public static actions(): webdriver.ActionSequence {
return browser.driver.actions();
}
public static dragTo(to: webdriver.ILocation): webdriver.ActionSequence {
let actions = BrowserHelper.actions();
// reduce number of actions sent when testing via external selenium driver
// if (process.env.E2E_EXTERNAL_SERVER) {
actions.mouseMove({x: to.x-50, y: to.y});
// ease in to trigger snap suggestion
for (let i = 0; i < 10; i++) {
actions.mouseMove({x: 5, y: 0});
}
// }
// else {
//
// let pxPerStep = 5;
//
// let movedX = 0;
// let movedY = 0;
//
// while (Math.abs(movedX) < Math.abs(to.x) || Math.abs(movedY) < Math.abs(to.y)) {
// let dx = 0;
// let dy = 0;
//
// if (Math.abs(movedX) < Math.abs(to.x)) {
// dx = (to.x > 0) ? pxPerStep : -pxPerStep;
// }
//
// if (Math.abs(movedY) < Math.abs(to.y)) {
// dy = (to.y > 0) ? pxPerStep : -pxPerStep;
// }
//
// actions.mouseMove({x: dx, y: dy});
//
// movedX += dx;
// movedY += dy;
// }
// }
return actions;
}
}
Run Code Online (Sandbox Code Playgroud)
您需要在继续之前等待您的测试用例,为此您需要这样做:
it('should log the user in to the application', function(done) {
BrowserHelper.sendKeys(usernameInputField, username)
.then(function() {
return BrowserHelper.sendKeys(passwordInputField, password)
})
.then(function() {
return BrowserHelper.click(loginBtn)
}).then(done)
});
Run Code Online (Sandbox Code Playgroud)
注意done参数。这告诉测试运行器等待done回调被调用。
你也应该能够通过简单地返回一个承诺来完成同样的事情(至少在我的设置中有效)
it('should log the user in to the application', function() {
return BrowserHelper.sendKeys(usernameInputField, username)
.then(function() {
return BrowserHelper.sendKeys(passwordInputField, password)
})
.then(function() {
return BrowserHelper.click(loginBtn)
})
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3935 次 |
| 最近记录: |