Skip to content

Commit

Permalink
fix: add ability to accept/undo screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
shadowusr committed Nov 7, 2024
1 parent 3c56a2a commit 9bf621d
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 34 deletions.
2 changes: 1 addition & 1 deletion lib/static/components/state/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class State extends Component {

onScreenshotUndo = () => {
if (this.props.isStaticImageAccepterEnabled) {
this.props.actions.staticAccepterUnstageScreenshot(this.props.imageId);
this.props.actions.staticAccepterUnstageScreenshot([this.props.imageId]);
} else {
this.props.actions.undoAcceptImage(this.props.imageId);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/static/modules/actions/static-accepter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export const staticAccepterStageScreenshot = (imageIds: string[]): StaticAccepte
return {type: actionNames.STATIC_ACCEPTER_STAGE_SCREENSHOT, payload: imageIds};
};

type StaticAccepterUnstageScreenshotAction = Action<typeof actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, {imageId: string}>;
export const staticAccepterUnstageScreenshot = (imageId: string): StaticAccepterUnstageScreenshotAction => {
return {type: actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, payload: {imageId}};
type StaticAccepterUnstageScreenshotAction = Action<typeof actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, string[]>;
export const staticAccepterUnstageScreenshot = (imageIds: string[]): StaticAccepterUnstageScreenshotAction => {
return {type: actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT, payload: imageIds};
};

type StaticAccepterOpenConfirmAction = Action<typeof actionNames.OPEN_MODAL, {
Expand Down
8 changes: 4 additions & 4 deletions lib/static/modules/reducers/static-image-accepter.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ export default (state, action) => {
}

case actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT: {
const {imageId} = action.payload;

const acceptableImagesDiff = {};
const diff = set({}, ['staticImageAccepter', 'acceptableImages'], acceptableImagesDiff);
const imagesToCommitCount = get(state, ['staticImageAccepter', 'imagesToCommitCount']);

set(acceptableImagesDiff, [imageId, 'commitStatus'], null);
set(diff, ['staticImageAccepter', 'imagesToCommitCount'], imagesToCommitCount - 1);
for (const imageId of action.payload) {
set(acceptableImagesDiff, [imageId, 'commitStatus'], null);
set(diff, ['staticImageAccepter', 'imagesToCommitCount'], imagesToCommitCount - 1);
}

return applyStateUpdate(state, diff);
}
Expand Down
34 changes: 20 additions & 14 deletions lib/static/modules/reducers/tree/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,26 +255,32 @@ export default ((state, action) => {

case actionNames.STATIC_ACCEPTER_UNSTAGE_SCREENSHOT: {
const {tree, view} = state;
const {imageId} = action.payload;
const originalStatus = get(state, ['staticImageAccepter', 'acceptableImages', imageId, 'originalStatus']);
const imageIdsToUnstage = action.payload;

const failedResultId = tree.images.byId[imageId].parentId;
const failedBrowserId = tree.results.byId[failedResultId].parentId;
const failedSuiteId = tree.browsers.byId[failedBrowserId].parentId;
for (const imageId of imageIdsToUnstage) {
const originalStatus = get(state, ['staticImageAccepter', 'acceptableImages', imageId, 'originalStatus']);

ensureDiffProperty(diff, ['tree', 'results', 'byId']);
const failedResultId = tree.images.byId[imageId].parentId;
const failedBrowserId = tree.results.byId[failedResultId].parentId;
const failedSuiteId = tree.browsers.byId[failedBrowserId].parentId;

changeImageStatus(tree, imageId, originalStatus, diff.tree);
changeNodeState(tree.results.byId, failedResultId, {status: FAIL}, diff.tree.results.byId);
ensureDiffProperty(diff, ['tree', 'results', 'byId']);

failSuites(tree, failedSuiteId, diff.tree);
changeImageStatus(tree, imageId, originalStatus, diff.tree);
changeNodeState(tree.results.byId, failedResultId, {status: FAIL}, diff.tree.results.byId);

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});
failSuites(tree, failedSuiteId, diff.tree);

calcBrowsersShowness({tree, view, browserIds: [failedBrowserId], diff: diff.tree});
calcSuitesShowness({tree, suiteIds: [failedSuiteId], diff: 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});

calcBrowsersShowness({tree, view, browserIds: [failedBrowserId], diff: diff.tree});
calcSuitesShowness({tree, suiteIds: [failedSuiteId], diff: diff.tree});
}

console.log('applying diff');
console.log(diff);

return applyStateUpdate(state, diff);
}
Expand Down
4 changes: 1 addition & 3 deletions lib/static/new-ui/components/GuiniToolbarOverlay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ export function GuiniToolbarOverlay(): ReactNode {
};

const onCancelClick = (): void => {
for (const image of stagedImages) {
dispatch(staticAccepterUnstageScreenshot(image.id));
}
dispatch(staticAccepterUnstageScreenshot(stagedImages.map(image => image.id)));
};

const onModalCancelClick = (): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function ScreenshotsTreeViewItem(props: ScreenshotsTreeViewItemProps): Re
};
const onScreenshotUndo = (): void => {
if (isStaticImageAccepterEnabled) {
dispatch(staticAccepterUnstageScreenshot(props.image.id));
dispatch(staticAccepterUnstageScreenshot([props.image.id]));
} else {
dispatch(undoAcceptImage(props.image.id));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {ReactNode, useMemo} from 'react';
import {Icon} from '@gravity-ui/uikit';
import classNames from 'classnames';
import {
SquareCheck,
ChevronsExpandVertical,
Expand All @@ -8,42 +8,57 @@ import {
Square,
CircleInfo,
Play,
Check,
ArrowUturnCcwLeft
} from '@gravity-ui/icons';
import React, {ReactNode, useMemo} from 'react';
import {useDispatch, useSelector} from 'react-redux';

import styles from './index.module.css';
import {
acceptOpened,
collapseAll,
deselectAll,
expandAll,
retrySuite,
selectAll
selectAll, staticAccepterStageScreenshot, staticAccepterUnstageScreenshot,
undoAcceptImages
} from '@/static/modules/actions';
import {useDispatch, useSelector} from 'react-redux';
import {State} from '@/static/new-ui/types/store';
import {ImageEntity, State} from '@/static/new-ui/types/store';
import {CHECKED, INDETERMINATE} from '@/constants/checked-statuses';
import {IconButton} from '@/static/new-ui/components/IconButton';
import classNames from 'classnames';
import {
getCheckedTests,
getSelectedImages,
getVisibleBrowserIds,
getVisibleImages
} from '@/static/modules/selectors/tree';
import {getIsInitialized} from '@/static/new-ui/store/selectors';
import {getIsGui, getIsInitialized, getIsStaticImageAccepterEnabled} from '@/static/new-ui/store/selectors';
import {isAcceptable, isScreenRevertable} from '@/static/modules/utils';
import {EditScreensFeature, RunTestsFeature} from '@/constants';

interface TreeActionsToolbarProps {
onHighlightCurrentTest?: () => void;
}

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 selectedTests = useSelector(getCheckedTests);
const visibleBrowserIds: string[] = useSelector(getVisibleBrowserIds);
const isInitialized = useSelector(getIsInitialized);

const isRunTestsAvailable = useSelector((state: State) => state.app.availableFeatures)
.find(feature => feature.name === RunTestsFeature.name);
const isRunning = useSelector((state: State) => state.running);

const isEditScreensAvailable = useSelector((state: State) => state.app.availableFeatures)
.find(feature => feature.name === EditScreensFeature.name);

const isSelectedAll = useMemo(() => {
for (const suiteId of rootSuiteIds) {
if (suitesStateById[suiteId].checkStatus !== CHECKED) {
Expand All @@ -64,6 +79,16 @@ export function TreeActionsToolbar(props: TreeActionsToolbarProps): ReactNode {
return false;
}, [suitesStateById, rootSuiteIds]);

const isStaticImageAccepterEnabled = useSelector(getIsStaticImageAccepterEnabled);
const isGuiMode = useSelector(getIsGui);
const visibleImages: ImageEntity[] = useSelector(getVisibleImages);
const selectedImages: ImageEntity[] = useSelector(getSelectedImages);
const activeImages = isSelectedAtLeastOne ? selectedImages : visibleImages;

const isAtLeastOneAcceptable = activeImages.some(image => isAcceptable(image));
const isAtLeastOneRevertable = activeImages.some(image => isScreenRevertable({image, gui: isGuiMode, isLastResult: true, isStaticImageAccepterEnabled}));
const isUndoButtonVisible = isAtLeastOneRevertable && !isAtLeastOneAcceptable;

Check warning on line 90 in lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'isUndoButtonVisible' is assigned a value but never used

Check warning on line 90 in lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx

View workflow job for this annotation

GitHub Actions / build (20.x)

'isUndoButtonVisible' is assigned a value but never used

Check warning on line 90 in lib/static/new-ui/features/suites/components/TreeActionsToolbar/index.tsx

View workflow job for this annotation

GitHub Actions / build (22.6)

'isUndoButtonVisible' is assigned a value but never used

const selectedTestsCount = useMemo(() => {
let count = 0;

Expand Down Expand Up @@ -104,8 +129,39 @@ export function TreeActionsToolbar(props: TreeActionsToolbarProps): ReactNode {
}
};

const handleUndo = (): void => {
const acceptableImageIds = activeImages
.filter(image => isScreenRevertable({image, gui: isGuiMode, isLastResult: true, isStaticImageAccepterEnabled}))
.map(image => image.id);

if (isStaticImageAccepterEnabled && !isGuiMode) {
dispatch(staticAccepterUnstageScreenshot(acceptableImageIds));
} else {
dispatch(undoAcceptImages(acceptableImageIds));
}
};

const handleAccept = (): void => {
const acceptableImageIds = activeImages
.filter(image => isAcceptable(image))
.map(image => image.id);

if (isStaticImageAccepterEnabled && !isGuiMode) {
dispatch(staticAccepterStageScreenshot(acceptableImageIds));
} else {
dispatch(acceptOpened(acceptableImageIds));
}
};

const viewButtons = <>
<IconButton icon={<Icon data={Play} height={14} />} tooltip={isSelectedAtLeastOne ? 'Run selected' : 'Run visible'} view={'flat'} onClick={handleRun} disabled={isRunning || !isInitialized}></IconButton>
{isRunTestsAvailable && <IconButton icon={<Icon data={Play} height={14}/>}
tooltip={isSelectedAtLeastOne ? 'Run selected' : 'Run visible'} view={'flat'} onClick={handleRun}
disabled={isRunning || !isInitialized}></IconButton>}
{isEditScreensAvailable && <>
isUndoButtonVisible ?
<IconButton icon={<Icon data={ArrowUturnCcwLeft} />} tooltip={isSelectedAtLeastOne ? 'Undo accepting selected screenshots' : 'Undo accepting visible screenshots'} view={'flat'} onClick={handleUndo} disabled={isRunning || !isInitialized}></IconButton> :
<IconButton icon={<Icon data={Check} />} tooltip={isSelectedAtLeastOne ? 'Accept selected screenshots' : 'Accept visible screenshots'} view={'flat'} onClick={handleAccept} disabled={isRunning || !isInitialized}></IconButton>
</>}
<div className={styles.buttonsDivider}></div>
<IconButton icon={<Icon data={SquareDashed} height={14}/>} tooltip={'Focus on active test'} view={'flat'} onClick={props.onHighlightCurrentTest} disabled={!isInitialized}/>
<IconButton icon={<Icon data={ChevronsExpandVertical} height={14}/>} tooltip={'Expand all'} view={'flat'} onClick={handleExpandAll} disabled={!isInitialized}/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function VisualChecksPage(): ReactNode {
}

if (isStaticImageAccepterEnabled) {
dispatch(staticAccepterUnstageScreenshot(currentImage.id));
dispatch(staticAccepterUnstageScreenshot([currentImage.id]));
} else {
dispatch(undoAcceptImage(currentImage.id));
}
Expand Down

0 comments on commit 9bf621d

Please sign in to comment.