From 769e8ed36dd43e334c0210e783535771c7421b77 Mon Sep 17 00:00:00 2001 From: rocketraccoon Date: Mon, 25 May 2026 05:45:07 +0700 Subject: [PATCH 1/4] feat: sync filters between Suite and Visual Checks pages --- lib/static/modules/default-state.ts | 5 ----- lib/static/modules/reducers/filters.ts | 16 ++++------------ .../new-ui/components/BrowsersSelect/index.tsx | 4 ++-- .../new-ui/components/NameFilter/index.tsx | 4 ++-- .../suites/components/SuitesPage/index.tsx | 2 +- .../suites/components/TestTags/index.tsx | 3 ++- .../components/VisualChecksPage/index.tsx | 7 ++++--- .../components/VisualChecksPage/selectors.ts | 16 ++++++++++++---- lib/static/new-ui/types/store.ts | 6 ------ test/unit/lib/db-utils/common.js | 2 +- 10 files changed, 28 insertions(+), 37 deletions(-) diff --git a/lib/static/modules/default-state.ts b/lib/static/modules/default-state.ts index b32a07bea..4443552d0 100644 --- a/lib/static/modules/default-state.ts +++ b/lib/static/modules/default-state.ts @@ -116,11 +116,6 @@ export default Object.assign({config: configDefaults}, { currentBrowserId: null, stateName: null, - viewMode: ViewMode.ALL, - nameFilter: '', - useRegexFilter: false, - useMatchCaseFilter: false, - filteredBrowsers: [], diffMode: DiffModes.TWO_UP_INTERACTIVE.id }, loading: { diff --git a/lib/static/modules/reducers/filters.ts b/lib/static/modules/reducers/filters.ts index fc930715f..c84c219b6 100644 --- a/lib/static/modules/reducers/filters.ts +++ b/lib/static/modules/reducers/filters.ts @@ -1,5 +1,5 @@ import {State} from '@/static/new-ui/types/store'; -import {Page, PathNames, VISUAL_CHECKS_PAGE_DIFF_MODE_KEY} from '@/constants'; +import {Page, VISUAL_CHECKS_PAGE_DIFF_MODE_KEY} from '@/constants'; import actionNames from '@/static/modules/action-names'; import {FiltersAction, InitGuiReportAction, InitStaticReportAction} from '@/static/modules/actions'; import {DiffModeId, DiffModes, ViewMode} from '@/constants'; @@ -27,7 +27,6 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init case actionNames.INIT_GUI_REPORT: case actionNames.INIT_STATIC_REPORT: { const suitesPageViewMode = localStorageWrapper.getItem('app.suitesPage.viewMode', ViewMode.ALL) as ViewMode; - const visualChecksPageViewMode = localStorageWrapper.getItem('app.visualChecksPage.viewMode', ViewMode.ALL) as ViewMode; const visualChecksPageDiffMode = localStorageWrapper.getItem(VISUAL_CHECKS_PAGE_DIFF_MODE_KEY, DiffModes.TWO_UP_INTERACTIVE.id) as DiffModeId; const viewQuery = getViewQuery(window.location.search); @@ -46,22 +45,15 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init viewMode: suitesPageViewMode }, [Page.visualChecksPage]: { - viewMode: visualChecksPageViewMode, diffMode: visualChecksPageDiffMode } } } ); - if (window.location.hash?.startsWith(`#${PathNames.visualChecks}`)) { - newState.app[Page.visualChecksPage].filteredBrowsers = viewQuery.filteredBrowsers as BrowserItem[]; - newState.app[Page.visualChecksPage].viewMode = viewQuery.viewMode as ViewMode || visualChecksPageViewMode; - newState.app[Page.visualChecksPage].nameFilter = viewQuery.testNameFilter as string || ''; - } else { // Need for backward compatibility with old ui where are suites page only - newState.app[Page.suitesPage].filteredBrowsers = viewQuery.filteredBrowsers as BrowserItem[]; - newState.app[Page.suitesPage].viewMode = viewQuery.viewMode as ViewMode || suitesPageViewMode; - newState.app[Page.suitesPage].nameFilter = viewQuery.testNameFilter as string || ''; - } + newState.app[Page.suitesPage].filteredBrowsers = viewQuery.filteredBrowsers as BrowserItem[]; + newState.app[Page.suitesPage].viewMode = viewQuery.viewMode as ViewMode || suitesPageViewMode; + newState.app[Page.suitesPage].nameFilter = viewQuery.testNameFilter as string || ''; return newState; } diff --git a/lib/static/new-ui/components/BrowsersSelect/index.tsx b/lib/static/new-ui/components/BrowsersSelect/index.tsx index cd62d4ea5..aafc0b2ac 100644 --- a/lib/static/new-ui/components/BrowsersSelect/index.tsx +++ b/lib/static/new-ui/components/BrowsersSelect/index.tsx @@ -9,7 +9,7 @@ import {getIsInitialized} from '@/static/new-ui/store/selectors'; import {BrowserItem} from '@/types'; import styles from './index.module.css'; import {IconButton} from '../IconButton'; -import {usePage} from '@/static/new-ui/hooks/usePage'; +import {Page} from '@/constants'; // In the onUpdate callback we only have access to array of selected strings. That's why we need to serialize // id/version in string. Encoding to avoid errors if id/version contains delimiter. @@ -37,7 +37,7 @@ const IconsPreloader = (): React.JSX.Element => { export function BrowsersSelect(): ReactNode { const [selectedBrowsers, setSelectedBrowsers] = useState([]); - const page = usePage(); + const page = Page.suitesPage; const browsers = useSelector((state) => state.browsers); const filteredBrowsers = useSelector((state) => state.app[page].filteredBrowsers); const dispatch = useDispatch(); diff --git a/lib/static/new-ui/components/NameFilter/index.tsx b/lib/static/new-ui/components/NameFilter/index.tsx index e5bc8fe15..4ef9f35d5 100644 --- a/lib/static/new-ui/components/NameFilter/index.tsx +++ b/lib/static/new-ui/components/NameFilter/index.tsx @@ -8,9 +8,9 @@ import * as actions from '@/static/modules/actions'; import {getIsInitialized} from '@/static/new-ui/store/selectors'; import {NameFilterButton} from './NameFilterButton'; import styles from './index.module.css'; -import {usePage} from '@/static/new-ui/hooks/usePage'; import {useHotkey} from '@/static/new-ui/hooks/useHotkey'; import {search} from '@/static/modules/search'; +import {Page} from '@/constants'; export interface NameFilterHandle { focus: () => void; @@ -23,7 +23,7 @@ export interface NameFilterProps { export const NameFilter = forwardRef(function NameFilter(props, ref): ReactNode { const dispatch = useDispatch(); - const page = usePage(); + const page = Page.suitesPage; const nameFilter = useSelector((state) => state.app[page].nameFilter); const useRegexFilter = useSelector((state) => state.app[page].useRegexFilter); const useMatchCaseFilter = useSelector((state) => state.app[page].useMatchCaseFilter); 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 5b8e582eb..7de4f4e10 100644 --- a/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx +++ b/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx @@ -66,7 +66,7 @@ export function SuitesPage(): ReactNode { const dispatch = useDispatch(); const navigate = useNavigate(); - const statusValue = useSelector((state) => state.app[page].viewMode); + const statusValue = useSelector((state) => state.app[Page.suitesPage].viewMode); const statusCounts = useSelector((state) => getSuitesStatusCounts(state)); const onStatusChange = useCallback((value: string) => { dispatch(actions.changeViewMode({ diff --git a/lib/static/new-ui/features/suites/components/TestTags/index.tsx b/lib/static/new-ui/features/suites/components/TestTags/index.tsx index f57be877a..5c730effa 100644 --- a/lib/static/new-ui/features/suites/components/TestTags/index.tsx +++ b/lib/static/new-ui/features/suites/components/TestTags/index.tsx @@ -6,6 +6,7 @@ import {useDispatch, useSelector} from 'react-redux'; import {getCurrentResult} from '@/static/new-ui/features/suites/selectors'; import {updateNameFilter} from '@/static/modules/actions'; import {AttachmentType, TagsAttachment} from '@/types'; +import {Page} from '@/constants'; import {Badge, badgeStyles} from '@/static/new-ui/components/Badge'; import {usePage} from '@/static/new-ui/hooks/usePage'; @@ -14,7 +15,7 @@ export const TestTags = (): ReactNode => { const suite = useSelector(getCurrentResult); const dispatch = useDispatch(); const page = usePage(); - const nameFilter = useSelector((state) => state.app[page].nameFilter); + const nameFilter = useSelector((state) => state.app[Page.suitesPage].nameFilter); if (!suite) { return 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 302203b0e..1375bff82 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 @@ -6,9 +6,10 @@ import {SplitViewLayout} from '@/static/new-ui/components/SplitViewLayout'; import {UiCard} from '@/static/new-ui/components/Card/UiCard'; import { getAttempt, - getLastAttempt, + getCurrentBrowser, + getCurrentImage, getCurrentNamedImage, - getCurrentBrowser, getCurrentImage + getLastAttempt } from '@/static/new-ui/features/visual-checks/selectors'; import {AssertViewResult} from '@/static/new-ui/components/AssertViewResult'; import styles from './index.module.css'; @@ -216,7 +217,7 @@ export function VisualChecksPage(): ReactNode { const onStatusChange = useCallback((value: string) => { dispatch(actions.changeViewMode({ data: value as ViewMode, - page + page: Page.suitesPage })); }, [page]); diff --git a/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts b/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts index 49aa40451..99486df34 100644 --- a/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts +++ b/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts @@ -8,7 +8,15 @@ import {Page, TestStatus, ViewMode} from '@/constants'; import {matchTestName} from '@/static/modules/utils'; import {checkSearchResultExits} from '@/static/modules/search'; -export const getVisualChecksViewMode = (state: State): ViewMode => state.app[Page.visualChecksPage].viewMode; +export const getVisualChecksViewMode = (state: State): ViewMode => { + const realStatus = state.app[Page.suitesPage].viewMode; + + if ([ViewMode.ALL, ViewMode.FAILED, ViewMode.PASSED].includes(realStatus)) { + return realStatus; + } + + return ViewMode.ALL; +}; type Stats = Pick, ViewMode.ALL | ViewMode.PASSED | ViewMode.FAILED>; @@ -28,9 +36,9 @@ export const getVisualTreeViewData = createSelector( getImages, getNamedImages, getVisualChecksViewMode, - (state: State): string => state.app[Page.visualChecksPage].nameFilter, - (state: State): boolean => state.app[Page.visualChecksPage].useRegexFilter, - (state: State): boolean => state.app[Page.visualChecksPage].useMatchCaseFilter + (state: State): string => state.app[Page.suitesPage].nameFilter, + (state: State): boolean => state.app[Page.suitesPage].useRegexFilter, + (state: State): boolean => state.app[Page.suitesPage].useMatchCaseFilter ], ( images, diff --git a/lib/static/new-ui/types/store.ts b/lib/static/new-ui/types/store.ts index 738978045..2fca8cf28 100644 --- a/lib/static/new-ui/types/store.ts +++ b/lib/static/new-ui/types/store.ts @@ -279,12 +279,6 @@ export interface State { currentBrowserId: string | null; stateName: string | null; - // Filters in top of sidebar - nameFilter: string; - useRegexFilter: boolean; - useMatchCaseFilter: boolean; - viewMode: ViewMode; - filteredBrowsers: BrowserItem[]; diffMode: DiffModeId; }; loading: { diff --git a/test/unit/lib/db-utils/common.js b/test/unit/lib/db-utils/common.js index 414cdc302..9aa608fa3 100644 --- a/test/unit/lib/db-utils/common.js +++ b/test/unit/lib/db-utils/common.js @@ -1,6 +1,6 @@ 'use strict'; -const {handleDatabases, makeDbFileDownloadErrorMessage} = require('lib/db-utils/common'); +const {handleDatabases} = require('lib/db-utils/common'); describe('lib/db-utils/common', () => { const sandbox = sinon.sandbox.create(); From 4da368dc3a92df078bd9089a94b0f96c2ec51eff Mon Sep 17 00:00:00 2001 From: rocketraccoon Date: Mon, 25 May 2026 06:14:03 +0700 Subject: [PATCH 2/4] feat: state refactoring --- .../components/controls/common-controls.jsx | 2 +- .../components/controls/common-filters.jsx | 2 +- .../controls/test-name-filter-input.jsx | 2 +- lib/static/components/suites.jsx | 6 +- lib/static/modules/actions/filters.ts | 3 +- lib/static/modules/default-state.ts | 15 +- .../modules/middlewares/local-storage.js | 3 +- lib/static/modules/reducers/filters.ts | 21 +-- lib/static/modules/reducers/sort-tests.ts | 35 ++-- lib/static/modules/reducers/tree/helpers.js | 2 +- lib/static/modules/reducers/tree/index.js | 8 +- .../modules/reducers/tree/nodes/browsers.js | 2 +- lib/static/modules/search/index.ts | 8 +- lib/static/modules/selectors/tree.js | 2 +- lib/static/modules/selectors/view.js | 2 +- .../components/BrowsersSelect/index.tsx | 5 +- .../new-ui/components/NameFilter/index.tsx | 25 ++- .../suites/components/SuitesPage/index.tsx | 10 +- .../suites/components/TestTags/index.tsx | 8 +- .../components/VisualChecksPage/index.tsx | 3 +- .../components/VisualChecksPage/selectors.ts | 12 +- .../new-ui/hooks/useLegacyUrlMigration.tsx | 14 +- lib/static/new-ui/types/store.ts | 15 +- .../modules/middlewares/local-storage.js | 8 +- .../lib/static/modules/reducers/filters.js | 28 +-- .../lib/static/modules/reducers/tree/index.js | 168 +++++++----------- .../lib/static/modules/selectors/stats.js | 16 +- 27 files changed, 178 insertions(+), 247 deletions(-) diff --git a/lib/static/components/controls/common-controls.jsx b/lib/static/components/controls/common-controls.jsx index a3773ab1c..58882aea2 100644 --- a/lib/static/components/controls/common-controls.jsx +++ b/lib/static/components/controls/common-controls.jsx @@ -53,7 +53,7 @@ class ControlButtons extends Component { size='s' label="Show tests" value={app.suitesPage.viewMode} - handler={(data) => actions.changeViewMode({data, page: 'suitesPage'})} + handler={(data) => actions.changeViewMode({data})} options = {this._getShowTestsOptions()} /> ({ - filteredBrowsers: app.suitesPage.filteredBrowsers, + filteredBrowsers: app.filteredBrowsers, browsers, gui, staticImageAccepter diff --git a/lib/static/components/controls/test-name-filter-input.jsx b/lib/static/components/controls/test-name-filter-input.jsx index baf6bc5e5..fd4116ec1 100644 --- a/lib/static/components/controls/test-name-filter-input.jsx +++ b/lib/static/components/controls/test-name-filter-input.jsx @@ -43,6 +43,6 @@ TestNameFilterInput.propTypes = { }; export default connect( - (state) => ({testNameFilter: state.app.suitesPage.nameFilter}), + (state) => ({testNameFilter: state.app.nameFilter}), (dispatch) => ({actions: bindActionCreators(actions, dispatch)}) )(TestNameFilterInput); diff --git a/lib/static/components/suites.jsx b/lib/static/components/suites.jsx index 8cd17d128..e46587220 100644 --- a/lib/static/components/suites.jsx +++ b/lib/static/components/suites.jsx @@ -173,9 +173,9 @@ Suites.propTypes = { export default connect( (state) => ({ visibleRootSuiteIds: getVisibleRootSuiteIds(state), - viewMode: state.app.suitesPage.viewMode, - filteredBrowsers: state.app.suitesPage.filteredBrowsers, - testNameFilter: state.app.suitesPage.nameFilter, + viewMode: state.app.viewMode, + filteredBrowsers: state.app.filteredBrowsers, + testNameFilter: state.app.nameFilter, strictMatchFilter: state.view.strictMatchFilter, isReportEmpty: isEmpty(state.tree.browsers.byId) }), diff --git a/lib/static/modules/actions/filters.ts b/lib/static/modules/actions/filters.ts index 2c57d9aeb..3f140a82e 100644 --- a/lib/static/modules/actions/filters.ts +++ b/lib/static/modules/actions/filters.ts @@ -2,10 +2,9 @@ import actionNames from '@/static/modules/action-names'; import type {Action} from '@/static/modules/actions/types'; import {setFilteredBrowsers} from '@/static/modules/query-params'; import {BrowserItem} from '@/types'; -import {Page, ViewMode} from '@/constants'; +import {ViewMode} from '@/constants'; interface FilterPayload{ - page: Page; data: T; } diff --git a/lib/static/modules/default-state.ts b/lib/static/modules/default-state.ts index 4443552d0..42442579f 100644 --- a/lib/static/modules/default-state.ts +++ b/lib/static/modules/default-state.ts @@ -99,18 +99,19 @@ export default Object.assign({config: configDefaults}, { isNewUi: false, isInitialized: false, availableFeatures: [], + + viewMode: ViewMode.ALL, + nameFilter: '', + useRegexFilter: false, + useMatchCaseFilter: false, + filteredBrowsers: [], + [Page.suitesPage]: { currentBrowserId: null, currentTreeNodeId: null, currentGroupId: null, currentStepId: null, - currentHighlightedStepId: null, - - viewMode: ViewMode.ALL, - nameFilter: '', - useRegexFilter: false, - useMatchCaseFilter: false, - filteredBrowsers: [] + currentHighlightedStepId: null }, [Page.visualChecksPage]: { currentBrowserId: null, diff --git a/lib/static/modules/middlewares/local-storage.js b/lib/static/modules/middlewares/local-storage.js index 1a179f5cb..4f11e07dd 100644 --- a/lib/static/modules/middlewares/local-storage.js +++ b/lib/static/modules/middlewares/local-storage.js @@ -21,8 +21,7 @@ export default store => next => action => { if (action.type === actionNames.VISUAL_CHECKS_SET_DIFF_MODE) { localStorageWrapper.setItem(VISUAL_CHECKS_PAGE_DIFF_MODE_KEY, action.payload.diffModeId); } - localStorageWrapper.setItem('app.suitesPage.viewMode', app.suitesPage.viewMode); - localStorageWrapper.setItem('app.visualChecksPage.viewMode', app.visualChecksPage.viewMode); + localStorageWrapper.setItem('app.viewMode', app.viewMode); } return result; diff --git a/lib/static/modules/reducers/filters.ts b/lib/static/modules/reducers/filters.ts index c84c219b6..342529f04 100644 --- a/lib/static/modules/reducers/filters.ts +++ b/lib/static/modules/reducers/filters.ts @@ -18,15 +18,15 @@ interface FilterData { filteredBrowsers?: BrowserItem[]; } -const updateAppState = (state: State, page: Page, data: FilterData): State => ( - applyStateUpdate(state, {app: {[page]: data}}) +const updateAppState = (state: State, data: FilterData): State => ( + applyStateUpdate(state, {app: data}) ); export default (state: State, action: FiltersAction | InitGuiReportAction | InitStaticReportAction): State => { switch (action.type) { case actionNames.INIT_GUI_REPORT: case actionNames.INIT_STATIC_REPORT: { - const suitesPageViewMode = localStorageWrapper.getItem('app.suitesPage.viewMode', ViewMode.ALL) as ViewMode; + const suitesPageViewMode = localStorageWrapper.getItem('app.viewMode', ViewMode.ALL) as ViewMode; const visualChecksPageDiffMode = localStorageWrapper.getItem(VISUAL_CHECKS_PAGE_DIFF_MODE_KEY, DiffModes.TWO_UP_INTERACTIVE.id) as DiffModeId; const viewQuery = getViewQuery(window.location.search); @@ -41,9 +41,7 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init state, { app: { - [Page.suitesPage]: { - viewMode: suitesPageViewMode - }, + viewMode: suitesPageViewMode, [Page.visualChecksPage]: { diffMode: visualChecksPageDiffMode } @@ -51,16 +49,15 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init } ); - newState.app[Page.suitesPage].filteredBrowsers = viewQuery.filteredBrowsers as BrowserItem[]; - newState.app[Page.suitesPage].viewMode = viewQuery.viewMode as ViewMode || suitesPageViewMode; - newState.app[Page.suitesPage].nameFilter = viewQuery.testNameFilter as string || ''; + newState.app.filteredBrowsers = viewQuery.filteredBrowsers as BrowserItem[]; + newState.app.viewMode = viewQuery.viewMode as ViewMode || suitesPageViewMode; + newState.app.nameFilter = viewQuery.testNameFilter as string || ''; return newState; } case actionNames.CHANGE_VIEW_MODE: return updateAppState( state, - action.payload.page, { viewMode: action.payload.data } @@ -69,7 +66,6 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init case actionNames.VIEW_UPDATE_FILTER_BY_NAME: return updateAppState( state, - action.payload.page, { nameFilter: action.payload.data } @@ -78,7 +74,6 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init case actionNames.VIEW_SET_FILTER_MATCH_CASE: { return updateAppState( state, - action.payload.page, { useMatchCaseFilter: action.payload.data } @@ -88,7 +83,6 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init case actionNames.VIEW_SET_FILTER_USE_REGEX: return updateAppState( state, - action.payload.page, { useRegexFilter: action.payload.data } @@ -97,7 +91,6 @@ export default (state: State, action: FiltersAction | InitGuiReportAction | Init case actionNames.BROWSERS_SELECTED: return updateAppState( state, - action.payload.page, { filteredBrowsers: action.payload.data } diff --git a/lib/static/modules/reducers/sort-tests.ts b/lib/static/modules/reducers/sort-tests.ts index a6eb97ae2..d5277a642 100644 --- a/lib/static/modules/reducers/sort-tests.ts +++ b/lib/static/modules/reducers/sort-tests.ts @@ -1,5 +1,4 @@ import {SortDirection, State} from '@/static/new-ui/types/store'; -import {Page} from '@/constants'; import {SomeAction} from '@/static/modules/actions/types'; import actionNames from '@/static/modules/action-names'; import {applyStateUpdate} from '@/static/modules/utils'; @@ -94,27 +93,25 @@ export default (state: State, action: SomeAction): State => { }); } case actionNames.VIEW_UPDATE_FILTER_BY_NAME: { - if (action.payload.page === Page.suitesPage) { - const sortTestsData: Partial = {}; - const availableExpressions = [...state.app.sortTestsData.availableExpressions]; - const previousExpressionIds = state.app.sortTestsData.previousExpressionIds; - const previousDirection = state.app.sortTestsData.previousDirection; - - if (action.payload.data.length > 0 && !availableExpressions.some(expr => expr.id === SORT_BY_RELEVANCE.id)) { - sortTestsData.availableExpressions = [...availableExpressions, SORT_BY_RELEVANCE]; - sortTestsData.currentExpressionIds = [SORT_BY_RELEVANCE.id]; - sortTestsData.currentDirection = SortDirection.Desc; - } else if (action.payload.data.length === 0 && availableExpressions.some(expr => expr.id === SORT_BY_RELEVANCE.id)) { - sortTestsData.availableExpressions = availableExpressions.filter(expr => expr.id !== SORT_BY_RELEVANCE.id); - sortTestsData.currentExpressionIds = previousExpressionIds; - sortTestsData.currentDirection = previousDirection; - } + const sortTestsData: Partial = {}; + const availableExpressions = [...state.app.sortTestsData.availableExpressions]; + const previousExpressionIds = state.app.sortTestsData.previousExpressionIds; + const previousDirection = state.app.sortTestsData.previousDirection; - return applyStateUpdate(state, { - app: {sortTestsData} - }); + if (action.payload.data.length > 0 && !availableExpressions.some(expr => expr.id === SORT_BY_RELEVANCE.id)) { + sortTestsData.availableExpressions = [...availableExpressions, SORT_BY_RELEVANCE]; + sortTestsData.currentExpressionIds = [SORT_BY_RELEVANCE.id]; + sortTestsData.currentDirection = SortDirection.Desc; + } else if (action.payload.data.length === 0 && availableExpressions.some(expr => expr.id === SORT_BY_RELEVANCE.id)) { + sortTestsData.availableExpressions = availableExpressions.filter(expr => expr.id !== SORT_BY_RELEVANCE.id); + sortTestsData.currentExpressionIds = previousExpressionIds; + sortTestsData.currentDirection = previousDirection; } + return applyStateUpdate(state, { + app: {sortTestsData} + }); + return state; } default: diff --git a/lib/static/modules/reducers/tree/helpers.js b/lib/static/modules/reducers/tree/helpers.js index 10d2ad49b..1cadbc6c0 100644 --- a/lib/static/modules/reducers/tree/helpers.js +++ b/lib/static/modules/reducers/tree/helpers.js @@ -121,7 +121,7 @@ export function updateImagesStatus({tree, view, app, images, imageIdsArray, newS const browserIdsUniq = uniq(browserIds); const suiteIdsUniq = uniq(browserIdsUniq.map(browserId => tree.browsers.byId[browserId].parentId)); - updateParentSuitesStatus(tree, suiteIdsUniq, app.suitesPage.filteredBrowsers, diff); + updateParentSuitesStatus(tree, suiteIdsUniq, app.filteredBrowsers, diff); !isEmpty(suiteIdsUniq) && calcSuitesOpenness({tree, expand: view.expand, suiteIds: suiteIdsUniq, diff}); !isEmpty(browserIdsUniq) && calcBrowsersOpenness({tree, expand: view.expand, browserIds: browserIdsUniq, diff}); diff --git a/lib/static/modules/reducers/tree/index.js b/lib/static/modules/reducers/tree/index.js index f9bf55885..3326507ce 100644 --- a/lib/static/modules/reducers/tree/index.js +++ b/lib/static/modules/reducers/tree/index.js @@ -30,7 +30,7 @@ export default ((state, action) => { case actionNames.INIT_GUI_REPORT: case actionNames.INIT_STATIC_REPORT: { const {tree} = action.payload; - const {filteredBrowsers} = state.app.suitesPage; + const {filteredBrowsers} = state.app; tree.suites.failedRootIds = getFailedRootSuiteIds(tree.suites); @@ -125,9 +125,9 @@ export default ((state, action) => { case actionNames.BROWSERS_SELECTED: { const {tree, view, app} = state; - const filteredBrowsers = isEmpty(app.suitesPage.filteredBrowsers) + const filteredBrowsers = isEmpty(app.filteredBrowsers) ? state.browsers.map(({id}) => ({id, versions: []})) - : app.suitesPage.filteredBrowsers; + : app.filteredBrowsers; updateAllSuitesStatus(tree, filteredBrowsers, diff.tree); calcBrowsersShowness({tree, view, app, diff: diff.tree}); @@ -472,7 +472,7 @@ function addNodesToTree(state, payload) { calcBrowsersOpenness({tree, expand: view.expand, browserIds: [browserId]}); calcImagesOpenness({tree, expand: view.expand, imageIds}); - if (app.suitesPage.viewMode === ViewMode.FAILED) { + if (app.viewMode === ViewMode.FAILED) { calcBrowsersShowness({tree, view, app, browserIds: [browserId]}); calcSuitesShowness({tree, suiteIds: [youngestSuiteId]}); } diff --git a/lib/static/modules/reducers/tree/nodes/browsers.js b/lib/static/modules/reducers/tree/nodes/browsers.js index 238df6024..64a23258b 100644 --- a/lib/static/modules/reducers/tree/nodes/browsers.js +++ b/lib/static/modules/reducers/tree/nodes/browsers.js @@ -54,7 +54,7 @@ export function calcBrowsersShowness({tree, view, app = {}, browserIds = [], dif ensureDiffProperty(diff, ['browsers', 'byId']); const {strictMatchFilter} = view; - const {viewMode, filteredBrowsers, nameFilter, useMatchCaseFilter, useRegexFilter} = app.suitesPage ? app.suitesPage : {}; + const {viewMode, filteredBrowsers, nameFilter, useMatchCaseFilter, useRegexFilter} = app ? app : {}; const {isNewUi = false} = app; if (empty) { diff --git a/lib/static/modules/search/index.ts b/lib/static/modules/search/index.ts index 84775e37c..f614718c1 100644 --- a/lib/static/modules/search/index.ts +++ b/lib/static/modules/search/index.ts @@ -1,5 +1,4 @@ import {setMatchCaseFilter, setSearchLoading, updateNameFilter} from '@/static/modules/actions'; -import {Page} from '@/constants'; import {Tree} from '@/tests-tree-builder/base'; import {AttachmentType, TagsAttachment} from '@/types'; @@ -38,7 +37,6 @@ export const search = ( text: string, matchCase = false, useRegexFilter = false, - page: Page, updateMatchCase: boolean, dispatch: (action: unknown) => void ): void => { @@ -75,14 +73,12 @@ export const search = ( if (updateMatchCase) { dispatch(setMatchCaseFilter({ - data: matchCase, - page + data: matchCase })); } else { dispatch( updateNameFilter({ - data: text, - page + data: text }) ); } diff --git a/lib/static/modules/selectors/tree.js b/lib/static/modules/selectors/tree.js index a20a0c6ec..3b53d4b26 100644 --- a/lib/static/modules/selectors/tree.js +++ b/lib/static/modules/selectors/tree.js @@ -19,7 +19,7 @@ const getBrowsersStates = (state) => state.tree.browsers.stateById; const getImagesStates = (state) => state.tree.images.stateById; const getFailedRootSuiteIds = (state) => state.tree.suites.failedRootIds; const getRootSuiteIds = (state) => { - const viewMode = state.app.suitesPage.viewMode; + const viewMode = state.app.viewMode; return viewMode === ViewMode.FAILED ? state.tree.suites.failedRootIds : state.tree.suites.allRootIds; }; diff --git a/lib/static/modules/selectors/view.js b/lib/static/modules/selectors/view.js index f33efe345..ca456f3b4 100644 --- a/lib/static/modules/selectors/view.js +++ b/lib/static/modules/selectors/view.js @@ -1,2 +1,2 @@ -export const getFilteredBrowsers = (state) => state.app.suitesPage.filteredBrowsers; +export const getFilteredBrowsers = (state) => state.app.filteredBrowsers; export const getKeyToGroupTestsBy = (state) => state.view.keyToGroupTestsBy; diff --git a/lib/static/new-ui/components/BrowsersSelect/index.tsx b/lib/static/new-ui/components/BrowsersSelect/index.tsx index aafc0b2ac..4eeaa1561 100644 --- a/lib/static/new-ui/components/BrowsersSelect/index.tsx +++ b/lib/static/new-ui/components/BrowsersSelect/index.tsx @@ -39,7 +39,7 @@ export function BrowsersSelect(): ReactNode { const [selectedBrowsers, setSelectedBrowsers] = useState([]); const page = Page.suitesPage; const browsers = useSelector((state) => state.browsers); - const filteredBrowsers = useSelector((state) => state.app[page].filteredBrowsers); + const filteredBrowsers = useSelector((state) => state.app.filteredBrowsers); const dispatch = useDispatch(); useEffect(() => { @@ -130,8 +130,7 @@ export function BrowsersSelect(): ReactNode { const onClose = (): void => { dispatch( selectBrowsers({ - data: selectedBrowsers.filter(browser => browser.versions.length > 0, page), - page + data: selectedBrowsers.filter(browser => browser.versions.length > 0, page) }) ); }; diff --git a/lib/static/new-ui/components/NameFilter/index.tsx b/lib/static/new-ui/components/NameFilter/index.tsx index 4ef9f35d5..1eba5676e 100644 --- a/lib/static/new-ui/components/NameFilter/index.tsx +++ b/lib/static/new-ui/components/NameFilter/index.tsx @@ -10,7 +10,6 @@ import {NameFilterButton} from './NameFilterButton'; import styles from './index.module.css'; import {useHotkey} from '@/static/new-ui/hooks/useHotkey'; import {search} from '@/static/modules/search'; -import {Page} from '@/constants'; export interface NameFilterHandle { focus: () => void; @@ -23,10 +22,9 @@ export interface NameFilterProps { export const NameFilter = forwardRef(function NameFilter(props, ref): ReactNode { const dispatch = useDispatch(); - const page = Page.suitesPage; - const nameFilter = useSelector((state) => state.app[page].nameFilter); - const useRegexFilter = useSelector((state) => state.app[page].useRegexFilter); - const useMatchCaseFilter = useSelector((state) => state.app[page].useMatchCaseFilter); + const nameFilter = useSelector((state) => state.app.nameFilter); + const useRegexFilter = useSelector((state) => state.app.useRegexFilter); + const useMatchCaseFilter = useSelector((state) => state.app.useMatchCaseFilter); const [testNameFilter, setNameFilter] = useState(nameFilter); const [isFocused, setIsFocused] = useState(false); const [isAllSelected, setIsAllSelected] = useState(false); @@ -80,11 +78,11 @@ export const NameFilter = forwardRef(function const updateNameFilter = useCallback(debounce( (text) => { - search(text, useMatchCaseFilter, useRegexFilter, page, false, dispatch); + search(text, useMatchCaseFilter, useRegexFilter, false, dispatch); }, 500, {maxWait: 3000} - ), [useMatchCaseFilter, useRegexFilter, page]); + ), [useMatchCaseFilter, useRegexFilter]); const onChange = useCallback((event: ChangeEvent): void => { setNameFilter(event.target.value); @@ -93,27 +91,26 @@ export const NameFilter = forwardRef(function const onClear = useCallback((): void => { setNameFilter(''); - search('', useMatchCaseFilter, useRegexFilter, page, false, dispatch); - }, [setNameFilter, useMatchCaseFilter, useRegexFilter, page]); + search('', useMatchCaseFilter, useRegexFilter, false, dispatch); + }, [setNameFilter, useMatchCaseFilter, useRegexFilter]); const isInitialized = useSelector(getIsInitialized); const onCaseSensitiveClick = (): void => { - search(nameFilter, !useMatchCaseFilter, useRegexFilter, page, true, dispatch); + search(nameFilter, !useMatchCaseFilter, useRegexFilter, true, dispatch); }; const onRegexClick = (): void => { dispatch( actions.setUseRegexFilter({ - data: !useRegexFilter, - page + data: !useRegexFilter }) ); - search(nameFilter, useMatchCaseFilter, !useRegexFilter, page, true, dispatch); + search(nameFilter, useMatchCaseFilter, !useRegexFilter, true, dispatch); }; useEffect(() => { - search(nameFilter, useMatchCaseFilter, useRegexFilter, page, true, dispatch); + search(nameFilter, useMatchCaseFilter, useRegexFilter, true, dispatch); setNameFilter(nameFilter); }, [nameFilter]); 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 7de4f4e10..ef0376d79 100644 --- a/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx +++ b/lib/static/new-ui/features/suites/components/SuitesPage/index.tsx @@ -36,15 +36,12 @@ import {SideBar} from '@/static/new-ui/components/SideBar'; import {getCurrentSuiteHash, getSuitesStatusCounts, getSuitesTreeViewData} from './selectors'; import {getIconByStatus} from '@/static/new-ui/utils'; import {Page} from '@/constants'; -import {usePage} from '@/static/new-ui/hooks/usePage'; import {useHotkey} from '@/static/new-ui/hooks/useHotkey'; import {useLegacyUrlMigration} from '@/static/new-ui/hooks/useLegacyUrlMigration'; import {changeTestRetry, setCurrentTreeNode, setStrictMatchFilter} from '@/static/modules/actions'; import {getUrl} from '@/static/new-ui/utils/getUrl'; export function SuitesPage(): ReactNode { - const page = usePage(); - useLegacyUrlMigration(); const currentResult = useSelector(getCurrentResult); @@ -66,14 +63,13 @@ export function SuitesPage(): ReactNode { const dispatch = useDispatch(); const navigate = useNavigate(); - const statusValue = useSelector((state) => state.app[Page.suitesPage].viewMode); + const statusValue = useSelector((state) => state.app.viewMode); const statusCounts = useSelector((state) => getSuitesStatusCounts(state)); const onStatusChange = useCallback((value: string) => { dispatch(actions.changeViewMode({ - data: value as ViewMode, - page + data: value as ViewMode })); - }, [page]); + }, []); useEffect(() => { const stateName = diff --git a/lib/static/new-ui/features/suites/components/TestTags/index.tsx b/lib/static/new-ui/features/suites/components/TestTags/index.tsx index 5c730effa..e82bed18b 100644 --- a/lib/static/new-ui/features/suites/components/TestTags/index.tsx +++ b/lib/static/new-ui/features/suites/components/TestTags/index.tsx @@ -6,16 +6,13 @@ import {useDispatch, useSelector} from 'react-redux'; import {getCurrentResult} from '@/static/new-ui/features/suites/selectors'; import {updateNameFilter} from '@/static/modules/actions'; import {AttachmentType, TagsAttachment} from '@/types'; -import {Page} from '@/constants'; import {Badge, badgeStyles} from '@/static/new-ui/components/Badge'; -import {usePage} from '@/static/new-ui/hooks/usePage'; export const TestTags = (): ReactNode => { const suite = useSelector(getCurrentResult); const dispatch = useDispatch(); - const page = usePage(); - const nameFilter = useSelector((state) => state.app[Page.suitesPage].nameFilter); + const nameFilter = useSelector((state) => state.app.nameFilter); if (!suite) { return null; @@ -29,8 +26,7 @@ export const TestTags = (): ReactNode => { if (!nameFilter.includes(tagStr)) { dispatch( updateNameFilter({ - data: nameFilter.length ? `${nameFilter} ${tagStr}` : tagStr, - page + data: nameFilter.length ? `${nameFilter} ${tagStr}` : tagStr }) ); } 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 1375bff82..ad49dc68a 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 @@ -216,8 +216,7 @@ export function VisualChecksPage(): ReactNode { const onStatusChange = useCallback((value: string) => { dispatch(actions.changeViewMode({ - data: value as ViewMode, - page: Page.suitesPage + data: value as ViewMode })); }, [page]); diff --git a/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts b/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts index 99486df34..ed3477023 100644 --- a/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts +++ b/lib/static/new-ui/features/visual-checks/components/VisualChecksPage/selectors.ts @@ -4,12 +4,12 @@ import {EntityType, TreeRoot} from '@/static/new-ui/features/suites/components/S import {ImageEntity, State} from '@/static/new-ui/types/store'; import {getNamedImages} from '@/static/new-ui/features/visual-checks/selectors'; import {TreeViewData} from '@/static/new-ui/components/TreeView'; -import {Page, TestStatus, ViewMode} from '@/constants'; +import {TestStatus, ViewMode} from '@/constants'; import {matchTestName} from '@/static/modules/utils'; import {checkSearchResultExits} from '@/static/modules/search'; export const getVisualChecksViewMode = (state: State): ViewMode => { - const realStatus = state.app[Page.suitesPage].viewMode; + const realStatus = state.app.viewMode; if ([ViewMode.ALL, ViewMode.FAILED, ViewMode.PASSED].includes(realStatus)) { return realStatus; @@ -36,9 +36,9 @@ export const getVisualTreeViewData = createSelector( getImages, getNamedImages, getVisualChecksViewMode, - (state: State): string => state.app[Page.suitesPage].nameFilter, - (state: State): boolean => state.app[Page.suitesPage].useRegexFilter, - (state: State): boolean => state.app[Page.suitesPage].useMatchCaseFilter + (state: State): string => state.app.nameFilter, + (state: State): boolean => state.app.useRegexFilter, + (state: State): boolean => state.app.useMatchCaseFilter ], ( images, @@ -82,11 +82,11 @@ export const getVisualTreeViewData = createSelector( switch (status) { case TestStatus.SUCCESS: + case TestStatus.UPDATED: stats[ViewMode.PASSED]++; return visualChecksViewMode === ViewMode.PASSED || visualChecksViewMode === ViewMode.ALL; case TestStatus.FAIL: case TestStatus.ERROR: - case TestStatus.UPDATED: stats[ViewMode.FAILED]++; return visualChecksViewMode === ViewMode.FAILED || visualChecksViewMode === ViewMode.ALL; default: diff --git a/lib/static/new-ui/hooks/useLegacyUrlMigration.tsx b/lib/static/new-ui/hooks/useLegacyUrlMigration.tsx index 4275f059c..58030556e 100644 --- a/lib/static/new-ui/hooks/useLegacyUrlMigration.tsx +++ b/lib/static/new-ui/hooks/useLegacyUrlMigration.tsx @@ -81,7 +81,7 @@ export const useLegacyUrlMigration = (): void => { const browsers = useSelector((state: State) => state.tree.browsers.byId); const suites = useSelector((state: State) => state.tree.suites.byId); const allBrowsersList = useSelector(getBrowsersList); - const currentViewMode = useSelector((state: State) => state.app[Page.suitesPage].viewMode); + const currentViewMode = useSelector((state: State) => state.app.viewMode); const migrationAttemptedRef = useRef(false); @@ -117,15 +117,15 @@ export const useLegacyUrlMigration = (): void => { className: 'toaster' }); - dispatch(selectBrowsers({page: Page.suitesPage, data: allBrowsersList})); - dispatch(updateNameFilter({page: Page.suitesPage, data: ''})); + dispatch(selectBrowsers({data: allBrowsersList})); + dispatch(updateNameFilter({data: ''})); dispatch(setStrictMatchFilter(false)); - dispatch(changeViewMode({page: Page.suitesPage, data: currentViewMode})); + dispatch(changeViewMode({data: currentViewMode})); } } if (matchedBrowserId) { - dispatch(selectBrowsers({page: Page.suitesPage, data: allBrowsersList})); + dispatch(selectBrowsers({data: allBrowsersList})); const browser = browsers[matchedBrowserId]; const suite = suites[browser.parentId]; @@ -158,10 +158,10 @@ export const useLegacyUrlMigration = (): void => { navigate(newUrl, {replace: true}); - dispatch(updateNameFilter({page: Page.suitesPage, data: ''})); + dispatch(updateNameFilter({data: ''})); dispatch(setStrictMatchFilter(false)); // This one's for ensuring "expanded" states of tree nodes are inited - dispatch(changeViewMode({page: Page.suitesPage, data: currentViewMode})); + dispatch(changeViewMode({data: currentViewMode})); } } } diff --git a/lib/static/new-ui/types/store.ts b/lib/static/new-ui/types/store.ts index 2fca8cf28..137e2a1ac 100644 --- a/lib/static/new-ui/types/store.ts +++ b/lib/static/new-ui/types/store.ts @@ -260,6 +260,14 @@ export interface State { isInitialized: boolean; availableFeatures: Feature[], isSearchLoading?: boolean; + + // Filters in top of sidebar + nameFilter: string; + useRegexFilter: boolean; + useMatchCaseFilter: boolean; + viewMode: ViewMode; + filteredBrowsers: BrowserItem[]; + [Page.suitesPage]: { currentTreeNodeId: string | null; currentBrowserId: string | null; @@ -267,13 +275,6 @@ export interface State { currentStepId: string | null; // Is used when hovering over a timeline of a snapshots player to highlight corresponding step currentHighlightedStepId: string | null; - - // Filters in top of sidebar - nameFilter: string; - useRegexFilter: boolean; - useMatchCaseFilter: boolean; - viewMode: ViewMode; - filteredBrowsers: BrowserItem[]; }; [Page.visualChecksPage]: { currentBrowserId: string | null; diff --git a/test/unit/lib/static/modules/middlewares/local-storage.js b/test/unit/lib/static/modules/middlewares/local-storage.js index 81640e5cd..2818f2dae 100644 --- a/test/unit/lib/static/modules/middlewares/local-storage.js +++ b/test/unit/lib/static/modules/middlewares/local-storage.js @@ -51,10 +51,7 @@ describe('lib/static/modules/middlewares/local-storage', () => { it('should store view state in local storage for "VIEW" prefix and init report actions', () => { const store = mkStore_({ view: defaultState.view, - app: { - suitesPage: mkStatePageFilters({}), - visualChecksPage: mkStatePageFilters({}) - } + app: mkStatePageFilters({}) }); const action = {type, payload: {page: 'suitesPage'}}; const localStorageMw = mkMiddleware_(); @@ -66,8 +63,7 @@ describe('lib/static/modules/middlewares/local-storage', () => { strictMatchFilter: false }); - assert.calledWith(localStorageWrapper.setItem, 'app.suitesPage.viewMode', ViewMode.ALL); - assert.calledWith(localStorageWrapper.setItem, 'app.visualChecksPage.viewMode', ViewMode.ALL); + assert.calledWith(localStorageWrapper.setItem, 'app.viewMode', ViewMode.ALL); }); }); }); diff --git a/test/unit/lib/static/modules/reducers/filters.js b/test/unit/lib/static/modules/reducers/filters.js index 86a51cc61..5b4647aca 100644 --- a/test/unit/lib/static/modules/reducers/filters.js +++ b/test/unit/lib/static/modules/reducers/filters.js @@ -55,7 +55,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].filteredBrowsers, []); + assert.deepStrictEqual(newState.app.filteredBrowsers, []); }); it('should set "filteredBrowsers" property to specified browsers with expanded versions', () => { @@ -71,7 +71,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(state, action); - assert.deepStrictEqual(newState.app[page].filteredBrowsers, [ + assert.deepStrictEqual(newState.app.filteredBrowsers, [ {id: 'firefox', versions: ['v1', 'v2']}, {id: 'safari', versions: ['v3']} ]); @@ -89,7 +89,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(state, action); - assert.deepStrictEqual(newState.app[page].filteredBrowsers, [ + assert.deepStrictEqual(newState.app.filteredBrowsers, [ {id: 'firefox', versions: ['v1', 'v2']}, {id: 'safari', versions: []} ]); @@ -108,7 +108,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(state, action); - assert.deepStrictEqual(newState.app[page].filteredBrowsers, [ + assert.deepStrictEqual(newState.app.filteredBrowsers, [ {id: 'firefox', versions: ['v1', 'v2']}, {id: 'safari', versions: ['23', '11.2']} ]); @@ -127,7 +127,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(state, action); - assert.deepStrictEqual(newState.app[page].filteredBrowsers, [ + assert.deepStrictEqual(newState.app.filteredBrowsers, [ {id: 'chrome-phone', versions: ['phone-124.0', 'phone-101.0']}, {id: 'safari', versions: ['23']} ]); @@ -143,7 +143,7 @@ describe('lib/static/modules/reducers/view', () => { const action = {type, payload: _mkInitialState()}; const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].filteredBrowsers, [{ + assert.deepStrictEqual(newState.app.filteredBrowsers, [{ id: 'safari:some', versions: ['v:1', 'v,2'] }]); @@ -156,7 +156,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].nameFilter, ''); + assert.deepStrictEqual(newState.app.nameFilter, ''); }); it('should set "testNameFilter" property to specified value', () => { @@ -166,7 +166,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].nameFilter, 'sometest'); + assert.deepStrictEqual(newState.app.nameFilter, 'sometest'); }); }); @@ -176,7 +176,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].viewMode, ViewMode.ALL); + assert.deepStrictEqual(newState.app.viewMode, ViewMode.ALL); }); it('should set "viewMode" property to "passed" value', () => { @@ -186,7 +186,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].viewMode, ViewMode.PASSED); + assert.deepStrictEqual(newState.app.viewMode, ViewMode.PASSED); }); it('should set "viewMode" property to "failed" value', () => { @@ -196,7 +196,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].viewMode, ViewMode.FAILED); + assert.deepStrictEqual(newState.app.viewMode, ViewMode.FAILED); }); it('should set "viewMode" property to "retried" value', () => { @@ -206,7 +206,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].viewMode, ViewMode.RETRIED); + assert.deepStrictEqual(newState.app.viewMode, ViewMode.RETRIED); }); it('should set "viewMode" property to "skipped" value', () => { @@ -217,7 +217,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.deepStrictEqual(newState.app[page].viewMode, ViewMode.SKIPPED); + assert.deepStrictEqual(newState.app.viewMode, ViewMode.SKIPPED); }); }); }); @@ -237,7 +237,7 @@ describe('lib/static/modules/reducers/view', () => { const newState = reducer(defaultState, action); - assert.equal(newState.app[page].viewMode, ViewMode[viewModeKey]); + assert.equal(newState.app.viewMode, ViewMode[viewModeKey]); }); })); }); diff --git a/test/unit/lib/static/modules/reducers/tree/index.js b/test/unit/lib/static/modules/reducers/tree/index.js index 796dde0ef..93a5f8590 100644 --- a/test/unit/lib/static/modules/reducers/tree/index.js +++ b/test/unit/lib/static/modules/reducers/tree/index.js @@ -21,9 +21,7 @@ describe('lib/static/modules/reducers/tree', () => { const filteredBrowsers = [{id: 'yabro', versions: []}]; const state = { - app: { - suitesPage: mkStatePageFilters({filteredBrowsers}) - }, + app: mkStatePageFilters({filteredBrowsers}), view: mkStateView({}) }; @@ -53,9 +51,7 @@ describe('lib/static/modules/reducers/tree', () => { const filteredBrowsers = [{id: 'yabro-1', versions: []}]; const state = { - app: { - suitesPage: mkStatePageFilters({filteredBrowsers}) - }, + app: mkStatePageFilters({filteredBrowsers}), view: mkStateView({}) }; @@ -92,9 +88,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED}) - }, + app: mkStatePageFilters({viewMode: ViewMode.FAILED}), view: mkStateView({}) }; @@ -115,9 +109,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({nameFilter: 's2'}) - }, + app: mkStatePageFilters({nameFilter: 's2'}), view: mkStateView({}) }; @@ -146,9 +138,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED}) - }, + app: mkStatePageFilters({viewMode: ViewMode.FAILED}), view: mkStateView({}) }; @@ -171,9 +161,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED}) - }, + app: mkStatePageFilters({viewMode: ViewMode.FAILED}), view: mkStateView({}) }; @@ -195,7 +183,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { view: mkStateView({keyToGroupTestsBy: 'foo.bar'}), - app: {suitesPage: mkStatePageFilters({})} + app: mkStatePageFilters({}) }; const newState = reducer(state, { @@ -215,9 +203,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({nameFilter: 'some-name'}) - }, + app: mkStatePageFilters({nameFilter: 'some-name'}), view: mkStateView({}) }; @@ -238,7 +224,7 @@ describe('lib/static/modules/reducers/tree', () => { it(`"errors" expanded and suite has ${status} status`, () => { const suitesById = {...mkSuite({id: 's1', status})}; const tree = mkStateTree({suitesById}); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({app, view: mkStateView({expand: EXPAND_ERRORS})}, { type: actionName, @@ -265,7 +251,7 @@ describe('lib/static/modules/reducers/tree', () => { ...mkResult({id: 'r3', status: SUCCESS}) }; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({app, view: mkStateView({expand: EXPAND_RETRIES})}, { type: actionName, @@ -292,7 +278,7 @@ describe('lib/static/modules/reducers/tree', () => { ...mkResult({id: 'r3', status: SUCCESS}) }; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({app, view: mkStateView({expand: EXPAND_RETRIES})}, { type: actionName, @@ -308,7 +294,7 @@ describe('lib/static/modules/reducers/tree', () => { it(`"all" expanded and suite has ${SUCCESS} status`, () => { const suitesById = {...mkSuite({id: 's1', status: SUCCESS})}; const tree = mkStateTree({suitesById}); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({app, view: mkStateView({expand: EXPAND_ALL})}, { type: actionName, @@ -323,7 +309,7 @@ describe('lib/static/modules/reducers/tree', () => { it(`"errors" expanded and suite has ${SUCCESS} status`, () => { const suitesById = {...mkSuite({id: 's1', status: SUCCESS})}; const tree = mkStateTree({suitesById}); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({app, view: mkStateView({expand: EXPAND_ERRORS})}, { type: actionName, @@ -338,7 +324,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1', parentId: 's1', resultIds: ['r1']})}; const resultsById = {...mkResult({id: 'r1', status: SUCCESS})}; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({app, view: mkStateView({expand: EXPAND_RETRIES})}, { type: actionName, @@ -366,7 +352,7 @@ describe('lib/static/modules/reducers/tree', () => { }; const tree = mkStateTree({suitesById, browsersById, resultsById}); const view = mkStateView(); - const app = {suitesPage: mkStatePageFilters()}; + const app = mkStatePageFilters(); const newState = reducer({view, app}, { type: actionName, @@ -396,7 +382,7 @@ describe('lib/static/modules/reducers/tree', () => { const newState = reducer({ view: mkStateView({}), - app: {suitesPage: mkStatePageFilters()} + app: mkStatePageFilters() }, { type: actionName, payload: {tree} @@ -416,9 +402,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED}) - }, + app: mkStatePageFilters({viewMode: ViewMode.FAILED}), view: mkStateView({}) }; @@ -437,9 +421,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({nameFilter: 's1'}) - }, + app: mkStatePageFilters({nameFilter: 's1'}), view: mkStateView({}) }; @@ -459,9 +441,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const filteredBrowsers = [{id: 'yabro', versions: []}]; const state = { - app: { - suitesPage: mkStatePageFilters({filteredBrowsers}) - }, + app: mkStatePageFilters({filteredBrowsers}), view: mkStateView({}) }; @@ -482,9 +462,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED}) - }, + app: mkStatePageFilters({viewMode: ViewMode.FAILED}), view: mkStateView({}) }; @@ -503,9 +481,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({nameFilter: 'some-name'}) - }, + app: mkStatePageFilters({nameFilter: 'some-name'}), view: mkStateView({}) }; @@ -525,9 +501,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const filteredBrowsers = [{id: 'chrome', versions: []}]; const state = { - app: { - suitesPage: mkStatePageFilters({filteredBrowsers}) - }, + app: mkStatePageFilters({filteredBrowsers}), view: mkStateView({}) }; @@ -546,9 +520,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById}); const state = { - app: { - suitesPage: mkStatePageFilters({}) - }, + app: mkStatePageFilters({}), view: mkStateView({keyToGroupTestsBy: 'foo.bar'}) }; @@ -574,7 +546,7 @@ describe('lib/static/modules/reducers/tree', () => { }; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, view: mkStateView({expand: EXPAND_ERRORS})}, { type: actionName, payload: {tree} @@ -593,7 +565,7 @@ describe('lib/static/modules/reducers/tree', () => { ...mkResult({id: 'r2', status: SUCCESS}) }; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, view: mkStateView({expand: EXPAND_RETRIES})}, { type: actionName, @@ -609,7 +581,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1', parentId: 's1', resultIds: ['r1']})}; const resultsById = {...mkResult({id: 'r1', status: SUCCESS})}; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand: EXPAND_ALL}), app}, { type: actionName, @@ -626,7 +598,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1', parentId: 's1', resultIds: ['r1']})}; const resultsById = {...mkResult({id: 'r1', status: SUCCESS})}; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand: EXPAND_ERRORS}), app}, { type: actionName, @@ -642,7 +614,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1', parentId: 's1', resultIds: ['r1']})}; const resultsById = {...mkResult({id: 'r1', status: SUCCESS})}; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand: EXPAND_RETRIES}), app}, { type: actionName, @@ -660,7 +632,7 @@ describe('lib/static/modules/reducers/tree', () => { ...mkResult({id: 'r2', status: SUCCESS}) }; const tree = mkStateTree({suitesById, browsersById, resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand: EXPAND_RETRIES}), app}, { type: actionName, @@ -689,7 +661,7 @@ describe('lib/static/modules/reducers/tree', () => { }; const tree = mkStateTree({suitesById, browsersById, resultsById}); const view = mkStateView(); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view, app}, { type: actionName, @@ -709,7 +681,7 @@ describe('lib/static/modules/reducers/tree', () => { const imagesById = {...mkImage({id: 'i1', status: FAIL})}; const tree = mkStateTree({imagesById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand}), app}, { type: actionName, payload: {tree} @@ -722,7 +694,7 @@ describe('lib/static/modules/reducers/tree', () => { const imagesById = {...mkImage({id: 'i1', status: ERROR})}; const tree = mkStateTree({imagesById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand}), app}, { type: actionName, payload: {tree} @@ -735,7 +707,7 @@ describe('lib/static/modules/reducers/tree', () => { it('"all" expanded and test success', () => { const imagesById = {...mkImage({id: 'i1', status: SUCCESS})}; const tree = mkStateTree({imagesById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand: EXPAND_ALL}), app}, { type: actionName, @@ -752,7 +724,7 @@ describe('lib/static/modules/reducers/tree', () => { const imagesById = {...mkImage({id: 'i1', status: SUCCESS})}; const tree = mkStateTree({imagesById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand}), app}, { type: actionName, payload: {tree} @@ -765,7 +737,7 @@ describe('lib/static/modules/reducers/tree', () => { const imagesById = {...mkImage({id: 'i1', status: UPDATED})}; const tree = mkStateTree({imagesById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView({expand}), app}, { type: actionName, payload: {tree} @@ -781,7 +753,7 @@ describe('lib/static/modules/reducers/tree', () => { it('should be "false" by default', () => { const resultsById = {...mkResult({id: 'r1'})}; const tree = mkStateTree({resultsById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({view: mkStateView(), app}, { type: actionName, @@ -802,7 +774,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersStateById = {b1: {retryIndex: 0}}; const resultsById = {...mkResult({id: 'r1', parentId: 'b1'})}; const tree = mkStateTree({suitesById, browsersById, browsersStateById, resultsById}); - const app = {suitesPage: mkStatePageFilters({filteredBrowsers: []})}; + const app = mkStatePageFilters({filteredBrowsers: []}); const view = mkStateView({}); const newState = reducer({tree, app, view}, { @@ -1045,7 +1017,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById, browsersStateById}); const filteredBrowsers = [{id: 'yabro', versions: []}]; - const app = {suitesPage: mkStatePageFilters({filteredBrowsers})}; + const app = mkStatePageFilters({filteredBrowsers}); const view = mkStateView({}); const newState = reducer({tree, app, view}, { @@ -1074,7 +1046,7 @@ describe('lib/static/modules/reducers/tree', () => { const tree = mkStateTree({suitesById, browsersById, resultsById, browsersStateById}); const filteredBrowsers = [{id: 'yabro-1', versions: []}]; - const app = {suitesPage: mkStatePageFilters({filteredBrowsers})}; + const app = mkStatePageFilters({filteredBrowsers}); const view = mkStateView({}); const newState = reducer({view, tree, app}, { @@ -1092,7 +1064,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = mkResult({id: 'r1', parentId: 'b1', status: FAIL}); const browsersStateById = {'b1': {checkStatus: UNCHECKED}}; const tree = mkStateTree({suitesById, browsersById, resultsById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({filteredBrowsers: [{id: 'yabro-1', versions: []}]})}; + const app = mkStatePageFilters({filteredBrowsers: [{id: 'yabro-1', versions: []}]}); const view = mkStateView({}); const newState = reducer({view, tree, app}, { @@ -1109,7 +1081,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1'})}; const browsersStateById = {b1: {retryIndex: 1}}; const tree = mkStateTree({browsersById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.CHANGE_TEST_RETRY, @@ -1124,7 +1096,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersStateById = {b1: {retryIndex: 1, lastMatchedRetryIndex: 0}}; const tree = mkStateTree({browsersById, browsersStateById}); const view = mkStateView({keyToGroupTestsBy: 'some-key'}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const newState = reducer({app, tree, view}, { type: actionNames.CHANGE_TEST_RETRY, @@ -1141,7 +1113,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1'}), ...mkBrowser({id: 'b2'})}; const browsersStateById = {b1: {shouldBeShown: true}, b2: {shouldBeShown: true}}; const tree = mkStateTree({browsersById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1156,7 +1128,7 @@ describe('lib/static/modules/reducers/tree', () => { const browsersById = {...mkBrowser({id: 'b1'}), ...mkBrowser({id: 'b2'})}; const browsersStateById = {b1: {lastMatchedRetryIndex: 1}, b2: {lastMatchedRetryIndex: 2}}; const tree = mkStateTree({browsersById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1171,7 +1143,7 @@ describe('lib/static/modules/reducers/tree', () => { const suitesById = {...mkSuite({id: 's1'}), ...mkSuite({id: 's2'})}; const suitesStateById = {s1: {shouldBeShown: true}, s2: {shouldBeShown: true}}; const tree = mkStateTree({suitesById, suitesStateById}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1186,7 +1158,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = {...mkResult({id: 'r1'}), ...mkResult({id: 'r2'})}; const resultsStateById = {r1: {matchedSelectedGroup: true}, r2: {matchedSelectedGroup: true}}; const tree = mkStateTree({resultsById, resultsStateById}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1204,7 +1176,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = {...mkResult({id: 'r1', status: SUCCESS}), ...mkResult({id: 'r2', status: SUCCESS})}; const resultsStateById = {r1: {}, r2: {}}; const tree = mkStateTree({browsersById, resultsById, resultsStateById}); - const app = {suitesPage: mkStatePageFilters({viewMode: ViewMode.FAILED})}; + const app = mkStatePageFilters({viewMode: ViewMode.FAILED}); const view = mkStateView({}); const newState = reducer({tree, view, app}, { @@ -1222,7 +1194,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = {...mkResult({id: 'r1', status: SUCCESS}), ...mkResult({id: 'r2', status: SUCCESS})}; const resultsStateById = {r1: {}, r2: {}}; const tree = mkStateTree({browsersById, browsersStateById, resultsById, resultsStateById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1240,7 +1212,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsStateById = {r1: {}, r2: {}}; const browsersStateById = {'b1': {checkStatus: UNCHECKED}}; const tree = mkStateTree({browsersById, resultsById, resultsStateById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1258,7 +1230,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsStateById = {r1: {}, r2: {}}; const browsersStateById = {'b1': {checkStatus: UNCHECKED}}; const tree = mkStateTree({browsersById, resultsById, resultsStateById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1279,7 +1251,7 @@ describe('lib/static/modules/reducers/tree', () => { }; const browsersStateById = {'b1': {checkStatus: UNCHECKED}}; const tree = mkStateTree({browsersById, resultsById, browsersStateById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, tree, view: mkStateView()}, { type: actionNames.TOGGLE_TESTS_GROUP, @@ -1307,7 +1279,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = {...mkResult({id: 'r1'}), ...mkResult({id: 'r2'})}; const resultsStateById = {r1: {matchedSelectedGroup: true}, r2: {matchedSelectedGroup: true}}; const tree = mkStateTree({resultsById, resultsStateById}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({app, tree, view}, {type: actionNames.GROUP_TESTS_BY_KEY}); @@ -1336,7 +1308,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = {...mkResult({id: 'r1', status: SUCCESS}), ...mkResult({id: 'r2', status: SUCCESS})}; const tree = mkStateTree({browsersById, browsersStateById, resultsById}); const view = mkStateView({keyToGroupTestsBy: ''}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({tree, view, app}, {type: actionNames.GROUP_TESTS_BY_KEY}); @@ -1352,7 +1324,7 @@ describe('lib/static/modules/reducers/tree', () => { const resultsById = {...mkResult({id: 'r1', status: SUCCESS}), ...mkResult({id: 'r2', status: SUCCESS})}; const tree = mkStateTree({suitesById, suitesStateById, browsersById, browsersStateById, resultsById}); const view = mkStateView({keyToGroupTestsBy: ''}); - const app = {suitesPage: mkStatePageFilters({})}; + const app = mkStatePageFilters({}); const newState = reducer({tree, view, app}, {type: actionNames.GROUP_TESTS_BY_KEY}); @@ -1748,11 +1720,9 @@ describe('lib/static/modules/reducers/tree', () => { }); const app = { - suitesPage: mkStatePageFilters({ - viewMode: ViewMode.FAILED, - nameFilter: 'suite-2', - filteredBrowsers: [{id: 'c1', versions: []}] - }) + viewMode: ViewMode.FAILED, + nameFilter: 'suite-2', + filteredBrowsers: [{id: 'c1', versions: []}] }; const newState = reducer({tree, view, app}, {type: actionName}); @@ -1773,11 +1743,9 @@ describe('lib/static/modules/reducers/tree', () => { strictMatchFilter: true }); const app = { - suitesPage: mkStatePageFilters({ - viewMode: ViewMode.FAILED, - nameFilter: 's1', - filteredBrowsers: [{id: 'b1', versions: []}] - }) + viewMode: ViewMode.FAILED, + nameFilter: 's1', + filteredBrowsers: [{id: 'b1', versions: []}] }; const newState = reducer({tree, view, app}, {type: actionName}); @@ -1814,11 +1782,9 @@ describe('lib/static/modules/reducers/tree', () => { strictMatchFilter: true }); const app = { - suitesPage: mkStatePageFilters({ - viewMode: ViewMode.FAILED, - nameFilter: 'brow1', - filteredBrowsers: [{id: 'b1', versions: []}] - }) + viewMode: ViewMode.FAILED, + nameFilter: 'brow1', + filteredBrowsers: [{id: 'b1', versions: []}] }; const newState = reducer({tree, view, app}, {type: actionName}); @@ -1868,11 +1834,9 @@ describe('lib/static/modules/reducers/tree', () => { strictMatchFilter: false }); const app = { - suitesPage: mkStatePageFilters({ - viewMode: ViewMode.FAILED, - nameFilter: '', - filteredBrowsers: [{id: 'b1', versions: []}, {id: 'd1', versions: []}] - }) + viewMode: ViewMode.FAILED, + nameFilter: '', + filteredBrowsers: [{id: 'b1', versions: []}, {id: 'd1', versions: []}] }; const staticImageAccepter = {acceptableImages: { 'i0': {id: 'i0', originalStatus: FAIL, stateNameImageId: 's0 s1 b1 foo'}, diff --git a/test/unit/lib/static/modules/selectors/stats.js b/test/unit/lib/static/modules/selectors/stats.js index b58d82bfc..b5537e18d 100644 --- a/test/unit/lib/static/modules/selectors/stats.js +++ b/test/unit/lib/static/modules/selectors/stats.js @@ -28,7 +28,7 @@ describe('stats selectors', () => { }; it('should return correct statistics when it is not filtered', () => { - const app = {suitesPage: {filteredBrowsers: []}}; + const app = {filteredBrowsers: []}; const filteredStats = getStatsFilteredByBrowsers({stats, app}); @@ -42,7 +42,7 @@ describe('stats selectors', () => { }); it('should return correct statistics for one filtered browser', () => { - const app = {suitesPage: {filteredBrowsers: [{id: 'bro1'}]}}; + const app = {filteredBrowsers: [{id: 'bro1'}]}; const filteredStats = getStatsFilteredByBrowsers({stats, app}); @@ -53,7 +53,7 @@ describe('stats selectors', () => { }); it('should return correct statistics for several filtered browsers', () => { - const app = {suitesPage: {filteredBrowsers: [{id: 'bro1'}, {id: 'bro2'}]}}; + const app = {filteredBrowsers: [{id: 'bro1'}, {id: 'bro2'}]}; const filteredStats = getStatsFilteredByBrowsers({stats, app}); @@ -65,12 +65,10 @@ describe('stats selectors', () => { it('should return correct statistics corresponding to versions', () => { const app = { - suitesPage: { - filteredBrowsers: [ - {id: 'bro1', versions: ['ver1', 'ver2']}, - {id: 'bro2', versions: ['ver1']} - ] - } + filteredBrowsers: [ + {id: 'bro1', versions: ['ver1', 'ver2']}, + {id: 'bro2', versions: ['ver1']} + ] }; const filteredStats = getStatsFilteredByBrowsers({stats, app}); From 2482b89087c7ba1c6e81e947fc3b3017e3f466d0 Mon Sep 17 00:00:00 2001 From: rocketraccoon Date: Mon, 25 May 2026 09:31:31 +0700 Subject: [PATCH 3/4] feat: add tests --- .../new-ui/suites-page/filters.testplane.js | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 test/func/tests/common/new-ui/suites-page/filters.testplane.js diff --git a/test/func/tests/common/new-ui/suites-page/filters.testplane.js b/test/func/tests/common/new-ui/suites-page/filters.testplane.js new file mode 100644 index 000000000..cb0e1c1ac --- /dev/null +++ b/test/func/tests/common/new-ui/suites-page/filters.testplane.js @@ -0,0 +1,84 @@ +if (process.env.TOOL === 'testplane') { + describe(process.env.TOOL || 'Default', () => { + describe('New UI', () => { + describe('Suites page', () => { + describe('Sync filters', () => { + let searchInput; + let matchCaseButton; + let regexButton; + let visualChecksMenu; + + beforeEach(async ({browser}) => { + searchInput = await browser.$('[data-qa="name-filter"] input'); + matchCaseButton = await browser.$('[data-qa="match-case"]'); + regexButton = await browser.$('[data-qa="regex"]'); + visualChecksMenu = await browser.$('[data-qa="visual-checks-page-menu-item"]'); + + await searchInput.waitForClickable(); + }); + + it('check same text on visual checks page', async ({browser}) => { + const value = 'some text'; + await searchInput.setValue(value); + await visualChecksMenu.click(); + await browser.pause(1000); + + const newValue = await searchInput.getValue(); + + expect(newValue).toEqual(value); + }); + + it('check same status on visual checks page', async ({browser}) => { + const failedStatus = await browser.$('[title="Failed"]'); + await failedStatus.click(); + + await visualChecksMenu.click(); + + const checked = await failedStatus.$('input[aria-checked="true"]'); + expect(checked).toExist(); + }); + + it('check fallback Skipped status to ALL on visual checks page', async ({browser}) => { + const skippedStatus = await browser.$('[title="Skipped"]'); + await skippedStatus.click(); + + await visualChecksMenu.click(); + + const allStatus = await browser.$('[title="All"]'); + const checked = await allStatus.$('input[aria-checked="true"]'); + expect(checked).toExist(); + }); + + it('check fallback Retried status to ALL on visual checks page', async ({browser}) => { + const retriedStatus = await browser.$('[title="Retried"]'); + await retriedStatus.click(); + + await visualChecksMenu.click(); + + const allStatus = await browser.$('[title="All"]'); + const checked = await allStatus.$('input[aria-checked="true"]'); + expect(checked).toExist(); + }); + + it('check same matchCase on visual checks page', async ({browser}) => { + await matchCaseButton.click(); + + await visualChecksMenu.click(); + + const matchCaseChecked = await browser.$('[data-qa="match-case"][aria-pressed="true"]'); + expect(matchCaseChecked).toExist(); + }); + + it('check same regex on visual checks page', async ({browser}) => { + await regexButton.click(); + + await visualChecksMenu.click(); + + const regexButtonChecked = await browser.$('[data-qa="regex"][aria-pressed="true"]'); + expect(regexButtonChecked).toExist(); + }); + }); + }); + }); + }); +} From 2cd42f0b732f499a9f3a09d7d3ed533b0f5dcf8c Mon Sep 17 00:00:00 2001 From: Sergey Cherebedov Date: Thu, 4 Jun 2026 18:34:28 +0300 Subject: [PATCH 4/4] fix: exact search --- lib/static/modules/reducers/tree/nodes/browsers.js | 4 ++-- lib/static/modules/search/index.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/static/modules/reducers/tree/nodes/browsers.js b/lib/static/modules/reducers/tree/nodes/browsers.js index 64a23258b..7e9736cf1 100644 --- a/lib/static/modules/reducers/tree/nodes/browsers.js +++ b/lib/static/modules/reducers/tree/nodes/browsers.js @@ -4,7 +4,7 @@ import {ensureDiffProperty, getUpdatedProperty} from '../../../utils/state'; import {UNCHECKED} from '../../../../../constants/checked-statuses'; import {isNodeFailed} from '../../../utils'; import {changeNodeState, shouldNodeBeOpened} from '../helpers'; -import {checkSearchResultExits} from '@/static/modules/search'; +import {checkSearchResultExits, getSearchPosition} from '@/static/modules/search'; export function initBrowsersState(tree, view, app = {}) { tree.browsers.allIds.forEach((browserId) => { @@ -99,7 +99,7 @@ export function calcBrowsersShowness({tree, view, app = {}, browserIds = [], dif shouldBeShown, isHiddenBecauseOfStatus, checkStatus, - fuzzyMatchScore: browserIds.length - index + fuzzyMatchScore: getSearchPosition(browserId) }; }); } diff --git a/lib/static/modules/search/index.ts b/lib/static/modules/search/index.ts index f614718c1..61021d705 100644 --- a/lib/static/modules/search/index.ts +++ b/lib/static/modules/search/index.ts @@ -4,6 +4,7 @@ import {AttachmentType, TagsAttachment} from '@/types'; let worker: Worker; let searchResult: Set = new Set([]); +let searchResultPosition: Map = new Map([]); export const initSearch = (tree: Tree): void => { const list = tree.results.allIds; @@ -32,6 +33,7 @@ export const initSearch = (tree: Tree): void => { }; export const checkSearchResultExits = (browserId: string): boolean => searchResult.has(browserId); +export const getSearchPosition = (item: string): number => searchResultPosition.get(item) || -1; export const search = ( text: string, @@ -70,6 +72,14 @@ export const search = ( } }).then((result: string[]) => { searchResult = new Set(result); + searchResultPosition = new Map(); + + result.forEach((item, index) => { + searchResultPosition.set( + item, + result.length - index + ); + }); if (updateMatchCase) { dispatch(setMatchCaseFilter({