This repository has been archived by the owner on Mar 25, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from Financial-Times/reports
use reports
- Loading branch information
Showing
5 changed files
with
251 additions
and
159 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 |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const REGRESSION_TRANSPORT = process.env.REGRESSION_TRANSPORT; | ||
const RECIPIENTS = process.env.REGRESSION_RECIPIENTS; | ||
|
||
const nodemailer = require('nodemailer'); | ||
const transporter = nodemailer.createTransport(REGRESSION_TRANSPORT); | ||
const logger = require('@financial-times/n-logger').default.logger; | ||
|
||
module.exports = function sendEmails (err, output) { | ||
|
||
const options = { | ||
from: '"Signup Regression" <no-reply@ft.com>', | ||
to: RECIPIENTS, | ||
subject: `❗Regression tests failed ${new Date()}`, | ||
text: output.replace('[0;37m ','').replace('[41m ', '').replace('[42m ', '').replace('[44m ', '').replace('[45m ', '') | ||
}; | ||
|
||
transporter.sendMail(options, function (emailError, data) { | ||
if (emailError) { | ||
return logger.info('Error sending emails', emailError); | ||
} | ||
logger.info('Regression test notification sent to: ', data); | ||
}); | ||
}; |
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 |
---|---|---|
@@ -1,193 +1,117 @@ | ||
'use strict'; | ||
|
||
const REGRESSION_TRANSPORT = process.env.REGRESSION_TRANSPORT; | ||
const SLACK_URL = process.env.SLACK_URL; | ||
const RECIPIENTS = process.env.REGRESSION_RECIPIENTS; | ||
const SLACK_MENTIONS = process.env.SLACK_MENTIONS; | ||
|
||
// TODO exctract to config file | ||
const SUPPORTED_BROWSERS = ['ie8', 'ie9', 'ie10', 'ie11', 'chrome', 'firefox', 'edge', 'safari', 'iPhone6Plus', 'Nexus7']; | ||
|
||
const worker = require('@financial-times/n-worker'); | ||
const nodemailer = require('nodemailer'); | ||
const transporter = nodemailer.createTransport(REGRESSION_TRANSPORT); | ||
const exec = require('child_process').exec; | ||
const fetch = require('isomorphic-fetch'); | ||
const parseXML = require('xml2js').parseString; | ||
const fs = require('fs'); | ||
|
||
let VERBOSE = true; | ||
const util = require('./util'); | ||
const sendEmails = require('./email'); | ||
const sendSlackNotification = require('./slack'); | ||
const logger = require('@financial-times/n-logger').default.logger; | ||
|
||
const sendEmails = function (err, output) { | ||
function emptyReportsFolder (path) { | ||
logger.info('Deleting old reports...') | ||
|
||
const options = { | ||
from: '"Signup Regression" <no-reply@ft.com>', | ||
to: RECIPIENTS, | ||
subject: `❗Regression tests failed ${new Date()}`, | ||
text: output.replace('[0;37m ','').replace('[41m ', '').replace('[42m ', '').replace('[44m ', '').replace('[45m ', '') | ||
}; | ||
const reportNames = fs.readdirSync(path); | ||
|
||
transporter.sendMail(options, function (emailError, data) { | ||
if (emailError) { | ||
return console.log('Error sending emails', emailError); | ||
} | ||
console.log('Regression test notification sent to: ', data); | ||
}); | ||
}; | ||
|
||
const sendSlackNotification = function (err, results) { | ||
console.log('sendSlackNotification', err, results) | ||
if (!results) { | ||
|
||
// Don't show start message on slack when not in verbose mode | ||
if (VERBOSE) { | ||
const initOptions = { | ||
method: 'POST', | ||
body: JSON.stringify({ | ||
username: '(Beta) Signup Tests', | ||
text: `Started ${new Date()}` | ||
}), | ||
headers: { | ||
'Content-type': 'application/json' | ||
} | ||
}; | ||
fetch(SLACK_URL, initOptions); | ||
} | ||
return; | ||
for (const name of reportNames) { | ||
fs.unlinkSync(path + name); | ||
} | ||
} | ||
|
||
function readReports (path) { | ||
|
||
const fields = []; | ||
logger.info('Reading new reports...') | ||
const map = {}; | ||
|
||
for (const key in results) { | ||
if (results.hasOwnProperty(key)) { | ||
fields.push({ | ||
'title': key, | ||
'value': results[key].join(', '), | ||
'short': false | ||
}); | ||
} | ||
} | ||
const reportNames = fs.readdirSync(path); | ||
|
||
const options = { | ||
method: 'POST', | ||
body: JSON.stringify({ | ||
username: '(Beta) Signup Tests', | ||
'attachments': [ | ||
{ | ||
'fallback': '', | ||
|
||
'text': err ? `Some automated tests have failed\n${SLACK_MENTIONS}` : | ||
`${fields.length} of ${fields.length} tests passing`, | ||
|
||
'color': err ? 'warning' : | ||
'good', | ||
|
||
'fields': fields | ||
} | ||
] | ||
}), | ||
headers: { | ||
'Content-type': 'application/json' | ||
} | ||
}; | ||
|
||
fetch(SLACK_URL, options); | ||
}; | ||
|
||
// TODO don't read output, parse result files | ||
const curate = function (stdout, stderr) { | ||
let moreTestResults = true; | ||
const marker = 'Test: '; | ||
const failMarker = 'TEST FAILURE'; | ||
const EOL = '\n'; | ||
let result = {}; | ||
let showedFailure = stdout.indexOf(failMarker) !== -1 ? true : false; | ||
|
||
console.log('CURATING:'); | ||
console.log('failure', showedFailure, stdout.indexOf(failMarker)); | ||
console.log('stderr', stderr); | ||
|
||
// We can show failure but succeed on a retry | ||
// only return empty if stderr is present | ||
if (showedFailure && stderr) { | ||
return {}; | ||
if (!reportNames) { | ||
logger.info('No reports found!') | ||
} | ||
|
||
while (moreTestResults) { | ||
|
||
let index = stdout.indexOf(marker); | ||
for (const name of reportNames) { | ||
|
||
if (index === -1) { | ||
moreTestResults = false; | ||
break; | ||
} | ||
const xmlContents = fs.readFileSync(`${path}/${name}`, 'utf8'); | ||
|
||
let testName = stdout.substring(index + marker.length, stdout.indexOf(EOL, index)).replace('[0m', ''); | ||
parseXML(xmlContents, function (xmlError, json) { | ||
|
||
if (!result[testName]) { | ||
result[testName] = []; | ||
} | ||
// logger.info('\n\n\n\n', JSON.stringify(json)) | ||
|
||
let browser = null; | ||
let largestIndex = null; | ||
for (const candidate of SUPPORTED_BROWSERS) { | ||
let candidateIndex = stdout.lastIndexOf(candidate, index); | ||
const env = util.getEnvironmentData(name) | ||
const suite = json.testsuites.testsuite[0]; // TODO accomodate multisuite | ||
const suiteInfo = { | ||
name: suite.$.name, | ||
failures: suite.$.failures, | ||
skipped: suite.$.skipped, | ||
time: suite.$.time, | ||
timestamp: suite.$.timestamps, | ||
tests: [] | ||
} | ||
|
||
if (candidateIndex !== -1 && result[testName].indexOf(candidate) === -1) { | ||
if (!largestIndex || candidateIndex > largestIndex) { | ||
largestIndex = candidateIndex; | ||
browser = candidate; | ||
} | ||
for (const test of suite.testcase) { | ||
suiteInfo.tests.push({ | ||
name: test.$.name, | ||
failure: test.failure | ||
}) | ||
} | ||
} | ||
if (browser) { | ||
result[testName].push(browser); | ||
} | ||
largestIndex = null; | ||
|
||
stdout = stdout.substring(index + marker.length, stdout.length); | ||
if (map[env]) { | ||
map[env].push(suiteInfo) | ||
} | ||
else { | ||
map[env] = [suiteInfo]; | ||
}; | ||
}) | ||
} | ||
return result; | ||
return map; | ||
} | ||
|
||
export class Automation { | ||
module.exports = class Automation { | ||
|
||
static run () { | ||
console.log('Starting regression tests'); | ||
sendSlackNotification(); | ||
exec('make regression', {env: process.env}, function (error, stdout, stderr) { | ||
console.log('error', error); | ||
console.log('stdout', stdout); | ||
console.log('stderr', stderr); | ||
const output = curate(stdout, stderr); | ||
console.log('output', output) | ||
static run ({ | ||
nightwatchJson, | ||
regressionCommand='make regression', | ||
verbose=true, | ||
packageJson = {}, | ||
appName, | ||
appLogo | ||
}={}) { | ||
|
||
sendSlackNotification(error || stderr, output); | ||
if (!nightwatchJson) { | ||
throw new Error('must specify nightwtach config path') | ||
} | ||
|
||
if (error || stderr) { | ||
console.log('SENDING EMAIL', error, stderr) | ||
sendEmails(stderr, stdout); | ||
} | ||
else { | ||
console.log('NOT SENDING EMAIL', error, stderr) | ||
} | ||
logger.info('Starting regression tests...'); | ||
const reportsPath = nightwatchJson.output_folder; | ||
emptyReportsFolder(reportsPath) | ||
|
||
|
||
sendSlackNotification({ | ||
init: true, | ||
appName: appName, | ||
verbose: verbose | ||
}); | ||
} | ||
|
||
static schedule ({time='0 8 * * 1-5'}={}) { | ||
|
||
worker.setup().then(function () { | ||
exec(regressionCommand, {env: process.env}, function (error, stdout, stderr) { | ||
// logger.info('\n\nerror', error); | ||
// logger.info('\n\nstdout', stdout); | ||
// logger.info('\n\nstderr', stderr); | ||
|
||
console.log('scheduling', time); | ||
const reports = readReports(reportsPath); | ||
|
||
new worker.CronJob({ | ||
cronTime: time, | ||
timeZone: 'Europe/London', | ||
onTick: this.run, | ||
onComplete: function () { | ||
console.log('cronjob done'); | ||
} | ||
sendSlackNotification({ | ||
error: error || stderr, // TODO ONLY non retry errors | ||
reports: reports, | ||
appName: appName, | ||
appLogo: appLogo, | ||
packageJson: packageJson, | ||
verbose: verbose | ||
}); | ||
|
||
if (error || stderr) { | ||
logger.info('Sending email...', error, stderr) | ||
sendEmails(stderr, stdout); | ||
} | ||
}); | ||
} | ||
} |
Oops, something went wrong.