-
Notifications
You must be signed in to change notification settings - Fork 302
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[tests] Add end-to-end testing utility (#778)
To-date, executing the end-to-end ("e2e") test suite locally (for debugging or augmentation purposes) has been cumbersome as it involved manually invoking the application, Chromedriver and mocha processes for each test run (as well as shutting down and re-starting each process for subsequent test runs). In addition, developers had to cross-reference the Circle CI script and dashboard to verify the environment variables necessary to invoke the test-suite. This commit adds a utility script that: 1. makes the required environment variables explicit 2. automatically invokes all requisite processes, and 3. automatically kills invoked processes upon test completion or error. In addition, this logic ensures a clean exit if the developer interrupts a test run via `SIGINT` ( `CTRL` + `C`) These changes combined make running (and re-running) the tests much more streamlined, thereby significantly improving the developer workflow.
- Loading branch information
Showing
5 changed files
with
105 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,3 +191,7 @@ clean: | |
@rm -rf .$/build | ||
|
||
.PHONY: test build-source | ||
|
||
.PHONY: | ||
e2e: | ||
@npm run e2e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
#!/usr/bin/env node | ||
|
||
const path = require( 'path' ); | ||
const { promisify } = require( 'util' ); | ||
const { openSync, mkdirSync } = require( 'fs' ); | ||
const { execSync, spawn } = require( 'child_process' ); | ||
|
||
const PROJECT_DIR = path.join( __dirname, '../../' ); | ||
const BUILT_APP_DIR = path.join( PROJECT_DIR, 'release', 'mac', 'WordPress.com.app', 'Contents', 'MacOS' ); | ||
|
||
function spawnDetached( cwd, command, args, output ) { | ||
const app = spawn( command, args, { stdio: [ 'ignore', output, output ], detached: true, cwd } ); | ||
app.on( 'error', err => { | ||
throw `failed to initialize command "${ command }": "${ err }"`; | ||
} ); | ||
return app; | ||
} | ||
|
||
function initLogs( timestamp ) { | ||
const dir = path.join( PROJECT_DIR, 'test', 'logs', `${ timestamp }` ); | ||
|
||
mkdirSync( dir, { recursive: true } ); | ||
|
||
const appLog = openSync( path.join( dir, `app-${ timestamp }.log` ), 'a' ); | ||
const driverLog = openSync( path.join( dir, `chromedriver-${ timestamp }.log` ), 'a' ); | ||
|
||
if ( !appLog || !driverLog ) { | ||
throw 'failed to initialize logs'; | ||
} | ||
|
||
return { appLog, driverLog }; | ||
} | ||
|
||
const delay = promisify( setTimeout ); | ||
|
||
let app; | ||
let driver; | ||
|
||
function handleExit() { | ||
if ( driver ) { | ||
driver.kill(); | ||
} | ||
if ( app ) { | ||
app.kill(); | ||
} | ||
} | ||
|
||
// Handle both user-initiated (SIGINT) and normal termination. | ||
process.on( 'SIGINT', function() { | ||
handleExit(); | ||
process.exit(); | ||
} ); | ||
|
||
process.on( 'exit', handleExit ); | ||
|
||
async function run() { | ||
try { | ||
const requiredENVs = [ 'E2EUSERNAME', 'E2EPASSWORD', 'E2E_MAILOSAUR_INBOX' ]; | ||
const missingENVs = requiredENVs.filter( name => ! process.env[name] || process.env[name] === '' ); | ||
if ( missingENVs.length ) { | ||
throw `Missing non-empty ENV for: ${ missingENVs.join( ', ' ) }`; | ||
} | ||
|
||
// Replace `:` with `-` to format timestamp as YYYY-MM-DDTHH-MM-SS.mmmZ | ||
const timestamp = ( new Date() ).toJSON().replace( /:/g, '-' ); | ||
const { appLog, driverLog } = initLogs( timestamp ); | ||
|
||
app = spawnDetached( BUILT_APP_DIR, './WordPress.com', [ | ||
'--disable-renderer-backgrounding', | ||
'--disable-http-cache', | ||
'--start-maximized', | ||
'--remote-debugging-port=9222', | ||
], appLog ); | ||
await delay( 5000 ); | ||
|
||
driver = spawnDetached( PROJECT_DIR, 'npx', [ | ||
'chromedriver', | ||
'--port=9515', | ||
'--verbose', | ||
], driverLog ); | ||
|
||
const tests = path.join( PROJECT_DIR, 'test', 'tests', 'e2e.js' ); | ||
execSync( `npx mocha ${ tests } --timeout 20000`, { stdio: 'inherit' } ); | ||
} | ||
catch ( err ) { | ||
console.error( err ); | ||
} | ||
finally { | ||
// Explicitly call process.exit to ensure that spawned processes are killed. | ||
process.exit(); | ||
} | ||
} | ||
|
||
run(); |