Unverified Commit c94ec0d4 authored by Marius van den Beek's avatar Marius van den Beek Committed by GitHub
Browse files

Merge pull request #17456 from davelopez/23.1_fix_bulk_operation_menu_bis

[23.1] Fix history bulk operations menu (part 2)
parents 9d0a2009 b7b5c05a
Loading
Loading
Loading
Loading
+83 −39
Original line number Diff line number Diff line
@@ -24,8 +24,14 @@ const TASKS_CONFIG = {
    enable_celery_tasks: true,
};

const getPurgedContentSelection = () => new Map([["FAKE_ID", { purged: true }]]);
const getNonPurgedContentSelection = () => new Map([["FAKE_ID", { purged: false }]]);
const getMenuSelectorFor = (option) => `[data-description="${option} option"]`;

const getPurgedSelection = () => new Map([["FAKE_ID", { purged: true }]]);
const getNonPurgedSelection = () => new Map([["FAKE_ID", { purged: false }]]);
const getVisibleSelection = () => new Map([["FAKE_ID", { visible: true }]]);
const getHiddenSelection = () => new Map([["FAKE_ID", { visible: false }]]);
const getDeletedSelection = () => new Map([["FAKE_ID", { deleted: true }]]);
const getActiveSelection = () => new Map([["FAKE_ID", { deleted: false }]]);

