import type { UniDriver } from '@wix/wix-ui-test-utils/unidriver'; import { DropdownUniDriver, ListItemSectionUniDriver, } from '@wix/design-system/dist/testkit/unidriver'; import { baseUniDriverFactory } from '../../unidriver'; import { ViewsDropdownListItemUniDriver } from './ViewsDropdownListItem.uni.driver'; import { ManageViewPopoverUniDriver } from './ManageViewPopover.uni.driver'; import { CollectionSaveViewModalUniDriver } from './modals/SaveView/CollectionSaveViewModal.uni.driver'; import { CollectionRenameViewModalUniDriver } from './modals/RenameView/CollectionRenameViewModal.uni.driver'; import { SaveToNewViewFooterUniDriver } from './SaveToNewViewFooter.uni.driver'; export const CollectionViewsDropdownUniDriver = ( base: UniDriver, body: UniDriver, ) => { const viewsDropdownDriverBase = () => base.$('[data-hook="collection-views-dropdown"]'); // In redesign mode the manage view popover (3-dots menu) is rendered inside // the dropdown list as a suffix on the selected view item. The dropdown // content is rendered in a portal, so we search `body` instead of `base`. // In legacy mode the popover is next to the dropdown inside `base`, but // searching `body` still finds it because `base` is within `body`. const manageViewPopover = () => ManageViewPopoverUniDriver(body, body); const getSaveViewModal = () => CollectionSaveViewModalUniDriver(base, body); const getRenameViewModal = () => CollectionRenameViewModalUniDriver(base, body); const saveToNewViewFooter = () => SaveToNewViewFooterUniDriver(body, body); const getDropdownLayoutBase = async () => { const contentDataHook = await viewsDropdownDriverBase().attr( 'data-content-hook', ); if (contentDataHook) { return body.$(`[data-content-element="${contentDataHook}"]`); } const baseDataHook = await viewsDropdownDriverBase().attr('data-hook'); return body.$(`[data-hook="${baseDataHook}-content"]`); }; const viewsDropdownDriver = () => DropdownUniDriver(viewsDropdownDriverBase(), body); const getViewListItemById = (id: string) => { return ViewsDropdownListItemUniDriver( viewsDropdownDriverBase().$(`[data-hook="dropdown-item-${id}"]`), body, ); }; const getViewListItemAt = (index: number) => { return ViewsDropdownListItemUniDriver( viewsDropdownDriverBase().$$('[data-hook="view-item"]').get(index), body, ); }; const getCategoryListItemById = (id: string) => { return ListItemSectionUniDriver( viewsDropdownDriverBase().$(`[data-hook="dropdown-item-${id}"]`), body, ); }; const getCategoryListItemAt = (index: number) => { return ListItemSectionUniDriver( viewsDropdownDriverBase().$$('[data-hook="view-category"]').get(index), body, ); }; const open = () => viewsDropdownDriver().inputDriver.click(); /** * Detect redesign mode by checking whether the popover trigger lives * inside `base` (legacy — standalone next to the dropdown) or only * inside the dropdown portal / body (redesign — suffix on selected item). */ // const isRedesignMode = async () => { // return !( // await base.$('[data-hook="manage-view-popover-button"]').exists() // ); // }; const openManageViewPopover = async () => { // In redesign mode the 3-dots button is inside the dropdown list on the // selected view, so we need to ensure the dropdown is open first. // In legacy mode opening the dropdown is harmless — the trigger is always // accessible in the component tree. const isOpen = await viewsDropdownDriver().dropdownLayoutDriver.isShown(); if (!isOpen) { await open(); } await manageViewPopover().open(); }; const saveNewViewClick = async () => { const isOpen = await viewsDropdownDriver().dropdownLayoutDriver.isShown(); if (!isOpen) { await open(); } // Redesign: "Save to New View" is in the dropdown footer const footerExists = await saveToNewViewFooter().exists(); if (footerExists) { return saveToNewViewFooter().clickSaveNewView(); } // Legacy: "Save to New View" is inside the ManageViewPopover await manageViewPopover().open(); await manageViewPopover().clickSaveNewView(); }; const deleteViewActionClick = () => manageViewPopover().clickDeleteView(); const saveViewChangesActionClick = () => manageViewPopover().clickSaveChanges(); const renameViewActionClick = () => manageViewPopover().clickRenameView(); const setAsDefaultViewActionClick = () => manageViewPopover().clickSetAsDefaultView(); return { ...baseUniDriverFactory(base), _input: () => viewsDropdownDriverBase().$('input'), exists: () => viewsDropdownDriver().exists(), initialLoaderExists: async () => (await getDropdownLayoutBase()) .$('[data-hook="initial-loader"]') .exists(), /** Get the currently selected view name */ getSelected: async () => { // When the dropdown is open and in editing mode (disableEditing=false), // the input value shows the search text and the selected label moves to // the placeholder. After onSelect fires (disableEditing=true) the value // switches back to the selected label but the dropdown may still be // visually shown. Try the value first; fall back to the placeholder. const isOpen = await viewsDropdownDriver().dropdownLayoutDriver.isShown(); if (isOpen) { const selectedId = await viewsDropdownDriver().dropdownLayoutDriver.getSelectedOptionId(); if (!selectedId) { return ''; } const value = await viewsDropdownDriver().inputDriver.getValue(); return value || viewsDropdownDriver().inputDriver.getPlaceholder(); } return viewsDropdownDriver().inputDriver.getValue(); }, /** Get the dropdown input text */ getInputValue: () => viewsDropdownDriver().inputDriver.getValue(), /** Indicates input is ready-only */ getInputReadOnly: () => viewsDropdownDriver().inputDriver.getReadOnly(), /** Indicates input is focused */ isInputFocused: () => viewsDropdownDriver().inputDriver.isFocus(), /** Get the currently selected view id */ getSelectedId: () => viewsDropdownDriver().dropdownLayoutDriver.getSelectedOptionId(), /** Checks if total is shown */ hasTotal: () => viewsDropdownDriver().inputDriver.suffixComponentExists('.total-count'), /** Opens views dropdown */ open, /** Enter dropdown input text */ enterText: (value: string) => viewsDropdownDriver().inputDriver.enterText(value), /** Click dropdown input clear button */ clickInputClearButton: () => viewsDropdownDriver().inputDriver.clickClear(), /** Indicates input clear button exists */ inputClearButtonExists: () => viewsDropdownDriver().inputDriver.hasClearButton(), /** Get number of views */ getViewsCount: async () => viewsDropdownDriverBase().$$('[data-hook="view-item"]').count(), /** Get array of all view names in the dropdown */ getViewsContent: async () => viewsDropdownDriverBase().$$('[data-hook="view-item"]').text(), /** Get array popover content , like empty state */ getViewsPopoverContent: async () => viewsDropdownDriver().dropdownLayoutDriver.optionsContent(), /** Indicates whether the dropdown is open */ isOpen: () => viewsDropdownDriver().dropdownLayoutDriver.isShown(), /** Get the placeholder test of the dropdown */ getPlaceholderText: () => viewsDropdownDriver().inputDriver.getPlaceholder(), /** Get modal driver for "Save View" */ getSaveViewModal, /** Get modal driver for "Rename View" */ getRenameViewModal, /** Get view list item driver by view id */ getViewListItemById, /** Get view list item driver by view index */ getViewListItemAt, /** Get category list item driver by category id */ getCategoryListItemById, /** Get category list item driver by category index */ getCategoryListItemAt, /** Get "all items" view list item */ getAllItemsView: () => getCategoryListItemById('all-items-view'), /** Get number of categories */ getCategoriesCount: async () => viewsDropdownDriverBase().$$('[data-hook="view-category"]').count(), /** Get array of all category names in the dropdown */ getCategoriesContent: async () => viewsDropdownDriverBase().$$('[data-hook="view-category"]').text(), /** Check if view pending changes */ isViewPendingChanges: async () => viewsDropdownDriver().inputDriver.prefixComponentExists( '[data-hook="view-pending-changes"]', ), isDefaultView: async () => viewsDropdownDriver().inputDriver.prefixComponentExists( '[data-hook="view-default"]', ), /** Indicates whether the Manage View popover exists */ manageViewPopoverExists: () => manageViewPopover().exists(), /** Indicates whether the Manage View popover opened */ isManageViewPopoverOpen: () => manageViewPopover().isOpen(), /** Opens the Manage View popover */ openManageViewPopover, /** Clicks the Save Changes action in the Manage View popover */ saveViewChangesActionClick, /** Clicks the Save to New View button (auto-detects redesign vs legacy) */ saveNewViewActionClick: saveNewViewClick, /** Clicks the Rename action in the Manage View popover */ renameViewActionClick, /** Clicks the Set as Default View action in the Manage View popover */ setAsDefaultViewActionClick, /** Clicks the Delete action in the Manage View popover */ deleteViewActionClick, /** Get DropdownLayoutOptionUniDriver of a Save Changes option */ getSaveViewChangesBtn: () => manageViewPopover().getSaveChangesBtn(), /** Get the Save to New View button (auto-detects redesign vs legacy) */ getSaveNewViewBtn: async () => { const isOpen = await viewsDropdownDriver().dropdownLayoutDriver.isShown(); if (!isOpen) { await open(); } // Redesign: footer button; Legacy: popover menu item const footerExists = await saveToNewViewFooter().exists(); if (footerExists) { return saveToNewViewFooter().getSaveNewViewBtn(); } // Fallback to popover (e.g. popover not yet open in legacy mode) return manageViewPopover().getSaveNewViewBtn(); }, /** Get DropdownLayoutOptionUniDriver of a Delete View option */ getDeleteViewBtn: () => manageViewPopover().getDeleteViewBtn(), /** Get DropdownLayoutOptionUniDriver of a Rename option */ getRenameViewBtn: () => manageViewPopover().getRenameViewBtn(), /** Get DropdownLayoutOptionUniDriver of a Set as Default View option */ getSetAsDefaultViewBtn: () => manageViewPopover().getSetAsDefaultViewBtn(), /** Saves new view with a defined name and current filters / sort / columns */ saveNewView: async (name: string) => { await saveNewViewClick(); const saveViewModal = getSaveViewModal(); await saveViewModal.fillViewNameInput(name); await saveViewModal.clickSaveButton(); }, /** Deletes the view with a given index */ deleteView: async (ind: number) => { await open(); await getViewListItemAt(ind).select(); await openManageViewPopover(); await deleteViewActionClick(); }, /** Saves changes to the current view */ saveViewChanges: async () => { await openManageViewPopover(); await saveViewChangesActionClick(); }, /** Renames the current view with a given name */ renameView: async (name: string) => { await openManageViewPopover(); await renameViewActionClick(); const renameViewModal = getRenameViewModal(); await renameViewModal.fillViewNameInput(name); await renameViewModal.clickSaveButton(); }, /** Sets a default view with a given index */ setAsDefaultView: async (ind: number) => { await open(); await getViewListItemAt(ind).select(); await openManageViewPopover(); await setAsDefaultViewActionClick(); }, }; };