我们很高兴地宣布,Nightwatch 的下一个主要版本已 在 NPM 上以 alpha 预发布版本的形式提供。它包含大量针对编写和运行测试的新功能和改进,以及对 W3C WebDriver 兼容浏览器的全面跨浏览器测试支持。
底层架构已完全重构,以使用官方的 selenium-webdriver Node.js 库与浏览器驱动程序进行通信。这意味着更好的跨浏览器集成、更可靠的 DOM 元素命令处理,以及总体上更稳定、更快的测试。
alpha 版本的目标是收集一些反馈,识别和修复重大错误,同时最终确定新的 API 并更新文档。因此,告知我们遇到的任何重大问题非常重要,以便我们尽可能顺利地从 v1.x 升级。本文章末尾提到了几个重大更改,但它们应该相对较小。
我们还将继续为现有的 v1.7 版本发布补丁和重要修复。以下是 v2.0 中的新功能、改进和其他更改的概述。
要安装 alpha 版本,请运行以下命令:
npm i nightwatch@alpha
支持 WebDriver Actions API
WebDriver 提供了一个全面的 API,用于生成称为 Actions API 的复杂用户手势。此 API 可在 Nightwatch 中使用,并通过现有的 .perform()
命令随时可以使用。perform()
命令的先前功能仍然存在,并以与以前相同的方式工作。
以下是如何使用新的 actions api 的基本示例:
try {
const performResult = await browser.perform(function() {
const actions = this.actions({async: true});
return actions
.keyDown(Key.SHIFT)
.keyUp(Key.SHIFT);
});
console.log('perform', performResult)
} catch (err) {
console.error(err)
}
Selenium 文档网站上的 API 文档 中有更多示例。在上面的示例中,使用 this.actions(<options>)
创建了 Actions
类实例。.perform()
在末尾(在 selenium 文档中需要)应该在 Nightwatch 中省略,因为它将自动调用。
支持 Chrome DevTools 协议
两者 ChromeDriver 和 EdgeDriver 公开了一些用于处理其各自浏览器的特定命令。
使用 ChromeDriver 或 EdgeDriver 时,现在可以通过 Chrome DevTools 协议 执行命令。以下是 Nightwatch browser
对象上的 chrome
命名空间中可用的完整命令列表:
browser.chrome
- .launchApp()
- .getNetworkConditions()
- .setNetworkConditions()
- .sendDevToolsCommand()
- .sendAndGetDevToolsCommand()
- .setPermission()
- .setDownloadPath()
- .getCastSinks()
- .setCastSinkToUse()
- .startCastTabMirroring()
- .getCastIssueMessage()
- .stopCasting()
更多信息
新的 Firefox 特定命令
FirefoxDriver 公开了一些特定命令,例如用于设置上下文以运行“特权”javascript 代码或用于处理加载项。这些命令现在可以直接在 Nightwatch 上的 firefox
命名空间中使用。
browser.firefox
更多信息
新的 .ensure 断言
新的 .ensure
命名空间基于 until 模块,该模块来自 selenium-webdriver.
示例
describe('demo test for .ensure', function() {
test('basic test', function(browser) {
browser
.url('https://nightwatch.node.org.cn')
.ensure.titleMatches(/Nightwatch.js/)
.ensure.elementIsVisible('#index-container') });
});
新的 element() 全局方法和对使用 WebElements 的支持
新添加的 element()
全局方法可用于在测试用例之外预构建元素对象。也可以使用新添加的 by()
全局实用程序,它等效于使用 By()
类(来自 selenium-webdriver)来创建元素定位器。
此外,browser
对象也作为全局对象可用,因此无需将其作为参数传递给测试,因为 Nightwatch v1.x 中就是这种情况。
还可以通过在 nightwatch 配置文件中将 disable_global_apis
设置为 true
来禁用全局 API。
示例
const assert = require('assert');
const {WebElement} = require('selenium-webdriver');
describe('demo element() global', function() {
const signupEl = element(by.css('#signupSection'));
const loginEl = element('#weblogin');
test('element globals command', async function() {
const tagName = await browser.waitForElementPresent(loginEl, 100).getTagName(loginEl);
assert.strictEqual(tagName, 'div');
// use elements created with element() to regular nightwatch assertions
browser.assert.visible(loginEl);
// use elements created with element() to expect assertions
browser.expect.element(loginEl).to.be.visible;
// retrieve the WebElement instance
const loginWebElement = await loginEl.getWebElement();
assert.ok(loginWebElement instanceof WebElement);
});
});
直接使用 Selenium WebDriver 对象
WebDriver
实例也作为 Nightwatch api 对象上的 driver
属性可用。
如果要链接 WebDriver 特定命令,则需要将它们包装在 perform()
或 waitUntil()
命令中。
示例
describe('demo driver object', function() {
it('get browser logs – classic', function() {
browser
.url('https://nightwatch.node.org.cn')
.waitForElementVisible('body')
.perform(function() {
this.driver.manage().logs().get('browser').then(result => {
console.log('Browser logs:', result)
})
});
});
it('get browser logs – with ES6 async/await', async function() {
await browser.url('https://nightwatch.node.org.cn').waitForElementVisible('body');
const logs = await browser.driver.manage().logs().get('browser');
console.log('Browser logs:', logs)
});
});
在 Nightwatch 中使用 WebDriver BiDi
基于 Selenium WebDriver 意味着 WebDriver 的最新功能和能力将直接在 Nightwatch 中可用,例如即将推出的 Webdriver BiDi 协议,被认为是“跨浏览器自动化的未来”。
WebDriver BiDi 是用于与浏览器通信的新协议,定义为一个新的 W3C 规范,目前正在开发中。
Selenium 4 中提供了早期支持,它已在 ChromeDriver 中通过 Chrome Developer Tools 提供。
WebDriver Bidi 允许用户在浏览器事件发生时捕获这些事件,而不是使用 WebDriver 用于其他 API 的传统请求/响应方法。
在内部,WebDriver 将创建 WebSocket 连接到浏览器,以传输事件和命令。
示例
以下示例通过 WebSocket 上的 WebDriver 双向连接调用 CDP 中的 Page.getLayoutMetrics
方法。
describe('demo webdriver bidirectional', function() {
it('samepl test bidi', async function(browser) {
await browser.url('https://nightwatch.node.org.cn/');
const cdpConnection = await browser.driver.createCDPConnection('page');
browser.assert.ok(cdpConnection._wsConnection && cdpConnection._wsConnection._url.startsWith('ws://'),
CDP connection is successful to: ${cdpConnection._wsConnection._url});
const layoutMetrics = await browser.perform(function(callback) {
const id = 99;
cdpConnection._wsConnection.on('message', function getLayoutMetrics(message) {
const params = JSON.parse(message)
if (params.id === 99) {
cdpConnection._wsConnection.off('message', getLayoutMetrics);
callback(params.result);
}
});
cdpConnection.execute('Page.getLayoutMetrics', id, {});
});
console.log('Layout Metrics:', layoutMetrics)
});
});
新的 API 命令
添加了一些新命令,并且还改进了几个现有命令的兼容性。
browser.getAccessibleName(<selector> | <WebElement>
)
返回元素的计算的 WAI-ARIA 标签。
const result = await browser.getAccessibleName('input[type=search]');
browser.getAriaRole(<selector> | <WebElement>
)
返回元素的计算的 WAI-ARIA 角色。
const result = await browser.getAriaRole('input[type=search]');
browser.takeElementScreenshot(<selector> | <WebElement>
)
截取元素边界矩形所包围的可见区域的屏幕截图。
const data = await browser.takeElementScreenshot('#container');
require('fs').writeFile('out.png', data, 'base64');
browser.uploadFile(<selector> | <WebElement>
)
使用绝对文件路径将文件上传到元素。
await browser.uploadFile('#myFile', '/path/to/file.pdf');
browser.waitUntil(<conditionFunction>
, [optionalMaxTimeout]
, [optionalRetryInterval]
, [optionalCallback]
)
一个通用命令,它可以使测试运行器等待条件评估为“真”值。条件可以通过任何返回要评估的值的函数或等待的 Promise 来指定。如果条件不满足,将抛出 TimeoutError
,测试将失败。
let conditionValue;
await browser.waitUntil(function() {
return conditionValue === true;
});
await browser.waitUntil(async function() {
const title = await this.execute(function() {
return document.title;
});
return title === 'Nightwatch.js';
}, 1000);
改进对使用 async/await 的支持
我们将 Nightwatch 命令的结果格式更改为在使用 await
运算符时直接返回值。
传递给回调的值与 v1.x 中的值相同。可以通过在 nightwatch 配置文件中将 backwards_compatibility_mode
设置为 true
来禁用此行为。
示例
使用 await
获取值
const value = await browser.getText('#weblogin');
console.log('Value is:', value);
使用回调获取值
browser.getText('#weblogin', function(result) {
console.log('Value is:', result.value);});
更多定义 WebDriver 功能的方法
现在可以通过在 nightwatch.conf.js 文件中将 Selenium Capabilities 对象的实例设置为 capabilities
值来定义会话功能。
您可以参考 Selenium 文档以获取所有可用的功能。以下是在 nightwatch.conf.js
中为无头模式下的 Chrome 定义 capabilities
对象的示例:
示例
// nightwatch.conf.js
const chrome = require('selenium-webdriver/chrome');
const capabilities = new chrome.Options();
capabilities.headless();
module.exports = {
test_settings: {
chrome: {
capabilities,
webdriver: {
start_process: true,
server_path: require('chromedriver').path,
cli_args: [
// --verbose
]
}
}
}
};
新的配置设置
以下是 v2.0 中引入的新设置及其默认值
module.exports = {
// Set this to true to use the v1.x response format for commands when using ES6 async/await
backwards_compatibility_mode: false,
// Set this to true to disable the global objects such as element(), browser, by(), expect()
disable_global_apis: false,
// Ignore network errors (e.g. ECONNRESET errors)
report_network_errors: true,
// Interactive element commands such as "click" or "setValue" can be retried if an error occurred (such as an "element not interactable" error)
element_command_retries: 2,
// Sets the initial window size, defined as an object with "width" and "height" numerical properties
window_size: null
}
新的 WebDriver 配置设置
以下是 v2.0 中为各种浏览器驱动程序引入的新的 webdriver
设置
module.exports = {
webdriver: {
// Sets the path to the Chrome binary to use. On Mac OS X, this path should reference the actual Chrome executable, not just the application binary (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
chrome_binary: '',
// Sets the path to Chrome's log file. This path should exist on the machine that will launch Chrome.
chrome_log_file: '',
// Configures the ChromeDriver to launch Chrome on Android via adb.
android_chrome: false,
// Sets the path to the Edge binary to use.
edge_binary: '',
// Sets the path to the Edge binary to use.
edge_log_file: '',
// Sets the binary to use. The binary may be specified as the path to a Firefox executable or a desired release Channel.
firefox_binary: '',
// Sets the path to an existing profile to use as a template for new browser sessions. This profile will be copied for each new session - changes will not be applied to the profile itself.
firefox_profile: ''
}
}
重大更改
我们已尽力最大限度地减少重大更改的数量,但其中一些更改很难避免。还删除了一些已弃用的功能。
以下是摘要。如果您在从 1.5 或更高版本升级后发现其他问题,请在 Github 上告知我们。
- 使用 ES6 async/await 测试用例时,Nightwatch 命令的结果值不包含
status
和value
属性,而是仅包含值(可以通过在 nightwatch 配置文件中将backwards_compatibility_mode
设置为true
来反转此操作)。 setValue
现在在发送按键之前清除值。sendKeys
不再是setValue
的别名,因为它不清除值,而是简单地发送键。
元素定位错误时结果对象的更改
- 包含
error
属性,该属性是 Error 对象实例。 - 不再包含
httpStatusCode
属性。 - 不再包含
value
属性。 - 已删除
proxy-agent
作为依赖项,因为它经常导致依赖项问题;可以从 NPM 单独安装 proxy-agent 包,并以相同的方式使用。