我們很高興宣布 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。這已透過現有的 .perform()
命令在 Nightwatch 中提供並可隨時使用。 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)
}
API 文件中的更多範例,請參閱 Selenium 文件網站。在上面的範例中, Actions
類別的實例是使用 this.actions(<options>)
建立的。結尾的 .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.dev.org.tw')
.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.dev.org.tw')
.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.dev.org.tw').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 中提供了早期支援,並且已透過 Chrome 開發人員工具在 ChromeDriver 中提供。
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.dev.org.tw/');
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 套件,並以相同的方式使用。
回饋
請隨時在我們的 Github Issue 上提交錯誤,或在我們的 討論區頁面上提交一般回饋。感謝您的協助!