async function mountSelectionOperationsWrapper(config) {
    const wrapper = shallowMount(
@@ -75,117 +81,155 @@ describe("History Selection Operations", () => {
                expect(wrapper.find('[data-description="selected count"]').text()).toContain("10");
            });

            it("should display 'hide' option only on visible items", async () => {
                const option = '[data-description="hide option"]';
            it("should display 'hide' option on visible items", async () => {
                const option = getMenuSelectorFor("hide");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "visible:true" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display 'hide' option when visible and hidden items are mixed", async () => {
                const option = getMenuSelectorFor("hide");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "visible:any" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should not display 'hide' option when only hidden items are selected", async () => {
                const option = getMenuSelectorFor("hide");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "visible:any", contentSelection: getHiddenSelection() });
                expect(wrapper.find(option).exists()).toBe(false);
                await wrapper.setProps({ filterText: "visible:false" });
                expect(wrapper.find(option).exists()).toBe(false);
            });

            it("should display 'unhide' option on hidden items", async () => {
                const option = '[data-description="unhide option"]';
                expect(wrapper.find(option).exists()).toBe(false);
                const option = getMenuSelectorFor("unhide");
                await wrapper.setProps({ filterText: "visible:false" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display 'unhide' option when hidden and visible items are mixed", async () => {
                const option = '[data-description="unhide option"]';
                expect(wrapper.find(option).exists()).toBe(false);
                const option = getMenuSelectorFor("unhide");
                await wrapper.setProps({ filterText: "visible:any" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should not display 'unhide' option when only visible items are selected", async () => {
                const option = getMenuSelectorFor("unhide");
                await wrapper.setProps({
                    filterText: "visible:any",
                    contentSelection: getVisibleSelection(),
                });
                expect(wrapper.find(option).exists()).toBe(false);
            });

            it("should display 'delete' option on non-deleted items", async () => {
                const option = '[data-description="delete option"]';
                const option = getMenuSelectorFor("delete");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "deleted:false" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display 'delete' option on non-deleted items", async () => {
                const option = getMenuSelectorFor("delete");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "deleted:false" });
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "deleted:true" });
                expect(wrapper.find(option).exists()).toBe(false);
            });

            it("should display 'delete' option when non-deleted and deleted items are mixed", async () => {
                const option = '[data-description="delete option"]';
                const option = getMenuSelectorFor("delete");
                await wrapper.setProps({ filterText: "deleted:any" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should not display 'delete' option when only deleted items are selected", async () => {
                const option = getMenuSelectorFor("delete");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "deleted:any", contentSelection: getDeletedSelection() });
                expect(wrapper.find(option).exists()).toBe(false);
            });

            it("should display 'permanently delete' option always", async () => {
                const option = '[data-description="purge option"]';
                const option = getMenuSelectorFor("purge");
                expect(wrapper.find(option).exists()).toBe(true);
                await wrapper.setProps({ filterText: "deleted:true" });
                await wrapper.setProps({ filterText: "deleted:any visible:any" });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display 'undelete' option on deleted and non-purged items", async () => {
                const option = '[data-description="undelete option"]';
                const option = getMenuSelectorFor("undelete");
                expect(wrapper.find(option).exists()).toBe(false);
                await wrapper.setProps({
                    filterText: "deleted:true",
                    contentSelection: getNonPurgedContentSelection(),
                    contentSelection: getNonPurgedSelection(),
                });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display 'undelete' option when non-purged items (deleted or not) are mixed", async () => {
                const option = '[data-description="undelete option"]';
                const option = getMenuSelectorFor("undelete");
                await wrapper.setProps({
                    filterText: "deleted:any",
                    contentSelection: getNonPurgedContentSelection(),
                    contentSelection: getNonPurgedSelection(),
                });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should not display 'undelete' when is manual selection mode and all selected items are purged", async () => {
                const option = '[data-description="undelete option"]';
            it("should not display 'undelete' when only non-deleted items are selected", async () => {
                const option = getMenuSelectorFor("undelete");
                await wrapper.setProps({
                    filterText: "deleted:true",
                    contentSelection: getPurgedContentSelection(),
                    filterText: "deleted:any",
                    contentSelection: getActiveSelection(),
                });
                expect(wrapper.find(option).exists()).toBe(false);
            });

            it("should not display 'undelete' when only purged items are selected", async () => {
                const option = getMenuSelectorFor("undelete");
                await wrapper.setProps({
                    contentSelection: getPurgedSelection(),
                    isQuerySelection: false,
                });
                expect(wrapper.find(option).exists()).toBe(false);
            });

            it("should display 'undelete' option when is query selection mode and filtering by deleted", async () => {
                const option = '[data-description="undelete option"]';
                const option = getMenuSelectorFor("undelete");
                // In query selection mode we don't know if some items may not be purged, so we allow to undelete
                await wrapper.setProps({
                    filterText: "deleted:true",
                    contentSelection: getPurgedContentSelection(),
                    contentSelection: getPurgedSelection(),
                    isQuerySelection: true,
                });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display 'undelete' option when is query selection mode and filtering by any deleted state", async () => {
                const option = '[data-description="undelete option"]';
                const option = getMenuSelectorFor("undelete");
                // In query selection mode we don't know if some items may not be purged, so we allow to undelete
                await wrapper.setProps({
                    filterText: "deleted:any",
                    contentSelection: getPurgedContentSelection(),
                    isQuerySelection: true,
                });
                await wrapper.setProps({ filterText: "deleted:any", isQuerySelection: true });
                expect(wrapper.find(option).exists()).toBe(true);
            });

            it("should display collection building options only on visible and non-deleted items", async () => {
            it("should display collection building options only on active (non-deleted) items", async () => {
                const buildListOption = '[data-description="build list"]';
                const buildPairOption = '[data-description="build pair"]';
                const buildListOfPairsOption = '[data-description="build list of pairs"]';
                await wrapper.setProps({ filterText: "visible:true deleted:false" });
                expect(wrapper.find(buildListOption).exists()).toBe(true);
                expect(wrapper.find(buildPairOption).exists()).toBe(true);
                expect(wrapper.find(buildListOfPairsOption).exists()).toBe(true);
                await wrapper.setProps({ filterText: "visible:false" });
                expect(wrapper.find(buildListOption).exists()).toBe(false);
                expect(wrapper.find(buildPairOption).exists()).toBe(false);
                expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
                await wrapper.setProps({ filterText: "deleted:true" });
                expect(wrapper.find(buildListOption).exists()).toBe(false);
                expect(wrapper.find(buildPairOption).exists()).toBe(false);
                expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
                await wrapper.setProps({ filterText: "visible:any" });
                expect(wrapper.find(buildListOption).exists()).toBe(false);
                expect(wrapper.find(buildPairOption).exists()).toBe(false);
                expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
                await wrapper.setProps({ filterText: "visible:any deleted:false" });
                expect(wrapper.find(buildListOption).exists()).toBe(true);
                expect(wrapper.find(buildPairOption).exists()).toBe(true);
                expect(wrapper.find(buildListOfPairsOption).exists()).toBe(true);
                await wrapper.setProps({ filterText: "deleted:any" });
                expect(wrapper.find(buildListOption).exists()).toBe(false);
                expect(wrapper.find(buildPairOption).exists()).toBe(false);
+57 −11
Original line number Diff line number Diff line
@@ -13,10 +13,13 @@
                <b-dropdown-text>
                    <span v-localize data-description="selected count">With {{ numSelected }} selected...</span>
                </b-dropdown-text>
                <b-dropdown-item v-if="showHidden" v-b-modal:show-selected-content data-description="unhide option">
                <b-dropdown-item
                    v-if="canUnhideSelection"
                    v-b-modal:show-selected-content
                    data-description="unhide option">
                    <span v-localize>Unhide</span>
                </b-dropdown-item>
                <b-dropdown-item v-else v-b-modal:hide-selected-content data-description="hide option">
                <b-dropdown-item v-if="canHideSelection" v-b-modal:hide-selected-content data-description="hide option">
                    <span v-localize>Hide</span>
                </b-dropdown-item>
                <b-dropdown-item
@@ -26,7 +29,7 @@
                    <span v-localize>Undelete</span>
                </b-dropdown-item>
                <b-dropdown-item
                    v-if="!showStrictDeleted"
                    v-if="canDeleteSelection"
                    v-b-modal:delete-selected-content
                    data-description="delete option">
                    <span v-localize>Delete</span>
@@ -195,20 +198,28 @@ export default {
    },
    computed: {
        /** @returns {Boolean} */
        showHidden() {
            return !HistoryFilters.checkFilter(this.filterText, "visible", true);
        canUnhideSelection() {
            return this.areAllSelectedHidden || (this.isAnyVisibilityAllowed && !this.areAllSelectedVisible);
        },
        /** @returns {Boolean} */
        canHideSelection() {
            return this.areAllSelectedVisible || (this.isAnyVisibilityAllowed && !this.areAllSelectedHidden);
        },
        /** @returns {Boolean} */
        showDeleted() {
            return !HistoryFilters.checkFilter(this.filterText, "deleted", false);
        },
        /** @returns {Boolean} */
        showStrictDeleted() {
            return HistoryFilters.checkFilter(this.filterText, "deleted", true);
        canDeleteSelection() {
            return this.areAllSelectedActive || (this.isAnyDeletedStateAllowed && !this.areAllSelectedDeleted);
        },
        /** @returns {Boolean} */
        canUndeleteSelection() {
            return this.showDeleted && (this.isQuerySelection || !this.areAllSelectedPurged);
        },
        /** @returns {Boolean} */
        showBuildOptions() {
            return !this.isQuerySelection && !this.showHidden && !this.showDeleted;
            return !this.isQuerySelection && this.areAllSelectedActive && !this.showDeleted;
        },
        /** @returns {Boolean} */
        showBuildOptionForAll() {
@@ -229,9 +240,6 @@ export default {
        noTagsSelected() {
            return this.selectedTags.length === 0;
        },
        canUndeleteSelection() {
            return this.showDeleted && (this.isQuerySelection || !this.areAllSelectedPurged);
        },
        areAllSelectedPurged() {
            for (const item of this.contentSelection.values()) {
                if (Object.prototype.hasOwnProperty.call(item, "purged") && !item["purged"]) {
@@ -240,6 +248,44 @@ export default {
            }
            return true;
        },
        areAllSelectedVisible() {
            for (const item of this.contentSelection.values()) {
                if (Object.prototype.hasOwnProperty.call(item, "visible") && !item["visible"]) {
                    return false;
                }
            }
            return true;
        },
        areAllSelectedHidden() {
            for (const item of this.contentSelection.values()) {
                if (Object.prototype.hasOwnProperty.call(item, "visible") && item["visible"]) {
                    return false;
                }
            }
            return true;
        },
        areAllSelectedActive() {
            for (const item of this.contentSelection.values()) {
                if (Object.prototype.hasOwnProperty.call(item, "deleted") && item["deleted"]) {
                    return false;
                }
            }
            return true;
        },
        areAllSelectedDeleted() {
            for (const item of this.contentSelection.values()) {
                if (Object.prototype.hasOwnProperty.call(item, "deleted") && !item["deleted"]) {
                    return false;
                }
            }
            return true;
        },
        isAnyVisibilityAllowed() {
            return HistoryFilters.checkFilter(this.filterText, "visible", "any");
        },
        isAnyDeletedStateAllowed() {
            return HistoryFilters.checkFilter(this.filterText, "deleted", "any");
        },
    },
    watch: {
        hasSelection(newVal) {