diff --git a/lib/static/modules/actions.js b/lib/static/modules/actions.js index c4bddb677..3c0642a7b 100644 --- a/lib/static/modules/actions.js +++ b/lib/static/modules/actions.js @@ -6,7 +6,7 @@ import actionNames from './action-names'; import modalTypes from './modal-types'; import {QUEUED, UPDATED} from '../../constants/test-statuses'; import {VIEW_CHANGED} from '../../constants/client-events'; -import {isSuiteFailed, isAcceptable} from './utils'; +import {isSuiteIdle, isSuiteSuccessful, isSuiteFailed, isAcceptable} from './utils'; import {isFailStatus, isErroredStatus} from '../../common-utils'; import { getRefImagesInfo, getAllOpenedImagesInfo, getImagesInfoId, filterByBro, rejectRefImagesInfo, @@ -51,12 +51,20 @@ export const runFailedTests = (fails, actionName = actionNames.RUN_FAILED_TESTS) return runTests({tests: fails, action: {type: actionName}}); }; +export const runSuccessfulTests = (successfulTests, actionName = actionNames.RETRY_TEST) => { + return runTests({tests: successfulTests, action: {type: actionName}}); +}; + export const retrySuite = (suite) => { return runTests({tests: [suite], action: {type: actionNames.RETRY_SUITE}}); }; export const retryTest = (suite, browserId = null) => { - return runFailedTests(assign({browserId}, suite), actionNames.RETRY_TEST); + const tests = assign({browserId}, suite); + + return isSuiteIdle(suite) || isSuiteSuccessful(suite) + ? runSuccessfulTests(tests, actionNames.RETRY_TEST) + : runFailedTests(tests, actionNames.RETRY_TEST); }; export const acceptOpened = (fails) => { diff --git a/lib/static/modules/utils.js b/lib/static/modules/utils.js index 4f6e94113..0f88f5fc4 100644 --- a/lib/static/modules/utils.js +++ b/lib/static/modules/utils.js @@ -1,16 +1,24 @@ 'use strict'; const {forOwn, pick, isArray, find, get, values, isEmpty} = require('lodash'); -const {isFailStatus, isErroredStatus, isSkippedStatus, determineStatus} = require('../../common-utils'); + +const { + isIdleStatus, + isSuccessStatus, + isFailStatus, + isErroredStatus, + isSkippedStatus, + determineStatus +} = require('../../common-utils'); + const {getCommonErrors} = require('../../constants/errors'); const {NO_REF_IMAGE_ERROR} = getCommonErrors(); function hasFailedImages(result) { - const {imagesInfo = [], status} = result; + const {imagesInfo = []} = result; - return imagesInfo.some(({status}) => isErroredStatus(status) || isFailStatus(status)) - || isErroredStatus(status) || isFailStatus(status); + return imagesInfo.some(({status}) => isErroredStatus(status) || isFailStatus(status)); } function isNoRefImageError(error) { @@ -24,11 +32,22 @@ function hasNoRefImageErrors({imagesInfo = []}) { function hasFails(node) { const {result} = node; - const isFailed = result && hasFailedImages(result); + + const isFailed = result && ( + hasFailedImages(result) || isErroredStatus(result.status) || isFailStatus(result.status) + ); return isFailed || walk(node, hasFails); } +function isSuiteIdle(suite) { + return isIdleStatus(suite.status); +} + +function isSuiteSuccessful(suite) { + return isSuccessStatus(suite.status); +} + function isSuiteFailed(suite) { return isFailStatus(suite.status) || isErroredStatus(suite.status); } @@ -202,6 +221,8 @@ module.exports = { isNoRefImageError, hasNoRefImageErrors, hasFails, + isSuiteIdle, + isSuiteSuccessful, isSuiteFailed, isAcceptable, hasRetries, diff --git a/test/unit/lib/static/modules/utils.js b/test/unit/lib/static/modules/utils.js index 29cc68cb3..c3c3ef0c0 100644 --- a/test/unit/lib/static/modules/utils.js +++ b/test/unit/lib/static/modules/utils.js @@ -2,6 +2,7 @@ const utils = require('lib/static/modules/utils'); const { + IDLE, FAIL, ERROR, SUCCESS @@ -16,29 +17,191 @@ const { } = require('../../../utils'); describe('static/modules/utils', () => { + describe('hasFails', () => { + describe('should return true for node if', () => { + const mkNode_ = ({imageStatus = SUCCESS, status = SUCCESS}) => { + return { + result: { + imagesInfo: [{status: SUCCESS}, {status: SUCCESS}, {status: imageStatus}], + status + } + }; + }; + + it('at least one image is with failed status', () => { + const node = mkNode_({imageStatus: FAIL}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('at least one image is with errored status', () => { + const node = mkNode_({imageStatus: ERROR}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('no images with failed or errored statuses but test is failed', () => { + const node = mkNode_({status: FAIL}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('no images with failed or errored statuses but test is errored', () => { + const node = mkNode_({status: ERROR}); + + assert.isTrue(utils.hasFails(node)); + }); + }); + + describe('should return true for node with good result (no fail or error status) if', () => { + const mkNode_ = (key, {imageStatus = SUCCESS, status = SUCCESS}) => { + const goodResult = { + result: { + imagesInfo: [{status: SUCCESS}, {status: SUCCESS}, {status: SUCCESS}], + status: SUCCESS + } + }; + + return { + ...goodResult, + [key]: [ + {...goodResult}, + { + result: { + imagesInfo: [{status: SUCCESS}, {status: SUCCESS}, {status: imageStatus}], + status + } + }, + {...goodResult} + ] + }; + }; + + it('at least one image in browsers of node is with failed status', () => { + const node = mkNode_('browsers', {imageStatus: FAIL}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('at least one image in children of node is with failed status', () => { + const node = mkNode_('children', {imageStatus: FAIL}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('at least one image in browsers is with errored status', () => { + const node = mkNode_('browsers', {imageStatus: ERROR}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('at least one image in children is with errored status', () => { + const node = mkNode_('children', {imageStatus: ERROR}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('in browsers no images with failed or errored statuses but test is failed', () => { + const node = mkNode_('browsers', {status: FAIL}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('in children no images with failed or errored statuses but test is failed', () => { + const node = mkNode_('children', {status: FAIL}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('in browsers no images with failed or errored statuses but test is errored', () => { + const node = mkNode_('browsers', {status: ERROR}); + + assert.isTrue(utils.hasFails(node)); + }); + + it('in children no images with failed or errored statuses but test is errored', () => { + const node = mkNode_('children', {status: ERROR}); + + assert.isTrue(utils.hasFails(node)); + }); + }); + }); + + describe('isSuiteIdle', () => { + it('should return true for idle test', () => { + assert.isTrue(utils.isSuiteIdle({status: IDLE})); + }); + + describe('should return false for', () => { + it('successful test', () => { + assert.isFalse(utils.isSuiteIdle({status: SUCCESS})); + }); + + it('failed test', () => { + assert.isFalse(utils.isSuiteIdle({status: FAIL})); + }); + + it('errored test', () => { + assert.isFalse(utils.isSuiteIdle({status: ERROR})); + }); + }); + }); + + describe('isSuiteSuccessful', () => { + it('should return true for successful test', () => { + assert.isTrue(utils.isSuiteSuccessful({status: SUCCESS})); + }); + + describe('should return false for', () => { + it('failed test', () => { + assert.isFalse(utils.isSuiteSuccessful({status: FAIL})); + }); + + it('errored test', () => { + assert.isFalse(utils.isSuiteSuccessful({status: ERROR})); + }); + }); + }); + + describe('isSuiteFailed', () => { + describe('should return true for', () => { + it('failed test', () => { + assert.isTrue(utils.isSuiteFailed({status: FAIL})); + }); + + it('errored test', () => { + assert.isTrue(utils.isSuiteFailed({status: ERROR})); + }); + }); + + it('should return false for successful test', () => { + assert.isFalse(utils.isSuiteFailed({status: SUCCESS})); + }); + }); + describe('isAcceptable', () => { - describe('should return true', () => { - it('for failed test', () => { + describe('should return true for', () => { + it('failed test', () => { assert.isTrue(utils.isAcceptable({status: FAIL})); }); - it('for test with missing reference image', () => { + it('test with missing reference image', () => { const error = {stack: NO_REF_IMAGE_ERROR}; assert.isTrue(utils.isAcceptable({status: ERROR, error})); }); }); - describe('should return false', () => { - it('for test with not screenshot error', () => { + describe('should return false for', () => { + it('test with not screenshot error', () => { assert.isFalse(utils.isAcceptable({status: ERROR, error: {}})); }); - it('for test with empty error', () => { + it('test with empty error', () => { assert.isFalse(utils.isAcceptable({status: ERROR, error: null})); }); - it('for not failed test', () => { + it('not failed test', () => { assert.isFalse(utils.isAcceptable({status: SUCCESS})); }); });