Skip to content

Commit 805010c

Browse files
Merge pull request #474 from gemini-testing/HERMIONE-984.fix_undo
fix: undo with remote images
2 parents 979f7ff + 486c2f2 commit 805010c

File tree

18 files changed

+273
-248
lines changed

18 files changed

+273
-248
lines changed

lib/gui/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ exports.start = async ({paths, hermione, guiApi, configs}) => {
7474
}
7575
});
7676

77-
server.post('/get-update-reference-data', (req, res) => {
77+
server.post('/reference-data-to-update', (req, res) => {
7878
try {
7979
const data = app.getTestsDataToUpdateRefs(req.body);
8080
res.json(data);

lib/gui/tool-runner/index.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ module.exports = class ToolRunner {
9797

9898
updateReferenceImage(tests) {
9999
return Promise.map(tests, (test) => {
100-
const updateResult = this._prepareUpdateResult(test);
100+
const updateResult = this._prepareTestResult(test);
101101
const formattedResult = this._reportBuilder.format(updateResult, UPDATED);
102102
const failResultId = formattedResult.id;
103103
const updateAttempt = this._reportBuilder.getUpdatedAttempt(formattedResult);
@@ -121,17 +121,43 @@ module.exports = class ToolRunner {
121121
});
122122
}
123123

124-
async undoAcceptImages(imageIds) {
125-
const {
126-
referencesToRemove,
127-
referencesToUpdate,
128-
...rest
129-
} = await this._reportBuilder.undoAcceptImages(imageIds, this._reportPath);
124+
async undoAcceptImages(tests) {
125+
const updatedImages = [], removedResults = [];
130126

131-
await reporterHelper.overwriteReferenceImage(referencesToUpdate, this._reportPath);
132-
await reporterHelper.removeReferenceImage(referencesToRemove);
127+
await Promise.map(tests, async (test) => {
128+
const updateResult = this._prepareTestResult(test);
129+
const formattedResult = this._reportBuilder.format(updateResult);
133130

134-
return rest;
131+
formattedResult.attempt = updateResult.attempt;
132+
133+
await Promise.map(updateResult.imagesInfo, async (imageInfo) => {
134+
const {stateName} = imageInfo;
135+
const {
136+
updatedImage,
137+
removedResult,
138+
previousExpectedPath,
139+
shouldRemoveReference,
140+
shouldRevertReference
141+
} = await this._reportBuilder.undoAcceptImage(formattedResult, stateName);
142+
143+
updatedImage && updatedImages.push(updatedImage);
144+
removedResult && removedResults.push(removedResult);
145+
146+
if (shouldRemoveReference) {
147+
await reporterHelper.removeReferenceImage(formattedResult, stateName);
148+
}
149+
150+
if (shouldRevertReference) {
151+
await reporterHelper.revertReferenceImage(formattedResult, stateName);
152+
}
153+
154+
if (previousExpectedPath) {
155+
formattedResult.updateCacheExpectedPath(stateName, previousExpectedPath);
156+
}
157+
});
158+
});
159+
160+
return {updatedImages, removedResults};
135161
}
136162

137163
async findEqualDiffs(images) {
@@ -204,7 +230,7 @@ module.exports = class ToolRunner {
204230
subscribeOnToolEvents(this._hermione, this._reportBuilder, this._eventSource, this._reportPath);
205231
}
206232

207-
_prepareUpdateResult(test) {
233+
_prepareTestResult(test) {
208234
const {browserId, attempt} = test;
209235
const fullTitle = mkFullTitle(test);
210236
const testId = formatId(getShortMD5(fullTitle), browserId);

lib/local-images-saver.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const utils = require('./server-utils');
44

55
module.exports = {
66
saveImg: async (srcCurrPath, {destPath, reportDir}) => {
7-
await utils.copyFileAsync(srcCurrPath, destPath, reportDir);
7+
await utils.copyFileAsync(srcCurrPath, destPath, {reportDir});
88

99
return destPath;
1010
}

lib/report-builder/gui.js

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -106,63 +106,33 @@ module.exports = class GuiReportBuilder extends StaticReportBuilder {
106106
return isUpdated ? attempt : attempt + 1;
107107
}
108108

109-
async undoAcceptImages(imageIds, reportPath) {
110-
const updatedImages = [], removedResults = [], referencesToRemove = [], referencesToUpdate = [];
111-
112-
for (const imageId of imageIds) {
113-
const {
114-
updatedImage,
115-
removedResult,
116-
referenceToRemove,
117-
referenceToUpdate
118-
} = await this._undoAcceptImage(imageId, reportPath);
119-
120-
updatedImage && updatedImages.push(updatedImage);
121-
removedResult && removedResults.push(removedResult);
122-
referenceToRemove && referencesToRemove.push(referenceToRemove);
123-
referenceToUpdate && referencesToUpdate.push(referenceToUpdate);
124-
}
125-
126-
return {updatedImages, removedResults, referencesToRemove, referencesToUpdate};
127-
}
128-
129-
async _undoAcceptImage(imageId, reportPath) {
109+
async undoAcceptImage(formattedResult, stateName) {
110+
const resultId = formattedResult.id;
111+
const suitePath = formattedResult.testPath;
112+
const browserName = formattedResult.browserId;
130113
const {
131-
suitePath,
132-
browserName,
133-
stateName,
114+
imageId,
134115
status,
135116
timestamp,
136-
image,
137117
previousImage,
138-
resultIdToRemove
139-
} = this._testsTree.getResultDataToUnacceptImage(imageId);
118+
shouldRemoveResult
119+
} = this._testsTree.getResultDataToUnacceptImage(resultId, stateName);
140120

141121
if (!isUpdatedStatus(status)) {
142122
return {};
143123
}
144124

145-
const fullTitle = suitePath.join(' ');
146-
const previousExpectedImgPath = _.get(previousImage, 'expectedImg.path', null);
147-
const refImgPath = previousImage.refImg.path;
148-
const currentExpectedImgPath = image.expectedImg.path;
125+
const previousExpectedPath = _.get(previousImage, 'expectedImg.path', null);
149126
const shouldRemoveReference = _.isNull(previousImage.refImg.size);
150-
let updatedImage, removedResult, referenceToRemove, referenceToUpdate;
127+
const shouldRevertReference = !shouldRemoveReference;
151128

152-
await this._removeImageFromReport(reportPath, currentExpectedImgPath);
153-
this._updateTestExpectedPath(fullTitle, browserName, stateName, previousExpectedImgPath);
154-
155-
if (shouldRemoveReference) {
156-
referenceToRemove = refImgPath;
157-
} else {
158-
referenceToUpdate = {path: refImgPath, source: previousExpectedImgPath};
159-
}
129+
let updatedImage, removedResult;
160130

161-
if (resultIdToRemove) {
162-
this._testsTree.removeTestResult(resultIdToRemove);
163-
this._decreaseTestAttemptNumber(fullTitle, browserName);
131+
if (shouldRemoveResult) {
132+
this._testsTree.removeTestResult(resultId);
133+
formattedResult.decreaseAttemptNumber();
164134

165-
removedResult = resultIdToRemove;
135+
removedResult = resultId;
166136
} else {
167137
updatedImage = this._testsTree.updateImageInfo(imageId, previousImage);
168138
}
@@ -175,7 +145,7 @@ module.exports = class GuiReportBuilder extends StaticReportBuilder {
175145
`json_extract(${DB_COLUMNS.IMAGES_INFO}, '$[0].stateName') = ?`
176146
].join(' AND ')}, JSON.stringify(suitePath), browserName, status, timestamp, stateName);
177147

178-
return {updatedImage, removedResult, referenceToRemove, referenceToUpdate};
148+
return {updatedImage, removedResult, previousExpectedPath, shouldRemoveReference, shouldRevertReference};
179149
}
180150

181151
_addTestResult(formattedResult, props, opts = {}) {

lib/report-builder/static.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,6 @@ module.exports = class StaticReportBuilder {
147147
this._sqliteAdapter.delete({where, orderBy, orderDescending, limit}, ...args);
148148
}
149149

150-
_updateTestExpectedPath(fullTitle, browserId, stateName, expectedPath) {
151-
TestAdapter.updateCacheExpectedPath(fullTitle, browserId, stateName, expectedPath);
152-
}
153-
154-
_decreaseTestAttemptNumber(fullTitle, browserName) {
155-
TestAdapter.decreaseAttemptNumber(fullTitle, browserName);
156-
}
157-
158150
async finalize() {
159151
this._sqliteAdapter.close();
160152

lib/reporter-helpers.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,42 @@
11
'use strict';
22

33
const path = require('path');
4+
const tmp = require('tmp');
45
const Promise = require('bluebird');
6+
const {getShortMD5} = require('./common-utils');
57
const utils = require('./server-utils');
68

7-
exports.updateReferenceImage = (testResult, reportPath, stateName) => {
9+
const mkReferenceId = (testId, stateName) => getShortMD5(`${testId}#${stateName}`);
10+
11+
exports.updateReferenceImage = async (testResult, reportPath, stateName) => {
812
const currImg = testResult.getCurrImg(stateName);
913

1014
const src = currImg.path
1115
? path.resolve(reportPath, currImg.path)
1216
: utils.getCurrentAbsolutePath(testResult, reportPath, stateName);
1317

18+
const referencePath = testResult.getRefImg(stateName).path;
19+
20+
if (utils.fileExists(referencePath)) {
21+
const referenceId = mkReferenceId(testResult.id, stateName);
22+
const oldReferencePath = path.resolve(tmp.tmpdir, referenceId);
23+
await utils.copyFileAsync(referencePath, oldReferencePath);
24+
}
25+
1426
return Promise.all([
15-
utils.copyFileAsync(src, testResult.getRefImg(stateName).path),
27+
utils.copyFileAsync(src, referencePath),
1628
utils.copyFileAsync(src, utils.getReferenceAbsolutePath(testResult, reportPath, stateName))
1729
]);
1830
};
1931

20-
exports.removeReferenceImage = (refImgPaths) => {
21-
return Promise.map([].concat(refImgPaths), (refImgPath) => utils.deleteFile(refImgPath));
32+
exports.revertReferenceImage = (testResult, stateName) => {
33+
const referenceId = mkReferenceId(testResult.id, stateName);
34+
const oldReferencePath = path.resolve(tmp.tmpdir, referenceId);
35+
const referencePath = testResult.getRefImg(stateName).path;
36+
37+
return utils.copyFileAsync(oldReferencePath, referencePath);
2238
};
2339

24-
exports.overwriteReferenceImage = (referencesToUpdate, reportPath) => {
25-
return Promise.map([].concat(referencesToUpdate), (referenceToUpdate) => {
26-
return utils.copyFileAsync(path.resolve(reportPath, referenceToUpdate.source), referenceToUpdate.path);
27-
});
40+
exports.removeReferenceImage = (testResult, stateName) => {
41+
return utils.deleteFile(testResult.getRefImg(stateName).path);
2842
};

lib/server-utils.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ function createPath(kind, result, stateName) {
4747
return path.join.apply(null, components);
4848
}
4949

50-
function copyFileAsync(srcPath, destPath, reportDir = '') {
50+
function copyFileAsync(srcPath, destPath, {reportDir = '', overwrite = true} = {}) {
5151
const resolvedDestPath = path.resolve(reportDir, destPath);
52-
return makeDirFor(resolvedDestPath).then(() => fs.copy(srcPath, resolvedDestPath));
52+
return makeDirFor(resolvedDestPath).then(() => fs.copy(srcPath, resolvedDestPath, {overwrite}));
5353
}
5454

5555
/**
@@ -67,6 +67,10 @@ function makeDirFor(destPath) {
6767
return fs.mkdirs(path.dirname(destPath));
6868
}
6969

70+
function fileExists(path) {
71+
return fs.existsSync(path);
72+
}
73+
7074
function logPathToHtmlReport(pluginConfig) {
7175
const reportPath = `file://${path.resolve(pluginConfig.path, 'index.html')}`;
7276

@@ -278,6 +282,7 @@ module.exports = {
278282
copyFileAsync,
279283
deleteFile,
280284
makeDirFor,
285+
fileExists,
281286

282287
logPathToHtmlReport,
283288
logError,

lib/static/modules/actions.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const acceptOpened = (imageIds, type = actionNames.ACCEPT_OPENED_SCREENSH
135135
dispatch({type: actionNames.PROCESS_BEGIN});
136136

137137
try {
138-
const {data} = await axios.post('/get-update-reference-data', imageIds);
138+
const {data} = await axios.post('/reference-data-to-update', imageIds);
139139
const {data: updatedData} = await axios.post('/update-reference', data);
140140
dispatch({type, payload: updatedData});
141141

@@ -168,8 +168,9 @@ export const undoAcceptImages = (imageIds, {skipTreeUpdate = false} = {}) => {
168168
dispatch({type: actionNames.PROCESS_BEGIN});
169169

170170
try {
171-
const {data} = await axios.post('/undo-accept-images', imageIds);
172-
const payload = {...data, skipTreeUpdate};
171+
const {data} = await axios.post('/reference-data-to-update', imageIds);
172+
const {data: updatedData} = await axios.post('/undo-accept-images', data);
173+
const payload = {...updatedData, skipTreeUpdate};
173174

174175
dispatch({type: actionNames.UNDO_ACCEPT_IMAGES, payload});
175176
} catch (e) {

lib/test-adapter.js

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ module.exports = class TestAdapter {
3939
this._errors = this._hermione.errors;
4040
this._suite = SuiteAdapter.create(this._testResult);
4141
this._imagesSaver = this._hermione.htmlReporter.imagesSaver;
42-
this._testId = TestAdapter._mkTestId(this._testResult.fullTitle(), this._testResult.browserId);
42+
this._testId = this._mkTestId();
4343
this._errorDetails = undefined;
4444
this._timestamp = this._testResult.timestamp;
4545

@@ -95,14 +95,6 @@ module.exports = class TestAdapter {
9595
return imagesInfo.find(info => info.stateName === stateName);
9696
}
9797

98-
_getExpectedKey(stateName) {
99-
return TestAdapter._getExpectedKey(
100-
this._testResult.fullTitle(),
101-
this._testResult.browserId,
102-
stateName
103-
);
104-
}
105-
10698
_getExpectedPath(stateName, status, cacheExpectedPaths) {
10799
const key = this._getExpectedKey(stateName);
108100

@@ -433,6 +425,38 @@ module.exports = class TestAdapter {
433425
return result;
434426
}
435427

428+
updateCacheExpectedPath(stateName, expectedPath) {
429+
const key = this._getExpectedKey(stateName);
430+
431+
if (expectedPath) {
432+
globalCacheExpectedPaths.set(key, expectedPath);
433+
} else {
434+
globalCacheExpectedPaths.delete(key);
435+
}
436+
}
437+
438+
decreaseAttemptNumber() {
439+
const testId = this._mkTestId();
440+
const currentTestAttempt = testsAttempts.get(testId);
441+
const previousTestAttempt = currentTestAttempt - 1;
442+
443+
if (previousTestAttempt) {
444+
testsAttempts.set(testId, previousTestAttempt);
445+
} else {
446+
testsAttempts.delete(testId);
447+
}
448+
}
449+
450+
_mkTestId() {
451+
return this._testResult.fullTitle() + '.' + this._testResult.browserId;
452+
}
453+
454+
_getExpectedKey(stateName) {
455+
const shortTestId = getShortMD5(this._mkTestId());
456+
457+
return shortTestId + '#' + stateName;
458+
}
459+
436460
//parallelize and cache of 'looks-same.createDiff' (because it is very slow)
437461
async _saveDiffInWorker(imageDiffError, destPath, workers, cacheDiffImages = globalCacheDiffImages) {
438462
await utils.makeDirFor(destPath);
@@ -458,27 +482,4 @@ module.exports = class TestAdapter {
458482

459483
cacheDiffImages.set(hash, destPath);
460484
}
461-
462-
static _mkTestId(fullTitle, browserId) {
463-
return fullTitle + '.' + browserId;
464-
}
465-
466-
static _getExpectedKey(fullTitle, browserId, stateName) {
467-
const shortTestId = getShortMD5(this._mkTestId(fullTitle, browserId));
468-
469-
return shortTestId + '#' + stateName;
470-
}
471-
472-
static updateCacheExpectedPath(fullTitle, browserId, stateName, expectedPath) {
473-
const key = this._getExpectedKey(fullTitle, browserId, stateName);
474-
475-
globalCacheExpectedPaths.set(key, expectedPath);
476-
}
477-
478-
static decreaseAttemptNumber(fullTitle, browserName) {
479-
const testId = this._mkTestId(fullTitle, browserName);
480-
const currentTestAttempt = testsAttempts.get(testId);
481-
482-
testsAttempts.set(testId, currentTestAttempt - 1);
483-
}
484485
};

0 commit comments

Comments
 (0)