diff --git a/lib/static/icons/empty-report.svg b/lib/static/icons/empty-report.svg
new file mode 100644
index 000000000..a7ee97e5a
--- /dev/null
+++ b/lib/static/icons/empty-report.svg
@@ -0,0 +1 @@
+
diff --git a/lib/static/modules/reducers/static-image-accepter.js b/lib/static/modules/reducers/static-image-accepter.js
index 3c66d1ebd..8944125ae 100644
--- a/lib/static/modules/reducers/static-image-accepter.js
+++ b/lib/static/modules/reducers/static-image-accepter.js
@@ -62,9 +62,9 @@ export default (state, action) => {
const diff = set({}, ['staticImageAccepter', 'acceptableImages'], acceptableImagesDiff);
const imagesToCommitCount = get(state, ['staticImageAccepter', 'imagesToCommitCount']);
+ set(diff, ['staticImageAccepter', 'imagesToCommitCount'], imagesToCommitCount - action.payload.length);
for (const imageId of action.payload) {
set(acceptableImagesDiff, [imageId, 'commitStatus'], null);
- set(diff, ['staticImageAccepter', 'imagesToCommitCount'], imagesToCommitCount - 1);
}
return applyStateUpdate(state, diff);
diff --git a/lib/static/modules/reducers/tree/index.js b/lib/static/modules/reducers/tree/index.js
index 89834ba94..27eb67be4 100644
--- a/lib/static/modules/reducers/tree/index.js
+++ b/lib/static/modules/reducers/tree/index.js
@@ -257,6 +257,10 @@ export default ((state, action) => {
const {tree, view} = state;
const imageIdsToUnstage = action.payload;
+ const failedBrowserIds = [];
+ const failedSuiteIds = [];
+ const imageIds = [];
+
for (const imageId of imageIdsToUnstage) {
const originalStatus = get(state, ['staticImageAccepter', 'acceptableImages', imageId, 'originalStatus']);
@@ -269,15 +273,19 @@ export default ((state, action) => {
changeImageStatus(tree, imageId, originalStatus, diff.tree);
changeNodeState(tree.results.byId, failedResultId, {status: FAIL}, diff.tree.results.byId);
- failSuites(tree, failedSuiteId, diff.tree);
+ failedBrowserIds.push(failedBrowserId);
+ failedSuiteIds.push(failedSuiteId);
+ imageIds.push(imageId);
+ }
+
+ failSuites(tree, failedSuiteIds, diff.tree);
- calcSuitesOpenness({tree, expand: view.expand, suiteIds: [failedSuiteId], diff: diff.tree});
- calcBrowsersOpenness({tree, expand: view.expand, browserIds: [failedBrowserId], diff: diff.tree});
- calcImagesOpenness({tree, expand: view.expand, imageIds: [imageId], diff: diff.tree});
+ calcSuitesOpenness({tree, expand: view.expand, suiteIds: failedSuiteIds, diff: diff.tree});
+ calcBrowsersOpenness({tree, expand: view.expand, browserIds: failedBrowserIds, diff: diff.tree});
+ calcImagesOpenness({tree, expand: view.expand, imageIds, diff: diff.tree});
- calcBrowsersShowness({tree, view, browserIds: [failedBrowserId], diff: diff.tree});
- calcSuitesShowness({tree, suiteIds: [failedSuiteId], diff: diff.tree});
- }
+ calcBrowsersShowness({tree, view, browserIds: failedBrowserIds, diff: diff.tree});
+ calcSuitesShowness({tree, suiteIds: failedSuiteIds, diff: diff.tree});
return applyStateUpdate(state, diff);
}
diff --git a/lib/static/new-ui/components/AssertViewResult/index.tsx b/lib/static/new-ui/components/AssertViewResult/index.tsx
index e015b1dff..b4c90ffdf 100644
--- a/lib/static/new-ui/components/AssertViewResult/index.tsx
+++ b/lib/static/new-ui/components/AssertViewResult/index.tsx
@@ -1,7 +1,7 @@
import React, {ReactNode} from 'react';
import {connect} from 'react-redux';
-import {ImageEntity, State} from '@/static/new-ui/types/store';
+import {ImageEntity} from '@/static/new-ui/types/store';
import {DiffModeId, TestStatus} from '@/constants';
import {DiffViewer} from '../DiffViewer';
import {Screenshot} from '@/static/new-ui/components/Screenshot';
@@ -43,6 +43,6 @@ function AssertViewResultInternal({result, diffMode, style}: AssertViewResultPro
return null;
}
-export const AssertViewResult = connect((state: State) => ({
+export const AssertViewResult = connect(state => ({
diffMode: state.view.diffMode
}))(AssertViewResultInternal);
diff --git a/lib/static/new-ui/components/AttemptPicker/index.tsx b/lib/static/new-ui/components/AttemptPicker/index.tsx
index 9cbcdd155..51e0cf84b 100644
--- a/lib/static/new-ui/components/AttemptPicker/index.tsx
+++ b/lib/static/new-ui/components/AttemptPicker/index.tsx
@@ -2,7 +2,6 @@ import React, {ReactNode} from 'react';
import {connect, useDispatch, useSelector} from 'react-redux';
import {ArrowRotateRight} from '@gravity-ui/icons';
-import {State} from '@/static/new-ui/types/store';
import {AttemptPickerItem} from '@/static/new-ui/components/AttemptPickerItem';
import styles from './index.module.css';
import classNames from 'classnames';
@@ -26,9 +25,9 @@ function AttemptPickerInternal(props: AttemptPickerInternalProps): ReactNode {
const dispatch = useDispatch();
const currentBrowser = useSelector(getCurrentBrowser);
- const isRunTestsAvailable = useSelector((state: State) => state.app.availableFeatures)
+ const isRunTestsAvailable = useSelector(state => state.app.availableFeatures)
.find(feature => feature.name === RunTestsFeature.name);
- const isRunning = useSelector((state: State) => state.running);
+ const isRunning = useSelector(state => state.running);
const onAttemptPickHandler = (resultId: string, attemptIndex: number): void => {
if (!props.browserId || currentResultId === resultId) {
@@ -64,7 +63,7 @@ function AttemptPickerInternal(props: AttemptPickerInternalProps): ReactNode {
;
}
-export const AttemptPicker = connect((state: State) => {
+export const AttemptPicker = connect(state => {
let resultIds: string[] = [];
let currentResultId = '';
const browserId = state.app.suitesPage.currentBrowserId;
diff --git a/lib/static/new-ui/components/Card/AnimatedAppearCard.tsx b/lib/static/new-ui/components/Card/AnimatedAppearCard.tsx
index 0ce42be6f..c81b97aab 100644
--- a/lib/static/new-ui/components/Card/AnimatedAppearCard.tsx
+++ b/lib/static/new-ui/components/Card/AnimatedAppearCard.tsx
@@ -4,10 +4,9 @@ import cardStyles from './index.module.css';
import styles from './AnimatedAppearCard.module.css';
import classNames from 'classnames';
import {useSelector} from 'react-redux';
-import {State} from '@/static/new-ui/types/store';
export function AnimatedAppearCard(): ReactNode {
- const isInitialized = useSelector((state: State) => state.app.isInitialized);
+ const isInitialized = useSelector(state => state.app.isInitialized);
return
diff --git a/lib/static/new-ui/components/Card/EmptyReportCard.tsx b/lib/static/new-ui/components/Card/EmptyReportCard.tsx
index 391cdebb0..4c038efcf 100644
--- a/lib/static/new-ui/components/Card/EmptyReportCard.tsx
+++ b/lib/static/new-ui/components/Card/EmptyReportCard.tsx
@@ -1,5 +1,5 @@
import {TextHintCard} from '@/static/new-ui/components/Card/TextHintCard';
-import EmptyReport from '@/static/icons/empty-report.svg';
+import EmptyReport from '../../../icons/empty-report.svg';
import classNames from 'classnames';
import {Icon} from '@gravity-ui/uikit';
import {Check} from '@gravity-ui/icons';
diff --git a/lib/static/new-ui/components/CompactAttemptPicker/index.tsx b/lib/static/new-ui/components/CompactAttemptPicker/index.tsx
index c1c49cf31..86eb04cba 100644
--- a/lib/static/new-ui/components/CompactAttemptPicker/index.tsx
+++ b/lib/static/new-ui/components/CompactAttemptPicker/index.tsx
@@ -4,7 +4,6 @@ import React, {ReactNode} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import styles from './index.module.css';
-import {State} from '@/static/new-ui/types/store';
import {getCurrentNamedImage} from '@/static/new-ui/features/visual-checks/selectors';
import {getIconByStatus} from '@/static/new-ui/utils';
import {changeTestRetry} from '@/static/modules/actions';
@@ -13,11 +12,11 @@ export function CompactAttemptPicker(): ReactNode {
const dispatch = useDispatch();
const currentImage = useSelector(getCurrentNamedImage);
const currentBrowserId = currentImage?.browserId;
- const currentBrowser = useSelector((state: State) => currentBrowserId && state.tree.browsers.byId[currentBrowserId]);
- const resultsById = useSelector((state: State) => state.tree.results.byId);
+ const currentBrowser = useSelector(state => currentBrowserId && state.tree.browsers.byId[currentBrowserId]);
+ const resultsById = useSelector(state => state.tree.results.byId);
const totalAttemptsCount = currentBrowser ? currentBrowser.resultIds.length : null;
- const currentAttemptIndex = useSelector((state: State) => currentBrowser ? state.tree.browsers.stateById[currentBrowser.id].retryIndex : null);
+ const currentAttemptIndex = useSelector(state => currentBrowser ? state.tree.browsers.stateById[currentBrowser.id].retryIndex : null);
const onUpdate = ([value]: string[]): void => {
if (currentBrowserId) {
diff --git a/lib/static/new-ui/components/GuiniToolbarOverlay/index.tsx b/lib/static/new-ui/components/GuiniToolbarOverlay/index.tsx
index fb5c2cf9a..5f1473329 100644
--- a/lib/static/new-ui/components/GuiniToolbarOverlay/index.tsx
+++ b/lib/static/new-ui/components/GuiniToolbarOverlay/index.tsx
@@ -6,7 +6,6 @@ import {CloudArrowUpIn, TriangleExclamation} from '@gravity-ui/icons';
import styles from './index.module.css';
import classNames from 'classnames';
import {useDispatch, useSelector} from 'react-redux';
-import {State} from '@/static/new-ui/types/store';
import {
CommitResult,
staticAccepterCommitScreenshot,
@@ -23,22 +22,22 @@ export function GuiniToolbarOverlay(): ReactNode {
const dispatch = useDispatch();
const toaster = useToaster();
- const isInProgress = useSelector((state: State) => state.processing);
- const allImagesById = useSelector((state: State) => state.tree.images.byId);
- const acceptableImages = useSelector((state: State) => state.staticImageAccepter.acceptableImages);
- const delayedImages = useSelector((state: State) => state.staticImageAccepter.accepterDelayedImages);
+ const isInProgress = useSelector(state => state.processing);
+ const allImagesById = useSelector(state => state.tree.images.byId);
+ const acceptableImages = useSelector(state => state.staticImageAccepter.acceptableImages);
+ const delayedImages = useSelector(state => state.staticImageAccepter.accepterDelayedImages);
const stagedImages = Object.values(acceptableImages)
.filter(image => image.commitStatus === TestStatus.STAGED);
- const staticAccepterConfig = useSelector((state: State) => state.config.staticImageAccepter);
- const pullRequestUrl = useSelector((state: State) => state.config.staticImageAccepter.pullRequestUrl);
- const offset = useSelector((state: State) => state.ui.staticImageAccepterToolbar.offset);
+ const staticAccepterConfig = useSelector(state => state.config.staticImageAccepter);
+ const pullRequestUrl = useSelector(state => state.config.staticImageAccepter.pullRequestUrl);
+ const offset = useSelector(state => state.ui.staticImageAccepterToolbar.offset);
const location = useLocation();
const [isVisible, setIsVisible] = useState
(null);
const [isModalVisible, setIsModalVisible] = useState(false);
- const commitMessage = useSelector((state: State) => state.app.staticImageAccepterModal.commitMessage);
+ const commitMessage = useSelector(state => state.app.staticImageAccepterModal.commitMessage);
useEffect(() => {
const newIsVisible = stagedImages.length > 0 &&
diff --git a/lib/static/new-ui/components/LoadingBar/index.tsx b/lib/static/new-ui/components/LoadingBar/index.tsx
index 9c597968c..f2e8534ff 100644
--- a/lib/static/new-ui/components/LoadingBar/index.tsx
+++ b/lib/static/new-ui/components/LoadingBar/index.tsx
@@ -3,15 +3,14 @@ import React, {ReactNode, useEffect, useRef} from 'react';
import styles from './index.module.css';
import {useSelector} from 'react-redux';
import {getTotalLoadingProgress} from '@/static/new-ui/app/selectors';
-import {State} from '@/static/new-ui/types/store';
import classNames from 'classnames';
export function LoadingBar(): ReactNode {
- const isVisible = useSelector((state: State) => state.app.loading.isVisible);
- const isInProgress = useSelector((state: State) => state.app.loading.isInProgress);
+ const isVisible = useSelector(state => state.app.loading.isVisible);
+ const isInProgress = useSelector(state => state.app.loading.isInProgress);
const isVisibleRef = useRef(isVisible);
const progress = useSelector(getTotalLoadingProgress);
- const taskTitle = useSelector((state: State) => state.app.loading.taskTitle);
+ const taskTitle = useSelector(state => state.app.loading.taskTitle);
const [hidden, setHidden] = React.useState(true);
diff --git a/lib/static/new-ui/components/MainLayout/index.tsx b/lib/static/new-ui/components/MainLayout/index.tsx
index 53f0b65c3..014b7323e 100644
--- a/lib/static/new-ui/components/MainLayout/index.tsx
+++ b/lib/static/new-ui/components/MainLayout/index.tsx
@@ -9,7 +9,6 @@ import {SettingsPanel} from '@/static/new-ui/components/SettingsPanel';
import TestplaneIcon from '../../../icons/testplane-mono.svg';
import styles from './index.module.css';
import {Footer} from './Footer';
-import {State} from '@/static/new-ui/types/store';
import {EmptyReportCard} from '@/static/new-ui/components/Card/EmptyReportCard';
export enum PanelId {
@@ -41,7 +40,7 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
const isInitialized = useSelector(getIsInitialized);
- const browsersById = useSelector((state: State) => state.tree.browsers.byId);
+ const browsersById = useSelector(state => state.tree.browsers.byId);
const isReportEmpty = isInitialized && Object.keys(browsersById).length === 0;
const [visiblePanel, setVisiblePanel] = useState(null);
diff --git a/lib/static/new-ui/components/SettingsPanel/index.tsx b/lib/static/new-ui/components/SettingsPanel/index.tsx
index 959cb6ecc..cc63a2bc8 100644
--- a/lib/static/new-ui/components/SettingsPanel/index.tsx
+++ b/lib/static/new-ui/components/SettingsPanel/index.tsx
@@ -6,14 +6,13 @@ import {useDispatch, useSelector} from 'react-redux';
import {LocalStorageKey, UiMode} from '@/constants/local-storage';
import * as actions from '@/static/modules/actions';
-import {State} from '@/static/new-ui/types/store';
import useLocalStorage from '@/static/hooks/useLocalStorage';
import styles from './index.module.css';
export function SettingsPanel(): ReactNode {
const dispatch = useDispatch();
- const baseHost = useSelector((state: State) => state.view.baseHost);
+ const baseHost = useSelector(state => state.view.baseHost);
const [, setUiMode] = useLocalStorage(LocalStorageKey.UIMode, UiMode.New);
const onBaseHostChange = (event: React.ChangeEvent): void => {
diff --git a/lib/static/new-ui/features/suites/components/BrowsersSelect/index.tsx b/lib/static/new-ui/features/suites/components/BrowsersSelect/index.tsx
index a2e177d2c..9deb5cb1c 100644
--- a/lib/static/new-ui/features/suites/components/BrowsersSelect/index.tsx
+++ b/lib/static/new-ui/features/suites/components/BrowsersSelect/index.tsx
@@ -7,7 +7,6 @@ import {bindActionCreators} from 'redux';
import * as actions from '@/static/modules/actions';
import {BrowserIcon} from '@/static/new-ui/features/suites/components/BrowsersSelect/BrowserIcon';
import {getIsInitialized} from '@/static/new-ui/store/selectors';
-import {State} from '@/static/new-ui/types/store';
import {BrowserItem} from '@/types';
import styles from './index.module.css';
@@ -175,7 +174,7 @@ function BrowsersSelectInternal({browsers, filteredBrowsers, actions}: BrowsersS
}
export const BrowsersSelect = connect(
- (state: State) => ({
+ state => ({
filteredBrowsers: state.view.filteredBrowsers,
browsers: state.browsers
}),
diff --git a/lib/static/new-ui/features/suites/components/ScreenshotsTreeViewItem/index.tsx b/lib/static/new-ui/features/suites/components/ScreenshotsTreeViewItem/index.tsx
index d2edc4ad8..397d7f1f0 100644
--- a/lib/static/new-ui/features/suites/components/ScreenshotsTreeViewItem/index.tsx
+++ b/lib/static/new-ui/features/suites/components/ScreenshotsTreeViewItem/index.tsx
@@ -4,7 +4,7 @@ import React, {ReactNode} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {AssertViewResult} from '@/static/new-ui/components/AssertViewResult';
-import {ImageEntity, State} from '@/static/new-ui/types/store';
+import {ImageEntity} from '@/static/new-ui/types/store';
import {DiffModeId, DiffModes, EditScreensFeature, TestStatus} from '@/constants';
import {
acceptTest,
@@ -25,13 +25,13 @@ interface ScreenshotsTreeViewItemProps {
export function ScreenshotsTreeViewItem(props: ScreenshotsTreeViewItemProps): ReactNode {
const dispatch = useDispatch();
- const diffMode = useSelector((state: State) => state.view.diffMode);
- const isEditScreensAvailable = useSelector((state: State) => state.app.availableFeatures)
+ const diffMode = useSelector(state => state.view.diffMode);
+ const isEditScreensAvailable = useSelector(state => state.app.availableFeatures)
.find(feature => feature.name === EditScreensFeature.name);
- const isStaticImageAccepterEnabled = useSelector((state: State) => state.staticImageAccepter.enabled);
- const isRunning = useSelector((state: State) => state.running);
- const isProcessing = useSelector((state: State) => state.processing);
- const isGui = useSelector((state: State) => state.gui);
+ const isStaticImageAccepterEnabled = useSelector(state => state.staticImageAccepter.enabled);
+ const isRunning = useSelector(state => state.running);
+ const isProcessing = useSelector(state => state.processing);
+ const isGui = useSelector(state => state.gui);
const isDiffModeSwitcherVisible = props.image.status === TestStatus.FAIL && props.image.diffImg;
diff --git a/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx b/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx
index 242e43fea..14e7f2aea 100644
--- a/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx
+++ b/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx
@@ -19,7 +19,7 @@ import * as actions from '@/static/modules/actions';
import {CollapsibleSection} from '@/static/new-ui/features/suites/components/CollapsibleSection';
import {MetaInfo} from '@/static/new-ui/components/MetaInfo';
import {getIsInitialized} from '@/static/new-ui/store/selectors';
-import {ResultEntity, State} from '@/static/new-ui/types/store';
+import {ResultEntity} from '@/static/new-ui/types/store';
import {AttemptPicker} from '../../../../components/AttemptPicker';
import styles from './index.module.css';
@@ -86,7 +86,7 @@ function SuitesPageInternal({currentResult, actions, visibleBrowserIds}: SuitesP
}
export const SuitesPage = connect(
- (state: State) => {
+ state => {
const currentResult = getCurrentResult(state);
return {
diff --git a/lib/static/new-ui/features/suites/components/SuitesTreeView/index.tsx b/lib/static/new-ui/features/suites/components/SuitesTreeView/index.tsx
index 6f8287927..14676e0fc 100644
--- a/lib/static/new-ui/features/suites/components/SuitesTreeView/index.tsx
+++ b/lib/static/new-ui/features/suites/components/SuitesTreeView/index.tsx
@@ -16,7 +16,6 @@ import {
} from '@/static/new-ui/features/suites/components/SuitesPage/types';
import {TreeViewItemTitle} from '@/static/new-ui/features/suites/components/TreeViewItemTitle';
import {TreeViewItemSubtitle} from '@/static/new-ui/features/suites/components/TreeViewItemSubtitle';
-import {State} from '@/static/new-ui/types/store';
import {
getTreeViewExpandedById,
getTreeViewItems
@@ -39,10 +38,10 @@ export const SuitesTreeView = forwardRef state.app.isInitialized);
- const currentBrowserId = useSelector((state: State) => state.app.suitesPage.currentBrowserId);
- const treeViewItems = useSelector((state: State) => getTreeViewItems(state).tree);
- const treeViewExpandedById = useSelector((state: State) => getTreeViewExpandedById(state));
+ const isInitialized = useSelector(state => state.app.isInitialized);
+ const currentBrowserId = useSelector(state => state.app.suitesPage.currentBrowserId);
+ const treeViewItems = useSelector(state => getTreeViewItems(state).tree);
+ const treeViewExpandedById = useSelector(state => getTreeViewExpandedById(state));
const list = useList({
items: treeViewItems,
@@ -84,6 +83,8 @@ export const SuitesTreeView = forwardRef {
virtualizer.scrollToIndex(list.structure.visibleFlattenIds.indexOf(suiteId), {align: 'center'});
}, 50);
diff --git a/lib/static/new-ui/features/suites/components/TestNameFilter/index.tsx b/lib/static/new-ui/features/suites/components/TestNameFilter/index.tsx
index 1273bc9dd..637d0d5d9 100644
--- a/lib/static/new-ui/features/suites/components/TestNameFilter/index.tsx
+++ b/lib/static/new-ui/features/suites/components/TestNameFilter/index.tsx
@@ -4,7 +4,6 @@ import {debounce} from 'lodash';
import {connect, useSelector} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actions from '@/static/modules/actions';
-import {State} from '@/static/new-ui/types/store';
import {getIsInitialized} from '@/static/new-ui/store/selectors';
interface TestNameFilterProps {
@@ -32,6 +31,6 @@ function TestNameFilterInternal(props: TestNameFilterProps): ReactNode {
}
export const TestNameFilter = connect(
- (state: State) => ({testNameFilter: state.view.testNameFilter}),
+ state => ({testNameFilter: state.view.testNameFilter}),
(dispatch) => ({actions: bindActionCreators(actions, dispatch)})
)(TestNameFilterInternal);
diff --git a/lib/static/new-ui/features/suites/components/TestStatusFilter/index.tsx b/lib/static/new-ui/features/suites/components/TestStatusFilter/index.tsx
index 06ce7157d..f5fb7bdf9 100644
--- a/lib/static/new-ui/features/suites/components/TestStatusFilter/index.tsx
+++ b/lib/static/new-ui/features/suites/components/TestStatusFilter/index.tsx
@@ -6,7 +6,6 @@ import {bindActionCreators} from 'redux';
import {TestStatus, ViewMode} from '@/constants';
import * as actions from '@/static/modules/actions';
import {getStatusCounts, StatusCounts} from '@/static/new-ui/features/suites/components/TestStatusFilter/selectors';
-import {State} from '@/static/new-ui/types/store';
import {getIconByStatus} from '@/static/new-ui/utils';
import {getIsInitialized} from '@/static/new-ui/store/selectors';
import styles from './index.module.css';
@@ -41,7 +40,7 @@ function TestStatusFilterInternal({statusCounts, actions, viewMode}: TestStatusF
}
export const TestStatusFilter = connect(
- (state: State) => ({
+ state => ({
statusCounts: getStatusCounts(state),
viewMode: state.view.viewMode
}),
diff --git a/lib/static/new-ui/features/suites/components/TestSteps/index.tsx b/lib/static/new-ui/features/suites/components/TestSteps/index.tsx
index 2cb033de2..ad9ef8b21 100644
--- a/lib/static/new-ui/features/suites/components/TestSteps/index.tsx
+++ b/lib/static/new-ui/features/suites/components/TestSteps/index.tsx
@@ -9,7 +9,6 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {CollapsibleSection} from '@/static/new-ui/features/suites/components/CollapsibleSection';
-import {State} from '@/static/new-ui/types/store';
import {TreeViewItemIcon} from '@/static/new-ui/components/TreeViewItemIcon';
import {TestStepArgs} from '@/static/new-ui/features/suites/components/TestStepArgs';
import {getIconByStatus} from '@/static/new-ui/utils';
@@ -94,7 +93,7 @@ function TestStepsInternal(props: TestStepsProps): ReactNode {
} />;
}
-export const TestSteps = connect((state: State) => ({
+export const TestSteps = connect(state => ({
resultId: getCurrentResultId(state) ?? '',
testSteps: getTestSteps(state),
stepsExpandedById: getStepsExpandedById(state)
diff --git a/lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx b/lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx
index 3f0ba2af0..e8a10e26d 100644
--- a/lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx
+++ b/lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx
@@ -24,7 +24,7 @@ import {
selectAll, staticAccepterStageScreenshot, staticAccepterUnstageScreenshot,
undoAcceptImages
} from '@/static/modules/actions';
-import {ImageEntity, State} from '@/static/new-ui/types/store';
+import {ImageEntity} from '@/static/new-ui/types/store';
import {CHECKED, INDETERMINATE} from '@/constants/checked-statuses';
import {IconButton} from '@/static/new-ui/components/IconButton';
import {
@@ -44,39 +44,32 @@ interface TreeActionsToolbarProps {
export function TreeActionsToolbar(props: TreeActionsToolbarProps): ReactNode {
const dispatch = useDispatch();
- const rootSuiteIds = useSelector((state: State) => state.tree.suites.allRootIds);
- const suitesStateById = useSelector((state: State) => state.tree.suites.stateById);
- const browsersStateById = useSelector((state: State) => state.tree.browsers.stateById);
- const browsersById = useSelector((state: State) => state.tree.browsers.byId);
+ const rootSuiteIds = useSelector(state => state.tree.suites.allRootIds);
+ const suitesStateById = useSelector(state => state.tree.suites.stateById);
+ const browsersStateById = useSelector(state => state.tree.browsers.stateById);
+ const browsersById = useSelector(state => state.tree.browsers.byId);
const selectedTests = useSelector(getCheckedTests);
const visibleBrowserIds: string[] = useSelector(getVisibleBrowserIds);
const isInitialized = useSelector(getIsInitialized);
- const isRunTestsAvailable = useSelector((state: State) => state.app.availableFeatures)
+ const isRunTestsAvailable = useSelector(state => state.app.availableFeatures)
.find(feature => feature.name === RunTestsFeature.name);
- const isRunning = useSelector((state: State) => state.running);
+ const isRunning = useSelector(state => state.running);
- const isEditScreensAvailable = useSelector((state: State) => state.app.availableFeatures)
+ const isEditScreensAvailable = useSelector(state => state.app.availableFeatures)
.find(feature => feature.name === EditScreensFeature.name);
const isSelectedAll = useMemo(() => {
- for (const suiteId of rootSuiteIds) {
- if (suitesStateById[suiteId].checkStatus !== CHECKED) {
- return false;
- }
- }
-
- return true;
+ return rootSuiteIds.every(suiteId => suitesStateById[suiteId].checkStatus === CHECKED);
}, [suitesStateById, rootSuiteIds]);
const isSelectedAtLeastOne = useMemo(() => {
- for (const suiteId of rootSuiteIds) {
- if (suitesStateById[suiteId].shouldBeShown && (suitesStateById[suiteId].checkStatus === CHECKED || suitesStateById[suiteId].checkStatus === INDETERMINATE)) {
- return true;
- }
- }
+ return rootSuiteIds.some(suiteId => {
+ const isShown = suitesStateById[suiteId].shouldBeShown;
+ const isChecked = suitesStateById[suiteId].checkStatus === CHECKED || suitesStateById[suiteId].checkStatus === INDETERMINATE;
- return false;
+ return isShown && isChecked;
+ });
}, [suitesStateById, rootSuiteIds]);
const isStaticImageAccepterEnabled = useSelector(getIsStaticImageAccepterEnabled);
@@ -90,18 +83,12 @@ export function TreeActionsToolbar(props: TreeActionsToolbarProps): ReactNode {
const isUndoButtonVisible = isAtLeastOneRevertable && !isAtLeastOneAcceptable;
const selectedTestsCount = useMemo(() => {
- let count = 0;
+ const browserStates = Object.values(browsersStateById);
- for (const browser of Object.values(browsersStateById)) {
- if (browser.checkStatus === CHECKED) {
- count++;
- }
- }
-
- return count;
+ return browserStates.reduce((acc, state) => acc + Number(state.checkStatus === CHECKED), 0);
}, [browsersStateById]);
- const handleSelectAll = (): void => {
+ const handleToggleAll = (): void => {
if (isSelectedAll) {
dispatch(deselectAll());
} else {
@@ -134,7 +121,7 @@ export function TreeActionsToolbar(props: TreeActionsToolbarProps): ReactNode {
.filter(image => isScreenRevertable({image, gui: isGuiMode, isLastResult: true, isStaticImageAccepterEnabled}))
.map(image => image.id);
- if (isStaticImageAccepterEnabled && !isGuiMode) {
+ if (isStaticImageAccepterEnabled) {
dispatch(staticAccepterUnstageScreenshot(acceptableImageIds));
} else {
dispatch(undoAcceptImages(acceptableImageIds));
@@ -146,27 +133,30 @@ export function TreeActionsToolbar(props: TreeActionsToolbarProps): ReactNode {
.filter(image => isAcceptable(image))
.map(image => image.id);
- if (isStaticImageAccepterEnabled && !isGuiMode) {
+ if (isStaticImageAccepterEnabled) {
dispatch(staticAccepterStageScreenshot(acceptableImageIds));
} else {
dispatch(acceptOpened(acceptableImageIds));
}
};
+ const selectedOrVisible = isSelectedAtLeastOne ? 'selected' : 'visible';
+ const areActionsDisabled = isRunning || !isInitialized;
+
const viewButtons = <>
{isRunTestsAvailable && }
- tooltip={isSelectedAtLeastOne ? 'Run selected' : 'Run visible'} view={'flat'} onClick={handleRun}
+ tooltip={`Run ${selectedOrVisible}`} view={'flat'} onClick={handleRun}
disabled={isRunning || !isInitialized}>}
{isEditScreensAvailable && (
isUndoButtonVisible ?
- } tooltip={isSelectedAtLeastOne ? 'Undo accepting selected screenshots' : 'Undo accepting visible screenshots'} view={'flat'} onClick={handleUndo} disabled={isRunning || !isInitialized}> :
- } tooltip={isSelectedAtLeastOne ? 'Accept selected screenshots' : 'Accept visible screenshots'} view={'flat'} onClick={handleAccept} disabled={isRunning || !isInitialized}>
+ } tooltip={`Undo accepting ${selectedOrVisible} screenshots`} view={'flat'} onClick={handleUndo} disabled={areActionsDisabled}> :
+ } tooltip={`Accept ${selectedOrVisible} screenshots`} view={'flat'} onClick={handleAccept} disabled={areActionsDisabled}>
)}
} tooltip={'Focus on active test'} view={'flat'} onClick={props.onHighlightCurrentTest} disabled={!isInitialized}/>
} tooltip={'Expand all'} view={'flat'} onClick={handleExpandAll} disabled={!isInitialized}/>
} tooltip={'Collapse all'} view={'flat'} onClick={handleCollapseAll} disabled={!isInitialized}/>
- } tooltip={isSelectedAll ? 'Deselect all' : 'Select all'} view={'flat'} onClick={handleSelectAll} disabled={!isInitialized}/>
+ } tooltip={isSelectedAll ? 'Deselect all' : 'Select all'} view={'flat'} onClick={handleToggleAll} disabled={!isInitialized}/>
>;
return
diff --git a/lib/static/new-ui/features/suites/components/TreeViewItemTitle/index.tsx b/lib/static/new-ui/features/suites/components/TreeViewItemTitle/index.tsx
index e54145436..e248e2ab2 100644
--- a/lib/static/new-ui/features/suites/components/TreeViewItemTitle/index.tsx
+++ b/lib/static/new-ui/features/suites/components/TreeViewItemTitle/index.tsx
@@ -5,7 +5,6 @@ import classNames from 'classnames';
import {Checkbox} from '@gravity-ui/uikit';
import {useDispatch, useSelector} from 'react-redux';
import {toggleBrowserCheckbox, toggleSuiteCheckbox} from '@/static/modules/actions';
-import {State} from '@/static/new-ui/types/store';
import {getToggledCheckboxState, isCheckboxChecked, isCheckboxIndeterminate} from '@/common-utils';
interface TreeViewItemTitleProps {
@@ -15,7 +14,7 @@ interface TreeViewItemTitleProps {
export function TreeViewItemTitle({item, className}: TreeViewItemTitleProps): React.JSX.Element {
const dispatch = useDispatch();
- const checkStatus = useSelector((state: State) =>
+ const checkStatus = useSelector(state =>
item.type === TreeViewItemType.Suite ? state.tree.suites.stateById[item.id].checkStatus : state.tree.browsers.stateById[item.id].checkStatus);
const ref = useRef
(null);
diff --git a/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/index.tsx b/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/index.tsx
index 90d3ade23..35efb0bfd 100644
--- a/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/index.tsx
+++ b/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/index.tsx
@@ -5,7 +5,6 @@ import React, {ReactNode} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {SplitViewLayout} from '@/static/new-ui/components/SplitViewLayout';
-import {State} from '@/static/new-ui/types/store';
import {UiCard} from '@/static/new-ui/components/Card/UiCard';
import {
getCurrentImage,
@@ -40,17 +39,17 @@ export function VisualChecksPage(): ReactNode {
const onPreviousImageHandler = (): void => void dispatch(visualChecksPageSetCurrentNamedImage(visibleNamedImageIds[currentNamedImageIndex - 1]));
const onNextImageHandler = (): void => void dispatch(visualChecksPageSetCurrentNamedImage(visibleNamedImageIds[currentNamedImageIndex + 1]));
- const diffMode = useSelector((state: State) => state.view.diffMode);
+ const diffMode = useSelector(state => state.view.diffMode);
const onChangeHandler = (diffMode: DiffModeId): void => {
dispatch(changeDiffMode(diffMode));
};
- const isStaticImageAccepterEnabled = useSelector((state: State) => state.staticImageAccepter.enabled);
- const isEditScreensAvailable = useSelector((state: State) => state.app.availableFeatures)
+ const isStaticImageAccepterEnabled = useSelector(state => state.staticImageAccepter.enabled);
+ const isEditScreensAvailable = useSelector(state => state.app.availableFeatures)
.find(feature => feature.name === EditScreensFeature.name);
- const isRunning = useSelector((state: State) => state.running);
- const isProcessing = useSelector((state: State) => state.processing);
- const isGui = useSelector((state: State) => state.gui);
+ const isRunning = useSelector(state => state.running);
+ const isProcessing = useSelector(state => state.processing);
+ const isGui = useSelector(state => state.gui);
const onScreenshotAccept = (): void => {
if (!currentImage) {
@@ -75,14 +74,14 @@ export function VisualChecksPage(): ReactNode {
}
};
- const currentBrowserId = useSelector((state: State) => state.tree.results.byId[currentImage?.parentId ?? '']?.parentId);
- const currentBrowser = useSelector((state: State) => currentBrowserId && state.tree.browsers.byId[currentBrowserId]);
+ const currentBrowserId = useSelector(state => state.tree.results.byId[currentImage?.parentId ?? '']?.parentId);
+ const currentBrowser = useSelector(state => currentBrowserId && state.tree.browsers.byId[currentBrowserId]);
const currentResultId = currentImage?.parentId;
const isLastResult = Boolean(currentResultId && currentBrowser && currentResultId === currentBrowser.resultIds[currentBrowser.resultIds.length - 1]);
const isUndoAvailable = isScreenRevertable({gui: isGui, image: currentImage ?? {}, isLastResult, isStaticImageAccepterEnabled});
- const isInitialized = useSelector((state: State) => state.app.isInitialized);
+ const isInitialized = useSelector(state => state.app.isInitialized);
return
diff --git a/lib/static/new-ui/store/selectors.ts b/lib/static/new-ui/store/selectors.ts
index 021f53431..5b025a212 100644
--- a/lib/static/new-ui/store/selectors.ts
+++ b/lib/static/new-ui/store/selectors.ts
@@ -18,5 +18,5 @@ export const getResults = (state: State): Record => state.
export const getImages = (state: State): Record => state.tree.images.byId;
export const getIsInitialized = (state: State): boolean => state.app.isInitialized;
-export const getIsStaticImageAccepterEnabled = (state: State): boolean => state.config.staticImageAccepter.enabled;
+export const getIsStaticImageAccepterEnabled = (state: State): boolean => state.staticImageAccepter.enabled;
export const getIsGui = (state: State): boolean => state.gui;
diff --git a/lib/static/new-ui/types/store.ts b/lib/static/new-ui/types/store.ts
index 7e8c1891f..3134ef939 100644
--- a/lib/static/new-ui/types/store.ts
+++ b/lib/static/new-ui/types/store.ts
@@ -210,3 +210,8 @@ export interface State {
imagesToCommitCount: number;
};
}
+
+declare module 'react-redux' {
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
+ export interface DefaultRootState extends State {}
+}
diff --git a/lib/static/new-ui/utils/assert-view-status.tsx b/lib/static/new-ui/utils/assert-view-status.tsx
index f45f49260..aa445695e 100644
--- a/lib/static/new-ui/utils/assert-view-status.tsx
+++ b/lib/static/new-ui/utils/assert-view-status.tsx
@@ -17,20 +17,23 @@ import React, {ReactNode} from 'react';
export const getAssertViewStatusIcon = (image: ImageEntity | null): ReactNode => {
if (image === null) {
return ;
- } else if (image.status === TestStatus.SUCCESS) {
- return ;
- } else if (image.status === TestStatus.STAGED) {
- return ;
- } else if (image.status === TestStatus.COMMITED) {
- return ;
} else if (isNoRefImageError((image as ImageEntityError).error)) {
return ;
} else if (isInvalidRefImageError((image as ImageEntityError).error)) {
return ;
- } else if (image.status === TestStatus.FAIL) {
- return ;
- } else if (image.status === TestStatus.UPDATED) {
- return ;
+ }
+
+ switch (image.status) {
+ case TestStatus.SUCCESS:
+ return ;
+ case TestStatus.STAGED:
+ return ;
+ case TestStatus.COMMITED:
+ return ;
+ case TestStatus.FAIL:
+ return ;
+ case TestStatus.UPDATED:
+ return ;
}
return ;
@@ -39,20 +42,23 @@ export const getAssertViewStatusIcon = (image: ImageEntity | null): ReactNode =>
export const getAssertViewStatusMessage = (image: ImageEntity | null): string => {
if (image === null) {
return 'Image is absent';
- } else if (image.status === TestStatus.SUCCESS) {
- return 'Images match';
- } else if (image.status === TestStatus.STAGED) {
- return 'Image is staged';
- } else if (image.status === TestStatus.COMMITED) {
- return 'Image was committed';
} else if (isNoRefImageError((image as ImageEntityError).error)) {
return 'Reference not found';
} else if (isInvalidRefImageError((image as ImageEntityError).error)) {
return 'Reference is broken';
- } else if (image.status === TestStatus.FAIL) {
- return 'Difference detected';
- } else if (image.status === TestStatus.UPDATED) {
- return 'Reference updated';
+ }
+
+ switch (image.status) {
+ case TestStatus.SUCCESS:
+ return 'Images match';
+ case TestStatus.STAGED:
+ return 'Image is staged';
+ case TestStatus.COMMITED:
+ return 'Image was committed';
+ case TestStatus.FAIL:
+ return 'Difference detected';
+ case TestStatus.UPDATED:
+ return 'Reference updated';
}
return 'Failed to compare';
diff --git a/test/unit/lib/static/modules/reducers/static-image-accepter.ts b/test/unit/lib/static/modules/reducers/static-image-accepter.ts
index 0757fea1e..fe2b3c12e 100644
--- a/test/unit/lib/static/modules/reducers/static-image-accepter.ts
+++ b/test/unit/lib/static/modules/reducers/static-image-accepter.ts
@@ -144,9 +144,11 @@ describe('lib/static/modules/reducers/static-image-accepter', () => {
});
describe(actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, () => {
- it('should unstage image, decrementing "imagesToCommitCount"', () => {
+ it('should unstage images, decrementing "imagesToCommitCount"', () => {
const staticImageAccepter = mkStaticImageAccepter({acceptableImages: {
- 'imageId': mkAcceptableImage({id: 'imageId'})
+ 'imageId1': mkAcceptableImage({id: 'imageId1'}),
+ 'imageId2': mkAcceptableImage({id: 'imageId2'}),
+ 'imageId3': mkAcceptableImage({id: 'imageId3'})
}});
const imagesById = mkImage({id: 'imageId', stateName: 'state', parentId: 'resultId'});
@@ -156,11 +158,12 @@ describe('lib/static/modules/reducers/static-image-accepter', () => {
const state = {staticImageAccepter, tree};
- const newState = reducer(state, {type: actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, payload: ['imageId']});
+ const newState = reducer(state, {type: actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, payload: ['imageId1', 'imageId2']});
- assert.equal(newState.staticImageAccepter.acceptableImages['imageId'].commitStatus, null);
+ assert.equal(newState.staticImageAccepter.acceptableImages['imageId1'].commitStatus, null);
+ assert.equal(newState.staticImageAccepter.acceptableImages['imageId2'].commitStatus, null);
- assert.equal(newState.staticImageAccepter.imagesToCommitCount, state.staticImageAccepter.imagesToCommitCount - 1);
+ assert.equal(newState.staticImageAccepter.imagesToCommitCount, state.staticImageAccepter.imagesToCommitCount - 2);
});
});