-
Notifications
You must be signed in to change notification settings - Fork 38
[terra-functional-testing] Fixing Nexus screenshot bugs #839
Changes from 6 commits
d020533
d7f55d2
5ab36cd
30af124
d1195e5
52318ac
0f73f13
cef7d30
3b352ec
0fb4acd
e3f1c35
8bbf50a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,10 +55,11 @@ class ScreenshotRequestor { | |
|
||
/** | ||
* Deletes the existing screenshots | ||
* @param {string} referenceName - the name of the reference screenshots file to download | ||
*/ | ||
async deleteExistingScreenshots() { | ||
async deleteExistingScreenshots(referenceName) { | ||
const response = await fetch( | ||
this.serviceUrl, | ||
`${this.serviceUrl}${referenceName}/`, | ||
{ | ||
method: 'DELETE', | ||
headers: { | ||
|
@@ -80,6 +81,26 @@ class ScreenshotRequestor { | |
fs.removeSync(archiveName); | ||
} | ||
|
||
/** | ||
* Makes string to return for the screenshots to save and download | ||
* Naming convention is {theme}-{locale}-{browser}-{formfactor} and if provided string is empty does not add it. | ||
* @param {string} locale - the locale to use when downloading | ||
* @param {string} theme - the theme to use when downloading | ||
* @param {string} formFactor - the formFactor to use when downloading | ||
* @param {string} browser - the browser to use when uploading | ||
*/ | ||
static makeReferenceName(locale, theme, formFactor, browser) { | ||
const referenceName = [theme, locale, browser, formFactor].filter( | ||
(str) => { | ||
if (str !== '' || str !== undefined) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Are there any other types of falsey's we have to worry about? What would happen here with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There shouldn't be but reconsidering this, we're just checking for falsy values so might as well filter them all out for an easy code run so changed here |
||
return str; | ||
} | ||
return false; | ||
}, | ||
).join('-'); | ||
return referenceName; | ||
} | ||
|
||
/** | ||
* Zips the latest screenshots. | ||
*/ | ||
|
@@ -104,7 +125,7 @@ class ScreenshotRequestor { | |
|
||
const archiveName = path.join(this.zipFilePath, 'latest.zip'); | ||
|
||
// Name the uploaded file reference.zip since the latest screenshots will now be used as the reference screenshots. | ||
// Name the uploaded file the passed referenceName since the latest screenshots will now be used as the reference screenshots. | ||
archive.file(archiveName, { name: 'reference.zip' }); | ||
|
||
await archive.finalize(); | ||
|
@@ -115,20 +136,21 @@ class ScreenshotRequestor { | |
|
||
/** | ||
* Downloads the screenshots and unzip it to the reference screenshot directory defined by referenceScreenshotsPath. | ||
* @param {string} referenceName - the name of the reference screenshots file to download | ||
*/ | ||
async downloadScreenshots() { | ||
async downloadScreenshots(referenceName) { | ||
let archiveUrl; | ||
let fetchOptions; | ||
if (this.serviceAuthHeader !== undefined) { | ||
archiveUrl = `${this.serviceUrl}/reference.zip`; | ||
archiveUrl = `${this.serviceUrl}${referenceName}/reference.zip`; | ||
fetchOptions = { | ||
method: 'GET', | ||
headers: { | ||
Authorization: this.serviceAuthHeader, | ||
}, | ||
}; | ||
} else { | ||
archiveUrl = `${this.url}/reference.zip`; | ||
archiveUrl = `${this.url}${referenceName}/reference.zip`; | ||
fetchOptions = { | ||
method: 'GET', | ||
}; | ||
|
@@ -139,7 +161,7 @@ class ScreenshotRequestor { | |
); | ||
|
||
if (response.status === 404) { | ||
logger.info(`No screenshots downloaded from ${this.url}. Either the URL is invalid or no screenshots were previously uploaded.`); | ||
logger.info(`No screenshots downloaded from ${this.url}${referenceName}. Either the URL is invalid or no screenshots were previously uploaded.`); | ||
return; | ||
} | ||
|
||
|
@@ -153,7 +175,7 @@ class ScreenshotRequestor { | |
writeStream.on('finish', async () => { | ||
await extract('terra-wdio-screenshots.zip', { dir: this.referenceScreenshotsPath }); | ||
fs.removeSync('terra-wdio-screenshots.zip'); | ||
logger.info(`Screenshots downloaded from ${this.url}`); | ||
logger.info(`Screenshots downloaded from ${this.url}${referenceName}`); | ||
resolve(); | ||
}); | ||
} catch (error) { | ||
|
@@ -167,12 +189,13 @@ class ScreenshotRequestor { | |
/** | ||
* Uploads the site zip contained in memoryStream | ||
* @param {MemoryStream} memoryStream - the MemoryStream to use when uploading | ||
* @param {string} referenceName - the name of the reference zip to use when uploading | ||
*/ | ||
async uploadScreenshots(memoryStream) { | ||
async uploadScreenshots(memoryStream, referenceName) { | ||
const formData = new FormData(); | ||
formData.append('file', memoryStream, { filename: 'reference.zip', knownLength: memoryStream.length }); | ||
const response = await fetch( | ||
this.serviceUrl, | ||
`${this.serviceUrl}${referenceName}/`, | ||
{ | ||
method: 'PUT', | ||
headers: { | ||
|
@@ -183,22 +206,32 @@ class ScreenshotRequestor { | |
); | ||
ScreenshotRequestor.checkStatus(response); | ||
|
||
logger.info(`Screenshots are uploaded to ${this.url}`); | ||
logger.info(`Screenshots are uploaded to ${this.url}${referenceName}`); | ||
} | ||
|
||
/** | ||
* Downloads the screenshots. | ||
* @param {string} locale - the locale to use when downloading | ||
* @param {string} theme - the theme to use when downloading | ||
* @param {string} formFactor - the formFactor to use when downloading | ||
* @param {string} browser - the browser to use when uploading | ||
*/ | ||
async download() { | ||
await this.downloadScreenshots(); | ||
async download(locale, theme, formFactor, browser) { | ||
const referenceName = ScreenshotRequestor.makeReferenceName(locale, theme, formFactor, browser); | ||
await this.downloadScreenshots(referenceName); | ||
} | ||
|
||
/** | ||
* Uploads the screenshots by deleting the existing screenshots, zipping the new ones, and uploading it | ||
* @param {string} locale - the locale to use when uploading | ||
* @param {string} theme - the theme to use when uploading | ||
* @param {string} formFactor - the formFactor to use when uploading | ||
* @param {string} browser - the browser to use when uploading | ||
*/ | ||
async upload() { | ||
async upload(locale, theme, formFactor, browser) { | ||
const referenceName = ScreenshotRequestor.makeReferenceName(locale, theme, formFactor, browser); | ||
// Delete the existing screenshots from the remote repository because new screenshots will be uploaded. | ||
await this.deleteExistingScreenshots(); | ||
await this.deleteExistingScreenshots(referenceName); | ||
|
||
// Zip up the existing latest screenshots | ||
await this.zipLatestScreenshots(); | ||
|
@@ -207,7 +240,7 @@ class ScreenshotRequestor { | |
const memoryStream = await this.zipDirectoryToMemory(); | ||
|
||
// Upload the screenshots to the remote repository | ||
await this.uploadScreenshots(memoryStream); | ||
await this.uploadScreenshots(memoryStream, referenceName); | ||
|
||
// The zipped latest screenshots can now be safely deleted. | ||
this.deleteZippedLatestScreenshots(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ const path = require('path'); | |
const expect = require('expect'); | ||
const fs = require('fs-extra'); | ||
const { SevereServiceError } = require('webdriverio'); | ||
const getOutputDir = require('../../reporters/spec-reporter/get-output-dir'); | ||
const { accessibility, element, screenshot } = require('../../commands/validates'); | ||
const { toBeAccessible, toMatchReference } = require('../../commands/expect'); | ||
const { | ||
|
@@ -64,6 +65,10 @@ class WDIOTerraService { | |
gitToken, | ||
issueNumber, | ||
useRemoteReferenceScreenshots, | ||
locale, | ||
theme, | ||
formFactor, | ||
browser, | ||
} = this.serviceOptions; | ||
|
||
if (!useRemoteReferenceScreenshots) { | ||
|
@@ -90,7 +95,7 @@ class WDIOTerraService { | |
screenshotConfig = this.getRemoteScreenshotConfiguration(this.screenshotsSites, buildBranch); | ||
} | ||
const screenshotRequestor = new ScreenshotRequestor(screenshotConfig.publishScreenshotConfiguration); | ||
await screenshotRequestor.download(); | ||
await screenshotRequestor.download(locale, theme, formFactor, browser); | ||
} catch (error) { | ||
throw new SevereServiceError(error); | ||
} | ||
|
@@ -211,9 +216,7 @@ class WDIOTerraService { | |
':warning: :bangbang: **WDIO MISMATCH**\n\n', | ||
`Check that screenshot change is intended at: ${buildUrl}\n\n`, | ||
'If screenshot change is intended, remote reference screenshots will be updated upon PR merge.\n', | ||
'If screenshot change is unintended, please fix screenshot issues before PR merge to prevent them from being uploaded.\n\n', | ||
'Note: This comment only appears the first time a screenshot mismatch is detected on a PR build, ', | ||
'future builds will need to be checked for unintended screenshot mismatches.', | ||
'If screenshot change is unintended, please fix screenshot issues before PR merge to prevent them from being uploaded.', | ||
].join(''); | ||
|
||
const comments = await issue.getComments(); | ||
|
@@ -230,12 +233,16 @@ class WDIOTerraService { | |
async uploadBuildBranchScreenshots() { | ||
const { | ||
buildBranch, | ||
locale, | ||
theme, | ||
formFactor, | ||
browser, | ||
} = this.serviceOptions; | ||
|
||
try { | ||
const screenshotConfig = this.getRemoteScreenshotConfiguration(this.screenshotsSites, buildBranch); | ||
const screenshotRequestor = new ScreenshotRequestor(screenshotConfig.publishScreenshotConfiguration); | ||
await screenshotRequestor.upload(); | ||
await screenshotRequestor.upload(locale, theme, formFactor, browser); | ||
} catch (err) { | ||
throw new SevereServiceError(err); | ||
} | ||
|
@@ -256,13 +263,21 @@ class WDIOTerraService { | |
return; | ||
} | ||
|
||
const fileName = path.join(getOutputDir(), 'ignored-mismatch.json'); | ||
|
||
try { | ||
if (process.env.SCREENSHOT_MISMATCH_CHECK === 'true' && buildBranch.match(BUILD_BRANCH.pullRequest)) { | ||
if (fs.existsSync(fileName) && buildBranch.match(BUILD_BRANCH.pullRequest)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like the above comment: I'd try to avoid the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alright finally got a moment to updated this, moved off the sync api and onto the async ones |
||
// We found a screenshot mismatch during our build of this PR branch. | ||
await this.postMismatchWarningOnce(); | ||
// Remove mismatch flag file after running | ||
fs.removeSync(fileName); | ||
} else if (!buildBranch.match(BUILD_BRANCH.pullRequest) && buildType === BUILD_TYPE.branchEventCause) { | ||
// This non-PR branch is being merged or someone pushed code into it directly. | ||
await this.uploadBuildBranchScreenshots(); | ||
// Remove mismatch flag file after running if it exists | ||
if (fs.existsSync(fileName)) { | ||
fs.removeSync(fileName); | ||
} | ||
} | ||
} catch (err) { | ||
// The service will stop only if a SevereServiceError is thrown. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is it possible to turn this into an
async
function? the Sync API from node is infamous for being a blocking API (meaning that it needs to wait for this to complete before doing anything else in node). This will affect performance down the road, especially when we change this to run multiple suites at a time. I might suggest using the equivalent promises API's from node: https://nodejs.org/docs/latest-v14.x/api/fs.html#fs_promises_apiThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if it is possible, at the very least I have a suspicion that would be a major version bump since we would be changing a main testing function to be
async
when it wasn't before.