selenium: Upgrade from webdriver v4 to v5
* Options no longer needed or no longer exist in wdio v5:
- coloredLogs: Now always on. The underlying 'chalk' library can still
be influenced via the FORCE_COLOR environment variable.
- screenshotPath: Removed. Was already disabled in our config.
- deprecationWarnings: Meh.
- 'sync: true' – On by default when `@wdio/sync` is installed.
The wdio v5 config generator doesn't recommend setting manually.
* The selenium.sh script was removed. It existed to start and stop
chromedriver for local use by developers. This is now done by
the wdio-chromedriver-service. In WMF CI, Quibble starts its own
chromedriver (as optimisation, reused across gated repos),
which is why the 'selenium-test' entry points remains and skips
this.
* The wdio-mediawiki package now requires wdio v5 and Node 10.
This doesn't affect extension repos because versions are pinned.
Upgrade may happen at the earliest convenience.
* Several WDIO methods changed names or signature. Full list at:
<https://github.com/webdriverio/webdriverio/blob/v5.13.2/CHANGELOG.md#v500-2018-12-20>
Highlights:
- browser.element() is now browser.findElement() with "$()" as alias.
- browser.localStorage replaced by browser.setLocalStorage.
- browser.deleteCookie() requires `name` param. To delete all at once,
there is a new method browser.deleteAllCookies().
- Commands that return data no longer wrapped in `{ value: … }`.
Values are now returned directly.
- Custom config keys are now under browser.config instead of browser.options.
Renamed our username/password keys to be mw-prefixed, to avoid clashes
and reduce confusion with similar config keys.
- browser.click(selector) and browser.getText(selector) no longer exist.
Use $(selector).click() or .getText() instead.
* Fix "no such alert" warning from specs/page.js by removing the apparently
redundant code.
Bug: T234002
Bug: T213268
Change-Id: I908997569ca8457997af30cb29e98ac41fae3b64
This commit is contained in:
parent
50cd683080
commit
1955a8aa56
28 changed files with 5488 additions and 2125 deletions
7293
package-lock.json
generated
7293
package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
package.json
22
package.json
|
|
@ -6,12 +6,21 @@
|
|||
"qunit": "grunt qunit",
|
||||
"doc": "jsduck",
|
||||
"postdoc": "grunt copy:jsduck",
|
||||
"selenium": "bash ./tests/selenium/selenium.sh",
|
||||
"selenium": "wdio ./tests/selenium/wdio.conf.js",
|
||||
"selenium-daily": "npm run selenium-test -- --mochaOpts.grep @daily",
|
||||
"selenium-test": "wdio ./tests/selenium/wdio.conf.js"
|
||||
"selenium-test": "wdio ./tests/selenium/wdio.conf.js --skip-chromedriver"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-config-wikimedia": "0.13.0",
|
||||
"@wdio/cli": "5.13.2",
|
||||
"@wdio/devtools-service": "5.13.2",
|
||||
"@wdio/dot-reporter": "5.13.2",
|
||||
"@wdio/junit-reporter": "5.13.2",
|
||||
"@wdio/local-runner": "5.13.2",
|
||||
"@wdio/mocha-framework": "5.13.2",
|
||||
"@wdio/sauce-service": "5.13.2",
|
||||
"@wdio/sync": "5.13.2",
|
||||
"chromedriver": "73.0.0",
|
||||
"eslint-config-wikimedia": "0.14.1",
|
||||
"grunt": "1.0.4",
|
||||
"grunt-banana-checker": "0.7.0",
|
||||
"grunt-contrib-copy": "1.0.0",
|
||||
|
|
@ -29,11 +38,8 @@
|
|||
"postcss-less": "2.0.0",
|
||||
"qunit": "2.9.1",
|
||||
"stylelint-config-wikimedia": "0.6.0",
|
||||
"wdio-dot-reporter": "0.0.10",
|
||||
"wdio-junit-reporter": "0.4.4",
|
||||
"wdio-chromedriver-service": "5.0.2",
|
||||
"wdio-mediawiki": "file:tests/selenium/wdio-mediawiki",
|
||||
"wdio-mocha-framework": "0.6.4",
|
||||
"wdio-sauce-service": "0.4.14",
|
||||
"webdriverio": "4.14.4"
|
||||
"webdriverio": "5.13.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"wikimedia/server"
|
||||
"wikimedia",
|
||||
"wikimedia/node",
|
||||
"wikimedia/language/es2017"
|
||||
],
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"globals": {
|
||||
"$": false,
|
||||
"browser": false,
|
||||
"mw": false
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ const Page = require( 'wdio-mediawiki/Page' ),
|
|||
Api = require( 'wdio-mediawiki/Api' );
|
||||
|
||||
class CreateAccountPage extends Page {
|
||||
get username() { return browser.element( '#wpName2' ); }
|
||||
get password() { return browser.element( '#wpPassword2' ); }
|
||||
get confirmPassword() { return browser.element( '#wpRetype' ); }
|
||||
get create() { return browser.element( '#wpCreateaccount' ); }
|
||||
get heading() { return browser.element( '#firstHeading' ); }
|
||||
get username() { return $( '#wpName2' ); }
|
||||
get password() { return $( '#wpPassword2' ); }
|
||||
get confirmPassword() { return $( '#wpRetype' ); }
|
||||
get create() { return $( '#wpCreateaccount' ); }
|
||||
get heading() { return $( '#firstHeading' ); }
|
||||
|
||||
open() {
|
||||
super.openTitle( 'Special:CreateAccount' );
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ const Page = require( 'wdio-mediawiki/Page' ),
|
|||
Api = require( 'wdio-mediawiki/Api' );
|
||||
|
||||
class DeletePage extends Page {
|
||||
get reason() { return browser.element( '#wpReason' ); }
|
||||
get watch() { return browser.element( '#wpWatch' ); }
|
||||
get submit() { return browser.element( '#wpConfirmB' ); }
|
||||
get displayedContent() { return browser.element( '#mw-content-text' ); }
|
||||
get reason() { return $( '#wpReason' ); }
|
||||
get watch() { return $( '#wpWatch' ); }
|
||||
get submit() { return $( '#wpConfirmB' ); }
|
||||
get displayedContent() { return $( '#mw-content-text' ); }
|
||||
|
||||
open( title ) {
|
||||
super.openTitle( title, { action: 'delete' } );
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ const Page = require( 'wdio-mediawiki/Page' ),
|
|||
Api = require( 'wdio-mediawiki/Api' );
|
||||
|
||||
class EditPage extends Page {
|
||||
get content() { return browser.element( '#wpTextbox1' ); }
|
||||
get conflictingContent() { return browser.element( '#wpTextbox2' ); }
|
||||
get displayedContent() { return browser.element( '#mw-content-text .mw-parser-output' ); }
|
||||
get heading() { return browser.element( '#firstHeading' ); }
|
||||
get save() { return browser.element( '#wpSave' ); }
|
||||
get previewButton() { return browser.element( '#wpPreview' ); }
|
||||
get content() { return $( '#wpTextbox1' ); }
|
||||
get conflictingContent() { return $( '#wpTextbox2' ); }
|
||||
get displayedContent() { return $( '#mw-content-text .mw-parser-output' ); }
|
||||
get heading() { return $( '#firstHeading' ); }
|
||||
get save() { return $( '#wpSave' ); }
|
||||
get previewButton() { return $( '#wpPreview' ); }
|
||||
|
||||
openForEditing( title ) {
|
||||
super.openTitle( title, { action: 'edit' } );
|
||||
|
|
|
|||
|
|
@ -3,16 +3,15 @@ const Page = require( 'wdio-mediawiki/Page' ),
|
|||
Util = require( 'wdio-mediawiki/Util' );
|
||||
|
||||
class HistoryPage extends Page {
|
||||
get heading() { return browser.element( '#firstHeading' ); }
|
||||
get headingText() { return browser.getText( '#firstHeading' ); }
|
||||
get comment() { return browser.element( '#pagehistory .comment' ); }
|
||||
get rollback() { return browser.element( '.mw-rollback-link' ); }
|
||||
get rollbackLink() { return browser.element( '.mw-rollback-link a' ); }
|
||||
get rollbackConfirmable() { return browser.element( '.mw-rollback-link .jquery-confirmable-text' ); }
|
||||
get rollbackConfirmableYes() { return browser.element( '.mw-rollback-link .jquery-confirmable-button-yes' ); }
|
||||
get rollbackConfirmableNo() { return browser.element( '.mw-rollback-link .jquery-confirmable-button-no' ); }
|
||||
get rollbackNonJsConfirmable() { return browser.element( '.mw-htmlform .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label' ); }
|
||||
get rollbackNonJsConfirmableYes() { return browser.element( '.mw-htmlform .mw-htmlform-submit-buttons button' ); }
|
||||
get heading() { return $( '#firstHeading' ); }
|
||||
get comment() { return $( '#pagehistory .comment' ); }
|
||||
get rollback() { return $( '.mw-rollback-link' ); }
|
||||
get rollbackLink() { return $( '.mw-rollback-link a' ); }
|
||||
get rollbackConfirmable() { return $( '.mw-rollback-link .jquery-confirmable-text' ); }
|
||||
get rollbackConfirmableYes() { return $( '.mw-rollback-link .jquery-confirmable-button-yes' ); }
|
||||
get rollbackConfirmableNo() { return $( '.mw-rollback-link .jquery-confirmable-button-no' ); }
|
||||
get rollbackNonJsConfirmable() { return $( '.mw-htmlform .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label' ); }
|
||||
get rollbackNonJsConfirmableYes() { return $( '.mw-htmlform .mw-htmlform-submit-buttons button' ); }
|
||||
|
||||
open( title ) {
|
||||
super.openTitle( title, { action: 'history' } );
|
||||
|
|
@ -29,7 +28,7 @@ class HistoryPage extends Page {
|
|||
}
|
||||
|
||||
vandalizePage( name, content ) {
|
||||
const vandalUsername = 'Evil_' + browser.options.username;
|
||||
const vandalUsername = 'Evil_' + browser.config.mwUser;
|
||||
|
||||
browser.call( function () {
|
||||
return Api.edit( name, content );
|
||||
|
|
@ -37,7 +36,7 @@ class HistoryPage extends Page {
|
|||
|
||||
browser.call( function () {
|
||||
return Api.createAccount(
|
||||
vandalUsername, browser.options.password
|
||||
vandalUsername, browser.config.mwPwd
|
||||
);
|
||||
} );
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
const Page = require( 'wdio-mediawiki/Page' );
|
||||
|
||||
class PreferencesPage extends Page {
|
||||
get realName() { return browser.element( '#mw-input-wprealname' ); }
|
||||
get save() { return browser.element( '#prefcontrol' ); }
|
||||
get realName() { return $( '#mw-input-wprealname' ); }
|
||||
get save() { return $( '#prefcontrol' ); }
|
||||
|
||||
open() {
|
||||
super.openTitle( 'Special:Preferences' );
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const Page = require( 'wdio-mediawiki/Page' );
|
||||
|
||||
class RecentChangesPage extends Page {
|
||||
get changesList() { return browser.element( '.mw-changeslist' ); }
|
||||
get changesList() { return $( '.mw-changeslist' ); }
|
||||
get titles() { return this.changesList.$$( '.mw-changeslist-title' ); }
|
||||
|
||||
open() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
const Page = require( 'wdio-mediawiki/Page' );
|
||||
|
||||
class RestorePage extends Page {
|
||||
get reason() { return browser.element( '#wpComment' ); }
|
||||
get submit() { return browser.element( '#mw-undelete-submit' ); }
|
||||
get displayedContent() { return browser.element( '#mw-content-text' ); }
|
||||
get reason() { return $( '#wpComment' ); }
|
||||
get submit() { return $( '#mw-undelete-submit' ); }
|
||||
get displayedContent() { return $( '#mw-content-text' ); }
|
||||
|
||||
open( subject ) {
|
||||
super.openTitle( 'Special:Undelete/' + subject );
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const Page = require( 'wdio-mediawiki/Page' );
|
|||
|
||||
class UndoPage extends Page {
|
||||
|
||||
get save() { return browser.element( '#wpSave' ); }
|
||||
get save() { return $( '#wpSave' ); }
|
||||
|
||||
undo( title, previousRev, undoRev ) {
|
||||
super.openTitle( title, { action: 'edit', undoafter: previousRev, undo: undoRev } );
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const Page = require( 'wdio-mediawiki/Page' );
|
|||
|
||||
class WatchablePage extends Page {
|
||||
|
||||
get confirmWatch() { return browser.element( '#mw-content-text button[type="submit"]' ); }
|
||||
get confirmWatch() { return $( '#mw-content-text button[type="submit"]' ); }
|
||||
|
||||
watch( title ) {
|
||||
super.openTitle( title, { action: 'watch' } );
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const Page = require( 'wdio-mediawiki/Page' );
|
|||
|
||||
class WatchlistPage extends Page {
|
||||
get titles() {
|
||||
return browser.element( '.mw-changeslist' )
|
||||
return $( '.mw-changeslist' )
|
||||
.$$( '.mw-changeslist-line .mw-title' );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ describe( 'Page', function () {
|
|||
before( function () {
|
||||
// disable VisualEditor welcome dialog
|
||||
BlankPage.open();
|
||||
browser.localStorage( 'POST', { key: 've-beta-welcome-dialog', value: '1' } );
|
||||
browser.setLocalStorage( 've-beta-welcome-dialog', '1' );
|
||||
} );
|
||||
|
||||
beforeEach( function () {
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
content = Util.getTestString( 'beforeEach-content-' );
|
||||
name = Util.getTestString( 'BeforeEach-name-' );
|
||||
} );
|
||||
|
|
@ -30,13 +30,8 @@ describe( 'Page', function () {
|
|||
|
||||
assert.strictEqual( EditPage.heading.getText(), 'Creating ' + name );
|
||||
assert.strictEqual( EditPage.displayedContent.getText(), content );
|
||||
assert( EditPage.content.isVisible(), 'editor is still present' );
|
||||
assert( !EditPage.conflictingContent.isVisible(), 'no edit conflict happened' );
|
||||
// provoke and dismiss reload warning due to unsaved content
|
||||
browser.url( 'data:text/html,Done' );
|
||||
try {
|
||||
browser.alertAccept();
|
||||
} catch ( e ) {}
|
||||
assert( EditPage.content.isDisplayed(), 'editor is still present' );
|
||||
assert( !EditPage.conflictingContent.isDisplayed(), 'no edit conflict happened' );
|
||||
} );
|
||||
|
||||
it( 'should be creatable', function () {
|
||||
|
|
@ -81,7 +76,6 @@ describe( 'Page', function () {
|
|||
|
||||
// check
|
||||
assert.strictEqual( EditPage.heading.getText(), name );
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
assert( EditPage.displayedContent.getText().includes( editContent ) );
|
||||
} );
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ describe( 'Rollback with confirmation', function () {
|
|||
|
||||
before( function () {
|
||||
// disable VisualEditor welcome dialog
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
BlankPage.open();
|
||||
browser.localStorage( 'POST', { key: 've-beta-welcome-dialog', value: '1' } );
|
||||
browser.setLocalStorage( 've-beta-welcome-dialog', '1' );
|
||||
|
||||
// Enable rollback confirmation for admin user
|
||||
// Requires user to log in again, handled by deleteCookie() call in beforeEach function
|
||||
|
|
@ -21,7 +21,7 @@ describe( 'Rollback with confirmation', function () {
|
|||
} );
|
||||
|
||||
beforeEach( function () {
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
|
||||
content = Util.getTestString( 'beforeEach-content-' );
|
||||
name = Util.getTestString( 'BeforeEach-name-' );
|
||||
|
|
@ -45,7 +45,7 @@ describe( 'Rollback with confirmation', function () {
|
|||
it.skip( 'should offer a way to cancel rollbacks', function () {
|
||||
HistoryPage.rollback.click();
|
||||
|
||||
HistoryPage.rollbackConfirmableNo.waitForVisible( 5000 );
|
||||
HistoryPage.rollbackConfirmableNo.waitForDisplayed( 5000 );
|
||||
|
||||
HistoryPage.rollbackConfirmableNo.click();
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ describe( 'Rollback with confirmation', function () {
|
|||
it.skip( 'should perform rollbacks after confirming intention', function () {
|
||||
HistoryPage.rollback.click();
|
||||
|
||||
HistoryPage.rollbackConfirmableYes.waitForVisible( 5000 );
|
||||
HistoryPage.rollbackConfirmableYes.waitForDisplayed( 5000 );
|
||||
|
||||
HistoryPage.rollbackConfirmableYes.click();
|
||||
|
||||
|
|
@ -90,9 +90,9 @@ describe( 'Rollback without confirmation', function () {
|
|||
|
||||
before( function () {
|
||||
// disable VisualEditor welcome dialog
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
BlankPage.open();
|
||||
browser.localStorage( 'POST', { key: 've-beta-welcome-dialog', value: '1' } );
|
||||
browser.setLocalStorage( 've-beta-welcome-dialog', '1' );
|
||||
|
||||
// Disable rollback confirmation for admin user
|
||||
// Requires user to log in again, handled by deleteCookie() call in beforeEach function
|
||||
|
|
@ -101,7 +101,7 @@ describe( 'Rollback without confirmation', function () {
|
|||
} );
|
||||
|
||||
beforeEach( function () {
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
|
||||
content = Util.getTestString( 'beforeEach-content-' );
|
||||
name = Util.getTestString( 'BeforeEach-name-' );
|
||||
|
|
@ -117,7 +117,7 @@ describe( 'Rollback without confirmation', function () {
|
|||
|
||||
// waitUntil indirectly asserts that the content we are looking for is present
|
||||
browser.waitUntil( function () {
|
||||
return HistoryPage.headingText === 'Action complete';
|
||||
return HistoryPage.heading.getText() === 'Action complete';
|
||||
}, 5000, 'Expected rollback page to appear.' );
|
||||
} );
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ describe( 'Special:RecentChanges', function () {
|
|||
name;
|
||||
|
||||
beforeEach( function () {
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
content = Util.getTestString();
|
||||
name = Util.getTestString();
|
||||
} );
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ describe( 'Special:Watchlist', function () {
|
|||
} );
|
||||
|
||||
beforeEach( function () {
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
LoginPage.login( username, password );
|
||||
} );
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ describe( 'User', function () {
|
|||
before( function () {
|
||||
// disable VisualEditor welcome dialog
|
||||
BlankPage.open();
|
||||
browser.localStorage( 'POST', { key: 've-beta-welcome-dialog', value: '1' } );
|
||||
browser.setLocalStorage( 've-beta-welcome-dialog', '1' );
|
||||
} );
|
||||
|
||||
beforeEach( function () {
|
||||
browser.deleteCookie();
|
||||
browser.deleteAllCookies();
|
||||
username = Util.getTestString( 'User-' );
|
||||
password = Util.getTestString();
|
||||
} );
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ module.exports = {
|
|||
* @return {Promise<MWBot>}
|
||||
*/
|
||||
bot(
|
||||
username = browser.options.username,
|
||||
password = browser.options.password,
|
||||
baseUrl = browser.options.baseUrl
|
||||
username = browser.config.mwUser,
|
||||
password = browser.config.mwPwd,
|
||||
baseUrl = browser.config.baseUrl
|
||||
) {
|
||||
const bot = new MWBot();
|
||||
|
||||
|
|
@ -44,9 +44,9 @@ module.exports = {
|
|||
*/
|
||||
edit( title,
|
||||
content,
|
||||
username = browser.options.username,
|
||||
password = browser.options.password,
|
||||
baseUrl = browser.options.baseUrl
|
||||
username = browser.config.mwUser,
|
||||
password = browser.config.mwPwd,
|
||||
baseUrl = browser.config.baseUrl
|
||||
) {
|
||||
return this.bot( username, password, baseUrl )
|
||||
.then( function ( bot ) {
|
||||
|
|
@ -84,14 +84,14 @@ module.exports = {
|
|||
|
||||
// Log in as admin
|
||||
return bot.loginGetCreateaccountToken( {
|
||||
apiUrl: `${browser.options.baseUrl}/api.php`,
|
||||
username: browser.options.username,
|
||||
password: browser.options.password
|
||||
apiUrl: `${browser.config.baseUrl}/api.php`,
|
||||
username: browser.config.mwUser,
|
||||
password: browser.config.mwPwd
|
||||
} ).then( function () {
|
||||
// Create the new account
|
||||
return bot.request( {
|
||||
action: 'createaccount',
|
||||
createreturnurl: browser.options.baseUrl,
|
||||
createreturnurl: browser.config.baseUrl,
|
||||
createtoken: bot.createaccountToken,
|
||||
username: username,
|
||||
password: password,
|
||||
|
|
@ -115,7 +115,7 @@ module.exports = {
|
|||
// block user. default = admin
|
||||
return bot.request( {
|
||||
action: 'block',
|
||||
user: username || browser.options.username,
|
||||
user: username || browser.config.mwUser,
|
||||
reason: 'browser test',
|
||||
token: bot.editToken,
|
||||
expiry
|
||||
|
|
@ -137,7 +137,7 @@ module.exports = {
|
|||
// unblock user. default = admin
|
||||
return bot.request( {
|
||||
action: 'unblock',
|
||||
user: username || browser.options.username,
|
||||
user: username || browser.config.mwUser,
|
||||
reason: 'browser test done',
|
||||
token: bot.editToken
|
||||
} );
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const Page = require( './Page' );
|
||||
|
||||
class BlankPage extends Page {
|
||||
get heading() { return browser.element( '#firstHeading' ); }
|
||||
get heading() { return $( '#firstHeading' ); }
|
||||
|
||||
open() {
|
||||
super.openTitle( 'Special:BlankPage', { uselang: 'en' } );
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
const Page = require( './Page' );
|
||||
|
||||
class LoginPage extends Page {
|
||||
get username() { return browser.element( '#wpName1' ); }
|
||||
get password() { return browser.element( '#wpPassword1' ); }
|
||||
get loginButton() { return browser.element( '#wpLoginAttempt' ); }
|
||||
get userPage() { return browser.element( '#pt-userpage' ); }
|
||||
get username() { return $( '#wpName1' ); }
|
||||
get password() { return $( '#wpPassword1' ); }
|
||||
get loginButton() { return $( '#wpLoginAttempt' ); }
|
||||
get userPage() { return $( '#pt-userpage' ); }
|
||||
|
||||
open() {
|
||||
super.openTitle( 'Special:UserLogin' );
|
||||
|
|
@ -18,7 +18,7 @@ class LoginPage extends Page {
|
|||
}
|
||||
|
||||
loginAdmin() {
|
||||
this.login( browser.options.username, browser.options.password );
|
||||
this.login( browser.config.mwUser, browser.config.mwPwd );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class Page {
|
|||
openTitle( title, query = {}, fragment = '' ) {
|
||||
query.title = title;
|
||||
browser.url(
|
||||
browser.options.baseUrl + '/index.php?' +
|
||||
browser.config.baseUrl + '/index.php?' +
|
||||
querystring.stringify( query ) +
|
||||
( fragment ? ( '#' + fragment ) : '' )
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ See [BlankPage](./BlankPage.js) and [specs/BlankPage](./specs/BlankPage.js) for
|
|||
|
||||
Utilities to interact with the MediaWiki API. Uses the [mwbot](https://github.com/Fannon/mwbot) library.
|
||||
|
||||
Actions are performed logged-in using `browser.options.username` and `browser.options.password`,
|
||||
Actions are performed logged-in using `browser.config.mwUser` and `browser.config.mwPwd`,
|
||||
which typically come from `MEDIAWIKI_USER` and `MEDIAWIKI_PASSWORD` environment variables.
|
||||
|
||||
* `edit(string title, string content [, string username [, string password [, string baseUrl ] ] ])`
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const MWBot = require( 'mwbot' ),
|
|||
|
||||
function getJobCount() {
|
||||
const bot = new MWBot( {
|
||||
apiUrl: `${browser.options.baseUrl}/api.php`
|
||||
apiUrl: `${browser.config.baseUrl}/api.php`
|
||||
} );
|
||||
return bot.request( {
|
||||
action: 'query',
|
||||
|
|
|
|||
|
|
@ -11,11 +11,10 @@ module.exports = {
|
|||
*/
|
||||
waitForModuleState( moduleName, moduleStatus = 'ready', timeout = 2000 ) {
|
||||
browser.waitUntil( () => {
|
||||
const result = browser.execute( ( module ) => {
|
||||
return browser.execute( ( arg ) => {
|
||||
return typeof mw !== 'undefined' &&
|
||||
mw.loader.getState( module.name ) === module.status;
|
||||
mw.loader.getState( arg.name ) === arg.status;
|
||||
}, { status: moduleStatus, name: moduleName } );
|
||||
return result.value;
|
||||
}, timeout, 'Failed to wait for ' + moduleName + ' to be ' + moduleStatus + ' after ' + timeout + ' ms.' );
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ module.exports = {
|
|||
var filename, filePath;
|
||||
// Create sane file name for current test title
|
||||
filename = encodeURIComponent( title.replace( /\s+/g, '-' ) );
|
||||
filePath = `${browser.options.screenshotPath}/${filename}.png`;
|
||||
filePath = `${browser.config.screenshotPath}/${filename}.png`;
|
||||
// Ensure directory exists, based on WebDriverIO#saveScreenshotSync()
|
||||
try {
|
||||
fs.statSync( browser.options.screenshotPath );
|
||||
fs.statSync( browser.config.screenshotPath );
|
||||
} catch ( err ) {
|
||||
fs.mkdirSync( browser.options.screenshotPath );
|
||||
fs.mkdirSync( browser.config.screenshotPath );
|
||||
}
|
||||
// Create and save screenshot
|
||||
browser.saveScreenshot( filePath );
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"specs/"
|
||||
],
|
||||
"engines": {
|
||||
"node" : ">=6.0"
|
||||
"node" : ">=10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mwbot": "1.0.10"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const fs = require( 'fs' ),
|
||||
path = require( 'path' ),
|
||||
startChromedriver = !process.argv.includes( '--skip-chromedriver' ),
|
||||
logPath = process.env.LOG_DIR || path.join( __dirname, '/log' );
|
||||
|
||||
let ffmpeg;
|
||||
|
|
@ -14,33 +15,34 @@ function filePath( test, screenshotPath, extension ) {
|
|||
return path.join( screenshotPath, `${fileName( test.parent )}-${fileName( test.title )}.${extension}` );
|
||||
}
|
||||
|
||||
// relative path
|
||||
function relPath( foo ) {
|
||||
return path.resolve( __dirname, '../..', foo );
|
||||
}
|
||||
|
||||
/**
|
||||
* For more details documentation and available options,
|
||||
* see <https://webdriver.io/docs/configurationfile.html>
|
||||
* and <https://webdriver.io/docs/options.html>.
|
||||
*/
|
||||
exports.config = {
|
||||
// ======
|
||||
// Custom WDIO config specific to MediaWiki
|
||||
// Custom conf keys for MediaWiki
|
||||
//
|
||||
// Access via `browser.config.<key>`.
|
||||
// Defaults are for MediaWiki-Vagrant
|
||||
// ======
|
||||
// Use in a test as `browser.options.<key>`.
|
||||
// Defaults are for convenience with MediaWiki-Vagrant
|
||||
mwUser: process.env.MEDIAWIKI_USER || 'Admin',
|
||||
mwPwd: process.env.MEDIAWIKI_PASSWORD || 'vagrant',
|
||||
|
||||
// Wiki admin
|
||||
username: process.env.MEDIAWIKI_USER || 'Admin',
|
||||
password: process.env.MEDIAWIKI_PASSWORD || 'vagrant',
|
||||
|
||||
// Base for browser.url() and Page#openTitle()
|
||||
baseUrl: ( process.env.MW_SERVER || 'http://127.0.0.1:8080' ) + (
|
||||
process.env.MW_SCRIPT_PATH || '/w'
|
||||
),
|
||||
// ==================
|
||||
// Runner Configuration
|
||||
// ==================
|
||||
runner: 'local',
|
||||
// The standalone chromedriver (also used by WMF CI) uses "/wd/hub".
|
||||
// The one provided by wdio uses "/".
|
||||
path: startChromedriver ? '/' : '/wd/hub',
|
||||
|
||||
// ======
|
||||
// Sauce Labs
|
||||
// ======
|
||||
// See http://webdriver.io/guide/services/sauce.html
|
||||
// and https://github.com/bermi/sauce-connect-launcher#advanced-usage
|
||||
services: process.env.SAUCE_ACCESS_KEY ? [ 'sauce' ] : [],
|
||||
user: process.env.SAUCE_USERNAME,
|
||||
key: process.env.SAUCE_ACCESS_KEY,
|
||||
sauceConnect: true,
|
||||
|
|
@ -49,25 +51,21 @@ exports.config = {
|
|||
// Test Files
|
||||
// ==================
|
||||
specs: [
|
||||
relPath( './tests/selenium/wdio-mediawiki/specs/*.js' ),
|
||||
relPath( './tests/selenium/specs/**/*.js' )
|
||||
'./tests/selenium/wdio-mediawiki/specs/*.js',
|
||||
'./tests/selenium/specs/**/*.js'
|
||||
],
|
||||
|
||||
// ============
|
||||
// Capabilities
|
||||
// Define the different browser configurations to use ("capabilities") here.
|
||||
// ============
|
||||
|
||||
// How many instances of the same capability (browser) may be started at the same time.
|
||||
maxInstances: 1,
|
||||
|
||||
capabilities: [ {
|
||||
// For Chrome/Chromium https://sites.google.com/a/chromium.org/chromedriver/capabilities
|
||||
browserName: 'chrome',
|
||||
maxInstances: 1,
|
||||
chromeOptions: {
|
||||
'goog:chromeOptions': {
|
||||
// If DISPLAY is set, assume developer asked non-headless or CI with Xvfb.
|
||||
// Otherwise, use --headless (added in Chrome 59)
|
||||
// https://chromium.googlesource.com/chromium/src/+/59.0.3030.0/headless/README.md
|
||||
// Otherwise, use --headless.
|
||||
args: [
|
||||
...( process.env.DISPLAY ? [] : [ '--headless' ] ),
|
||||
// Chrome sandbox does not work in Docker
|
||||
|
|
@ -78,53 +76,31 @@ exports.config = {
|
|||
|
||||
// ===================
|
||||
// Test Configurations
|
||||
// Define all options that are relevant for the WebdriverIO instance here
|
||||
// ===================
|
||||
|
||||
// Enabling synchronous mode (via the wdio-sync package), means specs don't have to
|
||||
// use Promise#then() or await for browser commands, such as like `brower.element()`.
|
||||
// Instead, it will automatically pause JavaScript execution until th command finishes.
|
||||
//
|
||||
// For non-browser commands (such as MWBot and other promises), this means you
|
||||
// have to use `browser.call()` to make sure WDIO waits for it before the next
|
||||
// browser command.
|
||||
sync: true,
|
||||
|
||||
// Level of logging verbosity: silent | verbose | command | data | result | error
|
||||
// Level of logging verbosity: trace | debug | info | warn | error | silent
|
||||
logLevel: 'error',
|
||||
|
||||
// Enables colors for log output.
|
||||
coloredLogs: true,
|
||||
|
||||
// Warns when a deprecated command is used
|
||||
deprecationWarnings: true,
|
||||
|
||||
// Stop the tests once a certain number of failed tests have been recorded.
|
||||
// Default is 0 - don't bail, run all tests.
|
||||
// Stop after this many failures, or 0 to run all tests before reporting failures.
|
||||
bail: 0,
|
||||
|
||||
// Setting this enables automatic screenshots for when a browser command fails
|
||||
// It is also used by afterTest for capturig failed assertions.
|
||||
// We disable it since we have our screenshot handler in the afterTest hook.
|
||||
screenshotPath: null,
|
||||
|
||||
// Default timeout for each waitFor* command.
|
||||
waitforTimeout: 10 * 1000,
|
||||
|
||||
// Framework you want to run your specs with.
|
||||
// See also: http://webdriver.io/guide/testrunner/frameworks.html
|
||||
// Base for browser.url() and wdio-mediawiki/Page#openTitle()
|
||||
baseUrl: ( process.env.MW_SERVER || 'http://127.0.0.1:8080' ) + (
|
||||
process.env.MW_SCRIPT_PATH || '/w'
|
||||
),
|
||||
services: [
|
||||
...( startChromedriver ? [ 'chromedriver' ] : [] ),
|
||||
...( process.env.SAUCE_ACCESS_KEY ? [ 'sauce' ] : [] )
|
||||
],
|
||||
// See also: https://webdriver.io/docs/frameworks.html
|
||||
framework: 'mocha',
|
||||
|
||||
// Test reporter for stdout.
|
||||
// See also: http://webdriver.io/guide/testrunner/reporters.html
|
||||
reporters: [ 'dot', 'junit' ],
|
||||
reporterOptions: {
|
||||
junit: {
|
||||
// See also: https://webdriver.io/docs/dot-reporter.html
|
||||
reporters: [
|
||||
'dot',
|
||||
// See also: https://webdriver.io/docs/junit-reporter.html#configuration
|
||||
[ 'junit', {
|
||||
outputDir: logPath
|
||||
}
|
||||
},
|
||||
|
||||
// Options to be passed to Mocha.
|
||||
// See the full list at http://mochajs.org/
|
||||
} ]
|
||||
],
|
||||
// See also: http://mochajs.org/
|
||||
mochaOpts: {
|
||||
ui: 'bdd',
|
||||
timeout: 60 * 1000
|
||||
|
|
@ -133,15 +109,12 @@ exports.config = {
|
|||
// =====
|
||||
// Hooks
|
||||
// =====
|
||||
// See also: http://webdriver.io/guide/testrunner/configurationfile.html
|
||||
|
||||
/**
|
||||
* Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
|
||||
* @param {Object} test test details
|
||||
*/
|
||||
* Executed before a Mocha test starts.
|
||||
* @param {Object} test Mocha Test object
|
||||
*/
|
||||
beforeTest: function ( test ) {
|
||||
if ( process.env.DISPLAY && process.env.DISPLAY.startsWith( ':' ) ) {
|
||||
var logBuffer;
|
||||
const videoPath = filePath( test, logPath, 'mp4' );
|
||||
const { spawn } = require( 'child_process' );
|
||||
ffmpeg = spawn( 'ffmpeg', [
|
||||
|
|
@ -154,7 +127,7 @@ exports.config = {
|
|||
videoPath // output file
|
||||
] );
|
||||
|
||||
logBuffer = function ( buffer, prefix ) {
|
||||
const logBuffer = function ( buffer, prefix ) {
|
||||
const lines = buffer.toString().trim().split( '\n' );
|
||||
lines.forEach( function ( line ) {
|
||||
console.log( prefix + line );
|
||||
|
|
@ -180,10 +153,8 @@ exports.config = {
|
|||
} );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a screenshot when test fails.
|
||||
*
|
||||
* Executed after a Mocha test ends.
|
||||
* @param {Object} test Mocha Test object
|
||||
*/
|
||||
afterTest: function ( test ) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue