diff --git a/app/assets/javascripts/boards/components/board_add_new_column.vue b/app/assets/javascripts/boards/components/board_add_new_column.vue index d4b559add6e0e802e95a4da5fe24ec1033602799..22ad619e76be7ca6abcafbbd766178d3c33ffb75 100644 --- a/app/assets/javascripts/boards/components/board_add_new_column.vue +++ b/app/assets/javascripts/boards/components/board_add_new_column.vue @@ -2,9 +2,6 @@ import { GlFormRadio, GlFormRadioGroup, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; import { mapActions, mapGetters, mapState } from 'vuex'; import BoardAddNewColumnForm from '~/boards/components/board_add_new_column_form.vue'; -import { ListType } from '~/boards/constants'; -import boardsStore from '~/boards/stores/boards_store'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; export default { components: { @@ -24,7 +21,7 @@ export default { }, computed: { ...mapState(['labels', 'labelsLoading']), - ...mapGetters(['getListByLabelId', 'shouldUseGraphQL']), + ...mapGetters(['getListByLabelId']), columnForSelected() { return this.getListByLabelId(this.selectedId); }, @@ -34,17 +31,6 @@ export default { }, methods: { ...mapActions(['createList', 'fetchLabels', 'highlightList', 'setAddColumnFormVisibility']), - highlight(listId) { - if (this.shouldUseGraphQL) { - this.highlightList(listId); - } else { - const list = boardsStore.state.lists.find(({ id }) => id === listId); - list.highlighted = true; - setTimeout(() => { - list.highlighted = false; - }, 2000); - } - }, addList() { if (!this.selectedLabel) { return; @@ -54,23 +40,11 @@ export default { if (this.columnForSelected) { const listId = this.columnForSelected.id; - this.highlight(listId); + this.highlightList(listId); return; } - if (this.shouldUseGraphQL) { - this.createList({ labelId: this.selectedId }); - } else { - const listObj = { - labelId: getIdFromGraphQLId(this.selectedId), - title: this.selectedLabel.title, - position: boardsStore.state.lists.length - 2, - list_type: ListType.label, - label: this.selectedLabel, - }; - - boardsStore.new(listObj); - } + this.createList({ labelId: this.selectedId }); }, filterItems(searchTerm) { diff --git a/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue b/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue index 3381e4c3a7d8d43aaacec648929f3a23fb2995b4..030bc8284068ac543e325947a6b6e08932b26319 100644 --- a/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue +++ b/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue @@ -62,17 +62,7 @@ export default { // Don't do anything if this happened on a no trigger element if (e.target.classList.contains('js-no-trigger')) return; - if (this.glFeatures.graphqlBoardLists || this.isSwimlanesOn) { - this.setActiveId({ id: this.issue.id, sidebarType: ISSUABLE }); - return; - } - - const isMultiSelect = e.ctrlKey || e.metaKey; - - if (this.showDetail || isMultiSelect) { - this.showDetail = false; - this.$emit('show', { event: e, isMultiSelect }); - } + this.setActiveId({ id: this.issue.id, sidebarType: ISSUABLE }); }, }, }; diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue index 4df6ff752497054d2ccd70b6dc9bbefc3a47154d..50f444f27886a63c8239cb3ed7e9c234f9c3c1eb 100644 --- a/app/assets/javascripts/boards/components/board_content.vue +++ b/app/assets/javascripts/boards/components/board_content.vue @@ -5,24 +5,20 @@ import Draggable from 'vuedraggable'; import { mapState, mapGetters, mapActions } from 'vuex'; import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue'; import defaultSortableConfig from '~/sortable/sortable_config'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { DraggableItemTypes } from '../constants'; import BoardColumn from './board_column.vue'; -import BoardColumnDeprecated from './board_column_deprecated.vue'; export default { draggableItemTypes: DraggableItemTypes, components: { BoardAddNewColumn, BoardColumn, - BoardColumnDeprecated, BoardContentSidebar: () => import('~/boards/components/board_content_sidebar.vue'), EpicBoardContentSidebar: () => import('ee_component/boards/components/epic_board_content_sidebar.vue'), EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'), GlAlert, }, - mixins: [glFeatureFlagMixin()], inject: ['canAdminList'], props: { lists: { @@ -37,20 +33,15 @@ export default { }, computed: { ...mapState(['boardLists', 'error', 'addColumnForm']), - ...mapGetters(['isSwimlanesOn', 'isEpicBoard']), - useNewBoardColumnComponent() { - return this.glFeatures.graphqlBoardLists || this.isSwimlanesOn || this.isEpicBoard; - }, + ...mapGetters(['isSwimlanesOn', 'isEpicBoard', 'isIssueBoard']), addColumnFormVisible() { return this.addColumnForm?.visible; }, boardListsToUse() { - return this.useNewBoardColumnComponent - ? sortBy([...Object.values(this.boardLists)], 'position') - : this.lists; + return sortBy([...Object.values(this.boardLists)], 'position'); }, canDragColumns() { - return (this.isEpicBoard || this.glFeatures.graphqlBoardLists) && this.canAdminList; + return this.canAdminList; }, boardColumnWrapper() { return this.canDragColumns ? Draggable : 'div'; @@ -68,9 +59,6 @@ export default { return this.canDragColumns ? options : {}; }, - boardColumnComponent() { - return this.useNewBoardColumnComponent ? BoardColumn : BoardColumnDeprecated; - }, }, methods: { ...mapActions(['moveList', 'unsetError']), @@ -95,8 +83,7 @@ export default { class="boards-list gl-w-full gl-py-5 gl-px-3 gl-white-space-nowrap" @end="moveList" > - <component - :is="boardColumnComponent" + <board-column v-for="(list, index) in boardListsToUse" :key="index" ref="board" @@ -118,10 +105,7 @@ export default { :disabled="disabled" /> - <board-content-sidebar - v-if="isSwimlanesOn || glFeatures.graphqlBoardLists" - data-testid="issue-boards-sidebar" - /> + <board-content-sidebar v-if="isIssueBoard" data-testid="issue-boards-sidebar" /> <epic-board-content-sidebar v-else-if="isEpicBoard" data-testid="epic-boards-sidebar" /> </div> diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue index c089a6a39af7b9f61b2b29ef0b1dfbc26062b4b5..1ca1cf512a12ce6c56901078d6de912f2428a767 100644 --- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue +++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue @@ -31,20 +31,13 @@ export default { }; }, computed: { - ...mapGetters(['isSidebarOpen', 'shouldUseGraphQL', 'isEpicBoard']), + ...mapGetters(['isSidebarOpen', 'isEpicBoard']), ...mapState(['activeId', 'sidebarType', 'boardLists']), isWipLimitsOn() { return this.glFeatures.wipLimits && !this.isEpicBoard; }, activeList() { - /* - Warning: Though a computed property it is not reactive because we are - referencing a List Model class. Reactivity only applies to plain JS objects - */ - if (this.shouldUseGraphQL || this.isEpicBoard) { - return this.boardLists[this.activeId]; - } - return boardsStore.state.lists.find(({ id }) => id === this.activeId); + return this.boardLists[this.activeId]; }, activeListLabel() { return this.activeList.label; @@ -73,12 +66,8 @@ export default { deleteBoard() { // eslint-disable-next-line no-alert if (window.confirm(__('Are you sure you want to remove this list?'))) { - if (this.shouldUseGraphQL || this.isEpicBoard) { - this.track('click_button', { label: 'remove_list' }); - this.removeList(this.activeId); - } else { - this.activeList.destroy(); - } + this.track('click_button', { label: 'remove_list' }); + this.removeList(this.activeId); this.unsetActiveId(); } }, diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index c6040f1e4aa78fd70c3b2eab997dce950e7c22fc..72586970008c46181b0384e8ce37defe880bd95f 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -4,7 +4,6 @@ import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable import { updateHistory } from '~/lib/utils/url_utility'; import FilteredSearchContainer from '../filtered_search/container'; import vuexstore from './stores'; -import boardsStore from './stores/boards_store'; export default class FilteredSearchBoards extends FilteredSearchManager { constructor(store, updateUrl = false, cantEdit = []) { @@ -26,7 +25,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager { this.cantEdit = cantEdit.filter((i) => typeof i === 'string'); this.cantEditWithValue = cantEdit.filter((i) => typeof i === 'object'); - if (vuexstore.getters.shouldUseGraphQL && vuexstore.state.boardConfig) { + if (vuexstore.state.boardConfig) { const boardConfigPath = transformBoardConfig(vuexstore.state.boardConfig); // TODO Refactor: https://gitlab.com/gitlab-org/gitlab/-/issues/329274 // here we are using "window.location.search" as a temporary store @@ -45,14 +44,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager { const groupByParam = new URLSearchParams(window.location.search).get('group_by'); this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`; - if (vuexstore.getters.shouldUseGraphQL) { - updateHistory({ - url: `?${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`, - }); - vuexstore.dispatch('performSearch'); - } else if (this.updateUrl) { - boardsStore.updateFiltersUrl(); - } + updateHistory({ + url: `?${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`, + }); + vuexstore.dispatch('performSearch'); } removeTokens() { diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index a5dcabdd729fcc3ab4657052d12f95f4c8f49a8f..8e91718f7b3cf7df961e478293f01743384de68f 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -2,7 +2,7 @@ import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import PortalVue from 'portal-vue'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import { mapActions, mapGetters } from 'vuex'; +import { mapActions } from 'vuex'; import 'ee_else_ce/boards/models/issue'; import 'ee_else_ce/boards/models/list'; @@ -78,10 +78,7 @@ export default () => { initBoardsFilteredSearch(apolloProvider); } - if (!gon?.features?.graphqlBoardLists) { - boardsStore.create(); - boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours); - } + boardsStore.create(); // eslint-disable-next-line @gitlab/no-runtime-template-compiler issueBoardsApp = new Vue({ @@ -133,7 +130,6 @@ export default () => { }; }, computed: { - ...mapGetters(['shouldUseGraphQL']), detailIssueVisible() { return Object.keys(this.detailIssue.issue).length; }, @@ -174,14 +170,12 @@ export default () => { eventHub.$on('newDetailIssue', this.updateDetailIssue); eventHub.$on('clearDetailIssue', this.clearDetailIssue); sidebarEventHub.$on('toggleSubscription', this.toggleSubscription); - eventHub.$on('initialBoardLoad', this.initialBoardLoad); }, beforeDestroy() { eventHub.$off('updateTokens', this.updateTokens); eventHub.$off('newDetailIssue', this.updateDetailIssue); eventHub.$off('clearDetailIssue', this.clearDetailIssue); sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); - eventHub.$off('initialBoardLoad', this.initialBoardLoad); }, mounted() { if (!gon?.features?.issueBoardsFilteredSearch) { @@ -196,28 +190,9 @@ export default () => { this.performSearch(); boardsStore.disabled = this.disabled; - - if (!this.shouldUseGraphQL) { - this.initialBoardLoad(); - } }, methods: { ...mapActions(['setInitialBoardData', 'performSearch', 'setError']), - initialBoardLoad() { - boardsStore - .all() - .then((res) => res.data) - .then((lists) => { - lists.forEach((list) => boardsStore.addList(list)); - this.loading = false; - }) - .catch((error) => { - this.setError({ - error, - message: __('An error occurred while fetching the board lists. Please try again.'), - }); - }); - }, updateTokens() { this.filterManager.updateTokens(); }, diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js index 7d6179a8547e0a96609e3e5f6132cc5d87492a40..a3a8ad06c4300a6109dfb2753d0cadf54383ff39 100644 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js @@ -1,12 +1,9 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import { mapGetters } from 'vuex'; import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue'; -import BoardsSelectorDeprecated from '~/boards/components/boards_selector_deprecated.vue'; import store from '~/boards/stores'; import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; Vue.use(VueApollo); @@ -25,9 +22,7 @@ export default (params = {}) => { el: boardsSwitcherElement, components: { BoardsSelector, - BoardsSelectorDeprecated, }, - mixins: [glFeatureFlagMixin()], apolloProvider, store, provide: { @@ -52,16 +47,8 @@ export default (params = {}) => { return { boardsSelectorProps }; }, - computed: { - ...mapGetters(['shouldUseGraphQL', 'isEpicBoard']), - }, render(createElement) { - if (this.shouldUseGraphQL || this.isEpicBoard) { - return createElement(BoardsSelector, { - props: this.boardsSelectorProps, - }); - } - return createElement(BoardsSelectorDeprecated, { + return createElement(BoardsSelector, { props: this.boardsSelectorProps, }); }, diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 970d00841bdbf687bfcb24a4cbaab8a0c6559b45..e67b8b6235bb94e18f46e1a20bf6417d73dce0f8 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -82,11 +82,8 @@ export default { 'setFilters', convertObjectPropsToCamelCase(queryToObject(window.location.search, { gatherArrays: true })), ); - - if (gon.features.graphqlBoardLists) { - dispatch('fetchLists'); - dispatch('resetIssues'); - } + dispatch('fetchLists'); + dispatch('resetIssues'); }, fetchLists: ({ commit, state, dispatch }) => { @@ -182,7 +179,7 @@ export default { }); }, - fetchLabels: ({ state, commit, getters }, searchTerm) => { + fetchLabels: ({ state, commit }, searchTerm) => { const { fullPath, boardType } = state; const variables = { @@ -200,14 +197,7 @@ export default { variables, }) .then(({ data }) => { - let labels = data[boardType]?.labels.nodes; - - if (!getters.shouldUseGraphQL && !getters.isEpicBoard) { - labels = labels.map((label) => ({ - ...label, - id: getIdFromGraphQLId(label.id), - })); - } + const labels = data[boardType]?.labels.nodes; commit(types.RECEIVE_LABELS_SUCCESS, labels); return labels; diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js index 140c9ef7ac40cf8d6aae383e8d8c7612ee58b4bc..7fa8cac9c98fb774cf188c3e1f7941e35f08ef0e 100644 --- a/app/assets/javascripts/boards/stores/getters.js +++ b/app/assets/javascripts/boards/stores/getters.js @@ -51,8 +51,4 @@ export default { isEpicBoard: () => { return false; }, - - shouldUseGraphQL: () => { - return gon?.features?.graphqlBoardLists; - }, }; diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index 96a3b38669ddc1922a9f8cc3f3b580bd33ef11e2..60708c13b858eee4c61c5fab0a92a210d9a54ee5 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -7,7 +7,6 @@ class Groups::BoardsController < Groups::ApplicationController before_action :assign_endpoint_vars before_action do - push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: :yaml) push_frontend_feature_flag(:issue_boards_filtered_search, group, default_enabled: :yaml) push_frontend_feature_flag(:board_multi_select, group, default_enabled: :yaml) push_frontend_feature_flag(:swimlanes_buffered_rendering, group, default_enabled: :yaml) diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 035b76abfd654679f030479629b8d8adf436121b..316582f3994e4bad3394280a4d784a9d02f5c7ea 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -8,7 +8,6 @@ class Projects::BoardsController < Projects::ApplicationController before_action :assign_endpoint_vars before_action do push_frontend_feature_flag(:swimlanes_buffered_rendering, project, default_enabled: :yaml) - push_frontend_feature_flag(:graphql_board_lists, project, default_enabled: :yaml) push_frontend_feature_flag(:issue_boards_filtered_search, project, default_enabled: :yaml) push_frontend_feature_flag(:board_multi_select, project, default_enabled: :yaml) push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml) diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index a49c17e9265d02d9d362200befdf37688670c765..a3f817cdd72d4133a6bdd31c027ec06a42b559d7 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -1,5 +1,4 @@ - board = local_assigns.fetch(:board, nil) -- group = local_assigns.fetch(:group, false) - @no_breadcrumb_container = true - @no_container = true - @content_wrapper_class = "#{@content_wrapper_class} gl-relative" @@ -20,6 +19,4 @@ = render 'shared/issuable/search_bar', type: :boards, board: board #board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" } %board-content{ ":lists" => "state.lists", ":disabled" => "disabled" } - - if !is_epic_board && !Feature.enabled?(:graphql_board_lists, default_enabled: :yaml) - = render "shared/boards/components/sidebar", group: group %board-settings-sidebar diff --git a/app/views/shared/boards/components/_sidebar.html.haml b/app/views/shared/boards/components/_sidebar.html.haml deleted file mode 100644 index 8976e89b3d353e4155af3ffdc21cb66c8822c91c..0000000000000000000000000000000000000000 --- a/app/views/shared/boards/components/_sidebar.html.haml +++ /dev/null @@ -1,27 +0,0 @@ -%board-sidebar{ "inline-template" => true, ":current-user" => (UserSerializer.new.represent(current_user) || {}).to_json } - %transition{ name: "boards-sidebar-slide" } - %aside.right-sidebar.right-sidebar-expanded.boards-sidebar{ "v-show" => "showSidebar", 'aria-label': s_('Boards|Board'), 'data-testid': 'issue-boards-sidebar' } - .issuable-sidebar - .block.issuable-sidebar-header.position-relative - %span.issuable-header-text.hide-collapsed.float-left - %strong.bold - {{ issue.title }} - %br/ - %span - = render_if_exists "shared/boards/components/sidebar/issue_project_path" - = precede "#" do - {{ issue.iid }} - %a.gutter-toggle.position-absolute.position-top-0.position-right-0{ role: "button", - href: "#", - "@click.prevent" => "closeSidebar", - "aria-label" => "Toggle sidebar" } - = custom_icon("icon_close", size: 15) - .js-issuable-update - = render "shared/boards/components/sidebar/assignee" - = render_if_exists "shared/boards/components/sidebar/epic" - = render "shared/boards/components/sidebar/milestone" - = render "shared/boards/components/sidebar/time_tracker" - = render "shared/boards/components/sidebar/due_date" - = render "shared/boards/components/sidebar/labels" - = render_if_exists "shared/boards/components/sidebar/weight" - = render "shared/boards/components/sidebar/notifications" diff --git a/config/feature_flags/development/graphql_board_lists.yml b/config/feature_flags/development/graphql_board_lists.yml deleted file mode 100644 index 0a7578f406334ae7a3711aeff618ff7e8aa20420..0000000000000000000000000000000000000000 --- a/config/feature_flags/development/graphql_board_lists.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: graphql_board_lists -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37905 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/248908 -milestone: '13.4' -type: development -group: group::project management -default_enabled: true diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 3747be987b62b358db8872eb4d9e54e76a4cefd6..4d1805e3d31ae17aeda70a49b79042bafdb2c3e2 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -229,8 +229,7 @@ and vice versa. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285074) in GitLab 13.9. > - [Deployed behind a feature flag](../feature_flags.md), enabled by default. > - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/248908) in GitLab 14.1 -> - Recommended for production use. -> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-graphql-based-issue-boards). **(FREE SELF)** +> - [Feature flag `graphql_board_lists`](https://gitlab.com/gitlab-org/gitlab/-/issues/248908) removed in GitLab 14.3 There can be [risks when disabling released features](../../administration/feature_flags.md#risks-when-disabling-released-features). @@ -673,24 +672,6 @@ A few things to remember: by default. If you have more than 20 issues, start scrolling down and the next 20 appear. -### Enable or disable GraphQL-based issue boards **(FREE SELF)** - -It is deployed behind a feature flag that is **enabled by default** as of GitLab 14.1. -[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) -can disable it. - -To enable it: - -```ruby -Feature.enable(:graphql_board_lists) -``` - -To disable it: - -```ruby -Feature.disable(:graphql_board_lists) -``` - ### Enable or disable iteration lists in boards **(PREMIUM SELF)** The iteration list is under development but ready for production use. It is diff --git a/ee/app/assets/javascripts/boards/components/board_add_new_column.vue b/ee/app/assets/javascripts/boards/components/board_add_new_column.vue index 7d02059b129dffce72be03d1a3fcf25f359cc6a1..2f8d85717e1ba7406a9ebb409a3c1e2486801a0c 100644 --- a/ee/app/assets/javascripts/boards/components/board_add_new_column.vue +++ b/ee/app/assets/javascripts/boards/components/board_add_new_column.vue @@ -11,8 +11,6 @@ import { import { mapActions, mapGetters, mapState } from 'vuex'; import BoardAddNewColumnForm from '~/boards/components/board_add_new_column_form.vue'; import { ListType } from '~/boards/constants'; -import boardsStore from '~/boards/stores/boards_store'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { isScopedLabel } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; @@ -87,7 +85,7 @@ export default { 'assignees', 'assigneesLoading', ]), - ...mapGetters(['getListByTypeId', 'shouldUseGraphQL', 'isEpicBoard']), + ...mapGetters(['getListByTypeId', 'isEpicBoard']), info() { return listTypeInfo[this.columnType] || {}; @@ -132,16 +130,10 @@ export default { return false; } - if (this.shouldUseGraphQL || this.isEpicBoard) { - const key = `${this.columnType}Id`; - return this.getListByTypeId({ - [key]: this.selectedId, - }); - } - - return boardsStore.state.lists.find( - (list) => list[this.columnType]?.id === getIdFromGraphQLId(this.selectedId), - ); + const key = `${this.columnType}Id`; + return this.getListByTypeId({ + [key]: this.selectedId, + }); }, loading() { @@ -187,17 +179,6 @@ export default { 'fetchIterations', 'fetchMilestones', ]), - highlight(listId) { - if (this.shouldUseGraphQL || this.isEpicBoard) { - this.highlightList(listId); - } else { - const list = boardsStore.state.lists.find(({ id }) => id === listId); - list.highlighted = true; - setTimeout(() => { - list.highlighted = false; - }, 2000); - } - }, addList() { if (!this.selectedItem) { return; @@ -207,45 +188,12 @@ export default { if (this.columnForSelected) { const listId = this.columnForSelected.id; - this.highlight(listId); + this.highlightList(listId); return; } - if (this.shouldUseGraphQL || this.isEpicBoard) { - // eslint-disable-next-line @gitlab/require-i18n-strings - this.createList({ [`${this.columnType}Id`]: this.selectedId }); - } else { - const { length } = boardsStore.state.lists; - const position = this.hideClosed ? length - 1 : length - 2; - const listObj = { - // eslint-disable-next-line @gitlab/require-i18n-strings - [`${this.columnType}Id`]: getIdFromGraphQLId(this.selectedId), - title: this.selectedItem.title, - position, - list_type: this.columnType, - }; - - if (this.labelTypeSelected) { - listObj.label = this.selectedItem; - } else if (this.milestoneTypeSelected) { - listObj.milestone = { - ...this.selectedItem, - id: getIdFromGraphQLId(this.selectedItem.id), - }; - } else if (this.iterationTypeSelected) { - listObj.iteration = { - ...this.selectedItem, - id: getIdFromGraphQLId(this.selectedItem.id), - }; - } else if (this.assigneeTypeSelected) { - listObj.assignee = { - ...this.selectedItem, - id: getIdFromGraphQLId(this.selectedItem.id), - }; - } - - boardsStore.new(listObj); - } + // eslint-disable-next-line @gitlab/require-i18n-strings + this.createList({ [`${this.columnType}Id`]: this.selectedId }); }, filterItems(searchTerm) { diff --git a/ee/app/assets/javascripts/boards/components/board_settings_wip_limit.vue b/ee/app/assets/javascripts/boards/components/board_settings_wip_limit.vue index 5578109112e1f3580dcbf7c06ad7f58ada020223..26c7e8fb8c7b72a0e124c799447d1d35c08687f1 100644 --- a/ee/app/assets/javascripts/boards/components/board_settings_wip_limit.vue +++ b/ee/app/assets/javascripts/boards/components/board_settings_wip_limit.vue @@ -1,8 +1,6 @@ <script> import { GlButton, GlFormInput } from '@gitlab/ui'; -import { mapActions, mapGetters, mapState } from 'vuex'; -import boardsStoreEE from 'ee/boards/stores/boards_store_ee'; -import { inactiveId } from '~/boards/constants'; +import { mapActions, mapState } from 'vuex'; import { __, n__ } from '~/locale'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; @@ -36,7 +34,6 @@ export default { }, computed: { ...mapState(['activeId']), - ...mapGetters(['shouldUseGraphQL']), wipLimitTypeText() { return n__('%d issue', '%d issues', this.maxIssueCount); }, @@ -76,11 +73,6 @@ export default { const id = this.activeId; this.updateListWipLimit({ maxIssueCount: wipLimit, listId: id }) - .then(() => { - if (!this.shouldUseGraphQL) { - boardsStoreEE.setMaxIssueCountOnList(id, wipLimit); - } - }) .catch(() => { this.unsetActiveId(); this.setError({ @@ -96,11 +88,6 @@ export default { }, clearWipLimit() { this.updateListWipLimit({ maxIssueCount: 0, listId: this.activeId }) - .then(() => { - if (!this.shouldUseGraphQL) { - boardsStoreEE.setMaxIssueCountOnList(this.activeId, inactiveId); - } - }) .catch(() => { this.unsetActiveId(); this.setError({ diff --git a/ee/app/assets/javascripts/boards/components/boards_list_selector/index.js b/ee/app/assets/javascripts/boards/components/boards_list_selector/index.js index 3ebc9e1eda63c7ed6552a63459c99cc1283a9c12..3a5db37d0b159d0e7d577e56dc7e01e3dd7bc983 100644 --- a/ee/app/assets/javascripts/boards/components/boards_list_selector/index.js +++ b/ee/app/assets/javascripts/boards/components/boards_list_selector/index.js @@ -65,19 +65,12 @@ export default Vue.extend({ return list; }, handleItemClick(item) { - if ( - this.vuexStore.getters.shouldUseGraphQL && - !this.vuexStore.getters.getListByTitle(item.title) - ) { + if (!this.vuexStore.getters.getListByTitle(item.title)) { if (this.listType === 'milestones') { this.vuexStore.dispatch('createList', { milestoneId: fullMilestoneId(item.id) }); } else if (this.listType === 'assignees') { this.vuexStore.dispatch('createList', { assigneeId: fullUserId(item.id) }); } - } else if (!this.store.findList('title', item.title)) { - const list = this.prepareListObject(item); - - this.store.new(list); } }, }, diff --git a/ee/app/assets/javascripts/boards/stores/actions.js b/ee/app/assets/javascripts/boards/stores/actions.js index b7273e401e7b8c14b18794606b62879aea808ce5..23ca95dc7e31934718b8285ab947a26133d1fab1 100644 --- a/ee/app/assets/javascripts/boards/stores/actions.js +++ b/ee/app/assets/javascripts/boards/stores/actions.js @@ -6,15 +6,12 @@ import { filterVariables, } from '~/boards/boards_util'; import { BoardType } from '~/boards/constants'; -import eventHub from '~/boards/eventhub'; import groupBoardMembersQuery from '~/boards/graphql/group_board_members.query.graphql'; import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql'; import projectBoardMembersQuery from '~/boards/graphql/project_board_members.query.graphql'; import actionsCE, { gqlClient } from '~/boards/stores/actions'; -import boardsStore from '~/boards/stores/boards_store'; import * as typesCE from '~/boards/stores/mutation_types'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import axios from '~/lib/utils/axios_utils'; import { historyPushState, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { mergeUrlParams, removeParams, queryToObject } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; @@ -39,7 +36,6 @@ import projectBoardIterationsQuery from '../graphql/project_board_iterations.que import updateBoardEpicUserPreferencesMutation from '../graphql/update_board_epic_user_preferences.mutation.graphql'; import updateEpicLabelsMutation from '../graphql/update_epic_labels.mutation.graphql'; -import boardsStoreEE from './boards_store_ee'; import * as types from './mutation_types'; const fetchAndFormatListIssues = (state, extraVariables) => { @@ -121,13 +117,11 @@ export default { if (getters.isSwimlanesOn) { dispatch('resetEpics'); - dispatch('resetIssues'); dispatch('fetchEpicsSwimlanes'); - dispatch('fetchLists'); - } else if (gon.features.graphqlBoardLists || getters.isEpicBoard) { - dispatch('fetchLists'); - dispatch('resetIssues'); } + + dispatch('fetchLists'); + dispatch('resetIssues'); }, fetchEpicsSwimlanes({ state, commit }, { fetchNext = false } = {}) { @@ -221,38 +215,30 @@ export default { commit(types.SET_SHOW_LABELS, val); }, - updateListWipLimit({ commit, getters, dispatch }, { maxIssueCount, listId }) { - if (getters.shouldUseGraphQL) { - return gqlClient - .mutate({ - mutation: listUpdateLimitMetricsMutation, - variables: { - input: { - listId, - maxIssueCount, - }, + updateListWipLimit({ commit, dispatch }, { maxIssueCount, listId }) { + return gqlClient + .mutate({ + mutation: listUpdateLimitMetricsMutation, + variables: { + input: { + listId, + maxIssueCount, }, - }) - .then(({ data }) => { - if (data?.boardListUpdateLimitMetrics?.errors.length) { - throw new Error(); - } + }, + }) + .then(({ data }) => { + if (data?.boardListUpdateLimitMetrics?.errors.length) { + throw new Error(); + } - commit(types.UPDATE_LIST_SUCCESS, { - listId, - list: data.boardListUpdateLimitMetrics?.list, - }); - }) - .catch(() => { - dispatch('handleUpdateListFailure'); + commit(types.UPDATE_LIST_SUCCESS, { + listId, + list: data.boardListUpdateLimitMetrics?.list, }); - } - - return axios.put(`${boardsStoreEE.store.state.endpoints.listsEndpoint}/${listId}`, { - list: { - max_issue_count: maxIssueCount, - }, - }); + }) + .catch(() => { + dispatch('handleUpdateListFailure'); + }); }, fetchItemsForList: ( @@ -316,10 +302,6 @@ export default { ); dispatch('fetchEpicsSwimlanes'); dispatch('fetchLists'); - } else if (!gon.features.graphqlBoardLists) { - historyPushState(removeParams(['group_by']), window.location.href, true); - boardsStore.create(); - eventHub.$emit('initialBoardLoad'); } else { historyPushState(removeParams(['group_by']), window.location.href, true); } diff --git a/ee/app/assets/javascripts/boards/stores/boards_store_ee.js b/ee/app/assets/javascripts/boards/stores/boards_store_ee.js index 2585ff1cd380b3639fde79829e2b6d3bd9104eeb..6da66c0c68f9d3d859f63ef2c05b0a67a3ee4802 100644 --- a/ee/app/assets/javascripts/boards/stores/boards_store_ee.js +++ b/ee/app/assets/javascripts/boards/stores/boards_store_ee.js @@ -57,9 +57,6 @@ class BoardsStoreEE { this.store.scopedLabels = { enabled: parseBoolean(scopedLabels), }; - if (!gon.features.graphqlBoardLists) { - this.initBoardFilters(); - } } }; diff --git a/ee/app/assets/javascripts/boards/stores/getters.js b/ee/app/assets/javascripts/boards/stores/getters.js index 379bf68a813c41a03804da596c83cd08cdd061ed..a57282bcf161c3f5604a30bfb9fea824d17de45f 100644 --- a/ee/app/assets/javascripts/boards/stores/getters.js +++ b/ee/app/assets/javascripts/boards/stores/getters.js @@ -54,8 +54,4 @@ export default { isEpicBoard: (state) => { return state.issuableType === issuableTypes.epic; }, - - shouldUseGraphQL: (state) => { - return state.isShowingEpicsSwimlanes || gon?.features?.graphqlBoardLists; - }, }; diff --git a/ee/spec/features/boards/group_boards/multiple_boards_spec.rb b/ee/spec/features/boards/group_boards/multiple_boards_spec.rb index 0ecf40a733d2c0fdc40eea1ec01a2e29fe1aaf9c..65f73d356c4565f5f9a5ab3cfc6f31e72fbc33de 100644 --- a/ee/spec/features/boards/group_boards/multiple_boards_spec.rb +++ b/ee/spec/features/boards/group_boards/multiple_boards_spec.rb @@ -61,13 +61,4 @@ it_behaves_like 'multiple issue boards' end - - context 'when graphql_board_lists FF disabled' do - before do - stub_feature_flags(graphql_board_lists: false) - stub_licensed_features(multiple_group_issue_boards: true) - end - - it_behaves_like 'multiple issue boards' - end end diff --git a/ee/spec/features/boards/group_boards/user_edits_issues_spec.rb b/ee/spec/features/boards/group_boards/user_edits_issues_spec.rb deleted file mode 100644 index 924c18c8d297018a3ef54d09110cbd8f98a8d1b0..0000000000000000000000000000000000000000 --- a/ee/spec/features/boards/group_boards/user_edits_issues_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -# To be removed as :graphql_board_lists gets removed -# https://gitlab.com/gitlab-org/gitlab/-/issues/248908 - -require 'spec_helper' - -RSpec.describe 'label issues', :js do - include BoardHelpers - - let(:user) { create(:user) } - let(:group) { create(:group, :public) } - let(:project) { create(:project, :public, namespace: group) } - let(:board) { create(:board, group: group) } - let!(:development) { create(:label, project: project, name: 'Development') } - let!(:issue) { create(:labeled_issue, project: project, labels: [development]) } - let!(:list) { create(:list, board: board, label: development, position: 0) } - - before do - stub_licensed_features(multiple_group_issue_boards: true) - # stubbing until sidebar work is done: https://gitlab.com/gitlab-org/gitlab/-/issues/230711 - stub_feature_flags(graphql_board_lists: false) - group.add_maintainer(user) - - sign_in(user) - - visit group_boards_path(group) - wait_for_requests - end - - it 'adds a new group label from sidebar' do - card = find('.board:nth-child(2)').first('.board-card') - click_card(card) - - page.within '.right-sidebar .labels' do - click_link 'Edit' - click_link 'Create group label' - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - - # We need to hover before clicking to trigger - # dropdown repositioning so that the click isn't flaky - create_button = find_button('Create') - create_button.hover - create_button.click - end - - page.within '.labels' do - expect(page).to have_link 'test label' - end - end -end diff --git a/ee/spec/features/boards/sidebar_deprecated_spec.rb b/ee/spec/features/boards/sidebar_deprecated_spec.rb deleted file mode 100644 index d85d09f7156f24ea19175058b03bf6021ddf2fe2..0000000000000000000000000000000000000000 --- a/ee/spec/features/boards/sidebar_deprecated_spec.rb +++ /dev/null @@ -1,382 +0,0 @@ -# frozen_string_literal: true - -# To be removed as :graphql_board_lists gets removed -# https://gitlab.com/gitlab-org/gitlab/-/issues/248908 - -require 'spec_helper' - -RSpec.describe 'Issue Boards', :js do - include BoardHelpers - - let_it_be(:user) { create(:user) } - let_it_be(:user2) { create(:user) } - let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, :public, group: group) } - let_it_be(:milestone) { create(:milestone, project: project) } - let_it_be(:development) { create(:label, project: project, name: 'Development') } - let_it_be(:stretch) { create(:label, project: project, name: 'Stretch') } - let_it_be(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], weight: 3, relative_position: 2) } - let_it_be(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } - let_it_be(:board) { create(:board, project: project) } - let_it_be(:list) { create(:list, board: board, label: development, position: 0) } - let_it_be(:scoped_label_1) { create(:label, project: project, name: 'Scoped1::Label1') } - let_it_be(:scoped_label_2) { create(:label, project: project, name: 'Scoped2::Label2') } - - let(:card1) { find('.board:nth-child(2)').find('.board-card:nth-child(2)') } - let(:card2) { find('.board:nth-child(2)').find('.board-card:nth-child(1)') } - - before do - stub_feature_flags(graphql_board_lists: false) - stub_licensed_features(multiple_issue_assignees: true) - - project.add_maintainer(user) - project.team.add_developer(user2) - - sign_in user - - visit project_board_path(project, board) - wait_for_requests - end - - context 'assignee', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078' do - it 'updates the issues assignee' do - click_card(card2) - - page.within('.assignee') do - click_button('Edit') - wait_for_requests - - assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - first('.gl-avatar-labeled').click - end - - click_button('Apply') - wait_for_requests - - expect(page).to have_content(assignee) - end - - expect(card2).to have_selector('.avatar') - end - - it 'adds multiple assignees' do - click_card(card1) - - page.within('.assignee') do - click_button('Edit') - wait_for_requests - - assignee = all('.gl-avatar-labeled')[1].find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - find('[data-testid="unassign"]').click - - all('.gl-avatar-labeled')[0].click - all('.gl-avatar-labeled')[1].click - end - - click_button('Apply') - wait_for_requests - - aggregate_failures do - expect(page).to have_link(nil, title: user.name) - expect(page).to have_link(nil, title: assignee) - end - end - - expect(card1.all('.avatar').length).to eq(2) - end - - it 'removes the assignee' do - click_card(card1) - - page.within('.assignee') do - click_button('Edit') - - page.within('.dropdown-menu-user') do - find('[data-testid="unassign"]').click - end - - click_button('Apply') - - expect(page).to have_content('None') - end - - expect(card1).not_to have_selector('.avatar') - end - - it 'assignees to current user' do - click_card(card2) - - page.within('.assignee') do - expect(page).to have_content('None') - - click_button 'assign yourself' - - wait_for_requests - - expect(page).to have_content(user.name) - end - - expect(card2).to have_selector('.avatar') - end - - it 'updates assignee dropdown' do - click_card(card2) - - page.within('.assignee') do - click_button('Edit') - wait_for_requests - - assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - first('.gl-avatar-labeled').click - end - - click_button('Apply') - wait_for_requests - - expect(page).to have_content(assignee) - end - - click_card(card1) - - page.within('.assignee') do - click_button('Edit') - - expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon') - end - end - end - - context 'epic' do - before do - stub_licensed_features(epics: true) - group.add_owner(user) - - visit project_board_path(project, board) - wait_for_requests - end - - context 'when the issue is not associated with an epic' do - it 'displays `None` for value of epic' do - click_card(card1) - - expect(find('.js-epic-label').text).to have_content('None') - end - end - - context 'when the issue is associated with an epic' do - let(:epic1) { create(:epic, group: group, title: 'Foo') } - let!(:epic2) { create(:epic, group: group, title: 'Bar') } - let!(:epic_issue) { create(:epic_issue, issue: issue1, epic: epic1) } - - it 'displays name of epic and links to it' do - click_card(card1) - - expect(find('.js-epic-label')).to have_link(epic1.title, href: epic_path(epic1)) - end - - it 'updates the epic associated with the issue' do - click_card(card1) - - page.within('.js-epic-block') do - page.find('.sidebar-dropdown-toggle').click - wait_for_requests - - find('.gl-new-dropdown-item', text: epic2.title).click - wait_for_requests - - expect(page.find('.value')).to have_content(epic2.title) - end - - # Ensure that boards_store is also updated the epic associated with the issue. - click_card(card1) - - click_card(card1) - - expect(find('.js-epic-label')).to have_content(epic2.title) - end - end - end - - context 'weight' do - it 'displays weight async' do - click_card(card1) - - expect(find('.js-weight-weight-label').text).to have_content(issue1.weight) - end - - it 'updates weight in sidebar to 1' do - click_card(card1) - - page.within '.weight' do - click_link 'Edit' - find('.block.weight input').send_keys 1, :enter - - page.within '.value' do - expect(page).to have_content '1' - end - end - - # Ensure the request was sent and things are persisted - visit project_board_path(project, board) - wait_for_requests - - click_card(card1) - - page.within '.weight .value' do - expect(page).to have_content '1' - end - end - - it 'updates weight in sidebar to no weight' do - click_card(card1) - - page.within '.weight' do - click_link 'remove weight' - - page.within '.value' do - expect(page).to have_content 'None' - end - end - - # Ensure the request was sent and things are persisted - visit project_board_path(project, board) - wait_for_requests - - click_card(card1) - - page.within '.weight .value' do - expect(page).to have_content 'None' - end - end - - context 'unlicensed' do - before do - stub_licensed_features(issue_weights: false) - visit project_board_path(project, board) - wait_for_requests - end - - it 'hides weight' do - click_card(card1) - - expect(page).not_to have_selector('.js-weight-weight-label') - end - end - end - - context 'scoped labels' do - before do - stub_licensed_features(scoped_labels: true) - - visit project_board_path(project, board) - wait_for_requests - end - - it 'adds multiple scoped labels' do - click_card(card1) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - click_link scoped_label_1.title - - wait_for_requests - - click_link scoped_label_2.title - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - aggregate_failures do - expect(page).to have_selector('.gl-label-scoped', count: 2) - expect(page).to have_content(scoped_label_1.scoped_label_key) - expect(page).to have_content(scoped_label_1.scoped_label_value) - expect(page).to have_content(scoped_label_2.scoped_label_key) - expect(page).to have_content(scoped_label_2.scoped_label_value) - end - end - end - end - - context 'with scoped label assigned' do - let!(:issue3) { create(:labeled_issue, project: project, labels: [development, scoped_label_1, scoped_label_2], relative_position: 3) } - let(:card3) { find('.board:nth-child(2)').find('.board-card:nth-child(1)') } - - before do - stub_licensed_features(scoped_labels: true) - - visit project_board_path(project, board) - wait_for_requests - end - - it 'removes existing scoped label' do - click_card(card3) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - click_link scoped_label_2.title - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - aggregate_failures do - expect(page).to have_selector('.gl-label-scoped', count: 1) - expect(page).not_to have_content(scoped_label_1.scoped_label_value) - expect(page).to have_content(scoped_label_2.scoped_label_key) - expect(page).to have_content(scoped_label_2.scoped_label_value) - end - end - end - - aggregate_failures do - expect(card3).to have_selector('.gl-label-scoped', count: 1) - expect(card3).not_to have_content(scoped_label_1.scoped_label_key) - expect(card3).not_to have_content(scoped_label_1.scoped_label_value) - expect(card3).to have_content(scoped_label_2.scoped_label_key) - expect(card3).to have_content(scoped_label_2.scoped_label_value) - end - end - end - end - - context 'when opening sidebars' do - let(:settings_button) { find('.js-board-settings-button') } - - it 'closes card sidebar when opening settings sidebar' do - click_card(card1) - - expect(page).to have_selector('.right-sidebar') - - settings_button.click - - expect(page).to have_selector('.js-board-settings-sidebar') - expect(page).not_to have_selector('.right-sidebar') - end - - it 'closes settings sidebar when opening card sidebar' do - settings_button.click - - expect(page).to have_selector('.js-board-settings-sidebar') - - click_card(card1) - - expect(page).to have_selector('.right-sidebar') - expect(page).not_to have_selector('.js-board-settings-sidebar') - end - end -end diff --git a/ee/spec/features/boards/user_adds_lists_to_board_spec.rb b/ee/spec/features/boards/user_adds_lists_to_board_spec.rb index 6671f5869592aad02931b26ea973fa17d8fcc573..d01d70a385aef21f7f182f82462929728d0f2a78 100644 --- a/ee/spec/features/boards/user_adds_lists_to_board_spec.rb +++ b/ee/spec/features/boards/user_adds_lists_to_board_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe 'User adds milestone lists', :js do - using RSpec::Parameterized::TableSyntax - let_it_be(:group) { create(:group, :nested) } let_it_be(:project) { create(:project, :public, namespace: group) } let_it_be(:group_board) { create(:board, group: group) } @@ -25,11 +23,8 @@ group.add_owner(user) end - where(:board_type, :graphql_board_lists_enabled) do - :project | true - :project | false - :group | true - :group | false + where(:board_type) do + [[:project], [:group]] end with_them do @@ -43,10 +38,6 @@ set_cookie('sidebar_collapsed', 'true') - stub_feature_flags( - graphql_board_lists: graphql_board_lists_enabled - ) - if board_type == :project visit project_board_path(project, project_board) elsif board_type == :group diff --git a/ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb b/ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb index 90196a374b57e6107546d823d34ead2d64a57a79..0b746c0c3fe1553e952df671fefa0dbf2224bac0 100644 --- a/ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb +++ b/ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb @@ -145,14 +145,6 @@ let(:issue_title_selector) { '.board-card .board-card-title' } it_behaves_like 'filters by iteration' - - context 'when graphql_board_lists is disabled' do - before do - stub_feature_flags(graphql_board_lists: false) - end - - it_behaves_like 'filters by iteration' - end end context 'group board' do diff --git a/ee/spec/frontend/boards/components/board_add_new_column_spec.js b/ee/spec/frontend/boards/components/board_add_new_column_spec.js index 004ddf3b4263308c94f0cc893b0955118529af25..3dac98efc658da10d50e86fd18eca3a33d202f87 100644 --- a/ee/spec/frontend/boards/components/board_add_new_column_spec.js +++ b/ee/spec/frontend/boards/components/board_add_new_column_spec.js @@ -15,7 +15,6 @@ Vue.use(Vuex); describe('BoardAddNewColumn', () => { let wrapper; - let shouldUseGraphQL; const selectItem = (id) => { wrapper.findByTestId('selectItem').vm.$emit('change', id); @@ -59,7 +58,6 @@ describe('BoardAddNewColumn', () => { ...actions, }, getters: { - shouldUseGraphQL: () => shouldUseGraphQL, getListByTypeId: () => getListByTypeId, isEpicBoard: () => false, }, @@ -103,10 +101,6 @@ describe('BoardAddNewColumn', () => { radio.vm.$emit('change', type); }; - beforeEach(() => { - shouldUseGraphQL = true; - }); - it('clicking cancel hides the form', () => { const setAddColumnFormVisibility = jest.fn(); mountComponent({ diff --git a/ee/spec/frontend/boards/components/board_content_spec.js b/ee/spec/frontend/boards/components/board_content_spec.js index 1a776ed3a7a46579f6dc90f673c5c4dc2883685b..fa746bebdd389584f6625f0af48200f700ef98ef 100644 --- a/ee/spec/frontend/boards/components/board_content_spec.js +++ b/ee/spec/frontend/boards/components/board_content_spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils'; +import EpicBoardContentSidebar from 'ee/boards/components/epic_board_content_sidebar.vue'; import BoardContent from '~/boards/components/board_content.vue'; import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue'; import { createStore } from '~/boards/stores'; @@ -35,20 +36,22 @@ describe('ee/BoardContent', () => { }); describe.each` - licenseEnabled | state | result - ${true} | ${{ isShowingEpicsSwimlanes: true }} | ${true} - ${true} | ${{ isShowingEpicsSwimlanes: false }} | ${false} - ${false} | ${{ isShowingEpicsSwimlanes: true }} | ${false} - ${false} | ${{ isShowingEpicsSwimlanes: false }} | ${false} - `('with licenseEnabled=$licenseEnabled and state=$state', ({ licenseEnabled, state, result }) => { + state | resultIssue | resultEpic + ${{ isShowingEpicsSwimlanes: true, issuableType: 'issue' }} | ${true} | ${false} + ${{ isShowingEpicsSwimlanes: false, issuableType: 'issue' }} | ${true} | ${false} + ${{ isShowingEpicsSwimlanes: false, issuableType: 'epic' }} | ${false} | ${true} + `('with state=$state', ({ state, resultIssue, resultEpic }) => { beforeEach(() => { - gon.licensed_features.swimlanes = licenseEnabled; Object.assign(store.state, state); createComponent(); }); - it(`renders BoardContentSidebar = ${result}`, () => { - expect(wrapper.find(BoardContentSidebar).exists()).toBe(result); + it(`renders BoardContentSidebar = ${resultIssue}`, () => { + expect(wrapper.find(BoardContentSidebar).exists()).toBe(resultIssue); + }); + + it(`renders EpicBoardContentSidebar = ${resultEpic}`, () => { + expect(wrapper.find(EpicBoardContentSidebar).exists()).toBe(resultEpic); }); }); }); diff --git a/ee/spec/frontend/boards/components/board_list_selector/board_list_selector_spec.js b/ee/spec/frontend/boards/components/board_list_selector/board_list_selector_spec.js index 68e8fd571018baac2099423414052d8ac171a41e..bda0b7cc48030c22a2429f11bd46232d40db2c86 100644 --- a/ee/spec/frontend/boards/components/board_list_selector/board_list_selector_spec.js +++ b/ee/spec/frontend/boards/components/board_list_selector/board_list_selector_spec.js @@ -11,11 +11,6 @@ import axios from '~/lib/utils/axios_utils'; jest.mock('~/flash'); describe('BoardListSelector', () => { - global.gon.features = { - ...(global.gon.features || {}), - graphqlBoardLists: false, - }; - const dummyEndpoint = `${TEST_HOST}/users.json`; const createComponent = () => @@ -93,19 +88,7 @@ describe('BoardListSelector', () => { }); describe('handleItemClick', () => { - it('graphqlBoardLists FF off - creates new list in a store instance', () => { - jest.spyOn(vm.store, 'new').mockReturnValue({}); - const assignee = mockAssigneesList[0]; - - expect(vm.store.findList('title', assignee.name)).not.toBeDefined(); - vm.handleItemClick(assignee); - - expect(vm.store.new).toHaveBeenCalledWith(expect.any(Object)); - }); - - it('graphqlBoardLists FF on - creates new list in a store instance', () => { - global.gon.features.graphqlBoardLists = true; - + it('creates new list in a store instance', () => { jest.spyOn(vm.vuexStore, 'dispatch').mockReturnValue({}); const assignee = mockAssigneesList[0]; diff --git a/ee/spec/frontend/boards/components/board_settings_sidebar_spec.js b/ee/spec/frontend/boards/components/board_settings_sidebar_spec.js index bd5c969712dd7a5f428e98d690e93f9007dbf54f..0cf13c4aeca7c3df1780adc7663d6f9ce46b4092 100644 --- a/ee/spec/frontend/boards/components/board_settings_sidebar_spec.js +++ b/ee/spec/frontend/boards/components/board_settings_sidebar_spec.js @@ -1,39 +1,33 @@ -import '~/boards/models/list'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; import Vuex from 'vuex'; import BoardSettingsListTypes from 'ee_component/boards/components/board_settings_list_types.vue'; import BoardSettingsWipLimit from 'ee_component/boards/components/board_settings_wip_limit.vue'; +import { mockLabelList, mockMilestoneList } from 'jest/boards/mock_data'; import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue'; import { LIST } from '~/boards/constants'; -import boardsStore from '~/boards/stores/boards_store'; import getters from '~/boards/stores/getters'; -const localVue = createLocalVue(); - -localVue.use(Vuex); +Vue.use(Vuex); describe('ee/BoardSettingsSidebar', () => { let wrapper; let storeActions; - const labelTitle = 'test'; - const labelColor = '#FFFF'; - const listId = 1; - let mock; - const createComponent = (actions = {}, isWipLimitsOn = false) => { + const createComponent = ({ actions = {}, isWipLimitsOn = false, list = {} }) => { storeActions = actions; + const boardLists = { + [list.id]: { ...list, maxIssueCount: 0 }, + }; const store = new Vuex.Store({ - state: { sidebarType: LIST, activeId: listId }, + state: { sidebarType: LIST, activeId: list.id, boardLists }, getters, actions: storeActions, }); wrapper = shallowMount(BoardSettingsSidebar, { store, - localVue, provide: { glFeatures: { wipLimits: isWipLimitsOn, @@ -47,41 +41,18 @@ describe('ee/BoardSettingsSidebar', () => { }); }; - beforeEach(() => { - mock = new MockAdapter(axios); - boardsStore.create(); - }); - afterEach(() => { - mock.restore(); wrapper.destroy(); }); it('confirms we render BoardSettingsSidebarWipLimit', () => { - boardsStore.addList({ - id: listId, - label: { title: labelTitle, color: labelColor }, - max_issue_count: 0, - list_type: 'label', - }); - - createComponent({}, true); + createComponent({ list: mockLabelList, isWipLimitsOn: true }); expect(wrapper.find(BoardSettingsWipLimit).exists()).toBe(true); }); it('confirms we render BoardSettingsListTypes', () => { - boardsStore.addList({ - id: 1, - milestone: { - webUrl: 'https://gitlab.com/h5bp/html5-boilerplate/-/milestones/1', - title: 'Backlog', - }, - max_issue_count: 1, - list_type: 'milestone', - }); - - createComponent(); + createComponent({ list: mockMilestoneList }); expect(wrapper.find(BoardSettingsListTypes).exists()).toBe(true); }); diff --git a/ee/spec/frontend/boards/components/board_settings_wip_limit_spec.js b/ee/spec/frontend/boards/components/board_settings_wip_limit_spec.js index 4012eec54fbe4e4f76d7e790fff9e50688eed97c..d616b4ab00f0365843fadbf84cc69bc829fd1309 100644 --- a/ee/spec/frontend/boards/components/board_settings_wip_limit_spec.js +++ b/ee/spec/frontend/boards/components/board_settings_wip_limit_spec.js @@ -1,35 +1,20 @@ -import '~/boards/models/list'; import { GlFormInput } from '@gitlab/ui'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; +import { shallowMount } from '@vue/test-utils'; import { noop } from 'lodash'; +import Vue from 'vue'; import Vuex from 'vuex'; import BoardSettingsWipLimit from 'ee_component/boards/components/board_settings_wip_limit.vue'; import waitForPromises from 'helpers/wait_for_promises'; -import boardsStore from '~/boards/stores/boards_store'; +import { mockLabelList } from 'jest/boards/mock_data'; -const localVue = createLocalVue(); - -localVue.use(Vuex); +Vue.use(Vuex); describe('BoardSettingsWipLimit', () => { let wrapper; let storeActions; - const labelTitle = 'test'; - const labelColor = '#FFFF'; - const listId = 1; + const listId = mockLabelList.id; const currentWipLimit = 1; // Needs to be other than null to trigger requests - let mock; - - const addList = (maxIssueCount = 0) => { - boardsStore.addList({ - id: listId, - label: { title: labelTitle, color: labelColor }, - max_issue_count: maxIssueCount, - list_type: 'label', - }); - }; + const clickEdit = () => wrapper.find('.js-edit-button').vm.$emit('click'); const findRemoveWipLimit = () => wrapper.find('.js-remove-limit'); const findWipLimit = () => wrapper.find('.js-wip-limit'); @@ -46,13 +31,11 @@ describe('BoardSettingsWipLimit', () => { const store = new Vuex.Store({ state: vuexState, actions: storeActions, - getters: { shouldUseGraphQL: () => false }, }); wrapper = shallowMount(BoardSettingsWipLimit, { propsData: props, store, - localVue, data() { return localState; }, @@ -69,13 +52,7 @@ describe('BoardSettingsWipLimit', () => { } }; - beforeEach(() => { - boardsStore.create(); - mock = new MockAdapter(axios); - }); - afterEach(() => { - mock.restore(); jest.restoreAllMocks(); wrapper.destroy(); }); @@ -83,25 +60,28 @@ describe('BoardSettingsWipLimit', () => { describe('when activeList is present', () => { describe('when activeListWipLimit is 0', () => { it('renders "None" in the block', () => { - createComponent({ vuexState: { activeId: listId } }); + createComponent({ + vuexState: { + activeId: listId, + }, + }); expect(findWipLimit().text()).toBe('None'); }); }); - describe('when activeId is greater than 0', () => { - afterEach(() => { - boardsStore.removeList(listId); - }); - + describe('when activeListWipLimit is greater than 0', () => { it.each` num | expected ${1} | ${'1 issue'} ${11} | ${'11 issues'} `('it renders $num', ({ num, expected }) => { - addList(4); - - createComponent({ vuexState: { activeId: num }, props: { maxIssueCount: num } }); + createComponent({ + vuexState: { + activeId: listId, + }, + props: { maxIssueCount: num }, + }); expect(findWipLimit().text()).toBe(expected); }); @@ -112,7 +92,9 @@ describe('BoardSettingsWipLimit', () => { const maxIssueCount = 4; beforeEach(async () => { createComponent({ - vuexState: { activeId: listId }, + vuexState: { + activeId: listId, + }, actions: { updateListWipLimit: noop }, props: { maxIssueCount }, }); @@ -137,15 +119,14 @@ describe('BoardSettingsWipLimit', () => { describe('remove limit', () => { describe('when wipLimit is set', () => { + const spy = jest.fn().mockResolvedValue({ + data: { boardListUpdateLimitMetrics: { list: { maxIssueCount: 0 } } }, + }); beforeEach(() => { - addList(4); - - const spy = jest.fn().mockResolvedValue({ - config: { data: JSON.stringify({ list: { max_issue_count: 0 } }) }, - }); - createComponent({ - vuexState: { activeId: listId }, + vuexState: { + activeId: listId, + }, actions: { updateListWipLimit: spy }, props: { maxIssueCount: 4 }, }); @@ -156,18 +137,22 @@ describe('BoardSettingsWipLimit', () => { findRemoveWipLimit().vm.$emit('click'); + await waitForPromises(); await wrapper.vm.$nextTick(); - // WARNING: https://gitlab.com/gitlab-org/gitlab/-/issues/232573 - expect(boardsStore.findList('id', listId).maxIssueCount).toBe(0); + expect(spy).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ listId, maxIssueCount: 0 }), + ); }); }); describe('when wipLimit is not set', () => { beforeEach(() => { - addList(); - - createComponent({ vuexState: { activeId: listId }, actions: { updateListWipLimit: noop } }); + createComponent({ + vuexState: { activeId: listId }, + actions: { updateListWipLimit: noop }, + }); }); it('does not render the remove limit button', () => { @@ -177,14 +162,6 @@ describe('BoardSettingsWipLimit', () => { }); describe('when edit is true', () => { - beforeEach(() => { - addList(2); - }); - - afterEach(() => { - boardsStore.removeList(listId); - }); - describe.each` blurMethod ${'enter'} @@ -193,10 +170,12 @@ describe('BoardSettingsWipLimit', () => { describe(`when blur is triggered by ${blurMethod}`, () => { it('calls updateListWipLimit', async () => { const spy = jest.fn().mockResolvedValue({ - config: { data: JSON.stringify({ list: { max_issue_count: '4' } }) }, + data: { boardListUpdateLimitMetrics: { list: { maxIssueCount: 4 } } }, }); createComponent({ - vuexState: { activeId: listId }, + vuexState: { + activeId: listId, + }, actions: { updateListWipLimit: spy }, localState: { edit: true, currentWipLimit }, }); @@ -209,10 +188,12 @@ describe('BoardSettingsWipLimit', () => { }); describe('when component wipLimit and List.maxIssueCount are equal', () => { - it('doesnt call updateListWipLimit', async () => { + it('does not call updateListWipLimit', async () => { const spy = jest.fn().mockResolvedValue({}); createComponent({ - vuexState: { activeId: listId }, + vuexState: { + activeId: listId, + }, actions: { updateListWipLimit: spy }, localState: { edit: true, currentWipLimit: 2 }, props: { maxIssueCount: 2 }, @@ -227,7 +208,7 @@ describe('BoardSettingsWipLimit', () => { }); describe('when currentWipLimit is null', () => { - it('doesnt call updateListWipLimit', async () => { + it('does not call updateListWipLimit', async () => { const spy = jest.fn().mockResolvedValue({}); createComponent({ vuexState: { activeId: listId }, @@ -249,9 +230,12 @@ describe('BoardSettingsWipLimit', () => { beforeEach(() => { const spy = jest.fn().mockResolvedValue({}); createComponent({ - vuexState: { activeId: listId }, + vuexState: { + activeId: listId, + }, actions: { updateListWipLimit: spy }, localState: { edit: true, currentWipLimit: maxIssueCount }, + props: { maxIssueCount }, }); triggerBlur(blurMethod); @@ -260,14 +244,7 @@ describe('BoardSettingsWipLimit', () => { }); it('sets activeWipLimit to new maxIssueCount value', () => { - /* - * DANGER: bad coupling to the computed prop of the component because the - * computed prop relys on the list from boardStore, for now this is the way around - * stale values from boardsStore being updated, when we move List and BoardsStore to Vuex - * or Graphql we will be able to query the DOM for the new value. - */ - - expect(boardsStore.findList('id', 1).maxIssueCount).toBe(maxIssueCount); + expect(findWipLimit().text()).toContain(maxIssueCount); }); it('toggles GlFormInput on blur', () => { diff --git a/ee/spec/frontend/boards/stores/actions_spec.js b/ee/spec/frontend/boards/stores/actions_spec.js index 69dd8f7cb01abe6f1cf79097e1aad80bde47a335..4034f9cc747c367d04af4297c392464397cd3d59 100644 --- a/ee/spec/frontend/boards/stores/actions_spec.js +++ b/ee/spec/frontend/boards/stores/actions_spec.js @@ -112,12 +112,7 @@ describe('setFilters', () => { }); describe('performSearch', () => { - it('should dispatch setFilters action', (done) => { - testAction(actions.performSearch, {}, {}, [], [{ type: 'setFilters', payload: {} }], done); - }); - - it('should dispatch setFilters, fetchLists and resetIssues action when graphqlBoardLists FF is on', async () => { - window.gon = { features: { graphqlBoardLists: true } }; + it('should dispatch setFilters, fetchLists and resetIssues action', async () => { const getters = { isSwimlanesOn: false }; await testAction({ @@ -139,9 +134,9 @@ describe('performSearch', () => { expectedActions: [ { type: 'setFilters', payload: {} }, { type: 'resetEpics' }, - { type: 'resetIssues' }, { type: 'fetchEpicsSwimlanes' }, { type: 'fetchLists' }, + { type: 'resetIssues' }, ], }); }); @@ -464,7 +459,6 @@ describe('setShowLabels', () => { describe('updateListWipLimit', () => { let storeMock; - const getters = { shouldUseGraphQL: false }; beforeEach(() => { storeMock = { @@ -483,26 +477,9 @@ describe('updateListWipLimit', () => { jest.restoreAllMocks(); }); - it('axios - should call the correct url', () => { - const maxIssueCount = 0; - const activeId = 1; - - return actions - .updateListWipLimit({ state: { activeId }, getters }, { maxIssueCount, listId: activeId }) - .then(() => { - expect(axios.put).toHaveBeenCalledWith( - `${boardsStoreEE.store.state.endpoints.listsEndpoint}/${activeId}`, - { - list: { max_issue_count: maxIssueCount }, - }, - ); - }); - }); - - it('graphql - commit UPDATE_LIST_SUCCESS mutation on success', () => { + it('commit UPDATE_LIST_SUCCESS mutation on success', () => { const maxIssueCount = 0; const activeId = 1; - getters.shouldUseGraphQL = true; jest.spyOn(gqlClient, 'mutate').mockResolvedValue({ data: { boardListUpdateLimitMetrics: { @@ -517,7 +494,7 @@ describe('updateListWipLimit', () => { return testAction( actions.updateListWipLimit, { maxIssueCount, listId: activeId }, - { isShowingEpicsSwimlanes: true, ...getters }, + { isShowingEpicsSwimlanes: true }, [ { type: types.UPDATE_LIST_SUCCESS, @@ -533,16 +510,15 @@ describe('updateListWipLimit', () => { ); }); - it('graphql - dispatch handleUpdateListFailure on failure', () => { + it('dispatch handleUpdateListFailure on failure', () => { const maxIssueCount = 0; const activeId = 1; - getters.shouldUseGraphQL = true; jest.spyOn(gqlClient, 'mutate').mockResolvedValue(Promise.reject()); return testAction( actions.updateListWipLimit, { maxIssueCount, listId: activeId }, - { isShowingEpicsSwimlanes: true, ...getters }, + { isShowingEpicsSwimlanes: true }, [], [{ type: 'handleUpdateListFailure' }], ); diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6863e64745f7e6ba2ecf07ded880e2544b127c8c..67ea70229add6d7642fb37c37445f6a735e7bdd9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3626,9 +3626,6 @@ msgstr "" msgid "An error occurred while fetching terraform reports." msgstr "" -msgid "An error occurred while fetching the board lists. Please try again." -msgstr "" - msgid "An error occurred while fetching the job log." msgstr "" @@ -5513,9 +5510,6 @@ msgid_plural "Boards|Blocked by %{blockedByCount} %{issuableType}s" msgstr[0] "" msgstr[1] "" -msgid "Boards|Board" -msgstr "" - msgid "Boards|Collapse" msgstr "" diff --git a/spec/features/boards/multi_select_spec.rb b/spec/features/boards/multi_select_spec.rb index 057464326fa0e443260cb1480722de54d8fb8089..9148fb23214484630795df430579bff8c19cae05 100644 --- a/spec/features/boards/multi_select_spec.rb +++ b/spec/features/boards/multi_select_spec.rb @@ -43,12 +43,12 @@ def multi_select(selector, action = 'select') # Multi select drag&drop support is temporarily disabled # https://gitlab.com/gitlab-org/gitlab/-/issues/289797 - stub_feature_flags(graphql_board_lists: false, board_multi_select: project) + stub_feature_flags(board_multi_select: project) sign_in(user) end - context 'with lists' do + xcontext 'with lists' do let(:label1) { create(:label, project: project, name: 'Label 1', description: 'Test') } let(:label2) { create(:label, project: project, name: 'Label 2', description: 'Test') } let!(:list1) { create(:list, board: board, label: label1, position: 0) } diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb index 2f0230c61d891ea2b919a44f9cd2b935072f80ea..fa16f47f69af8848de31d62304eadb0e234e768c 100644 --- a/spec/features/boards/sidebar_labels_spec.rb +++ b/spec/features/boards/sidebar_labels_spec.rb @@ -5,8 +5,9 @@ RSpec.describe 'Project issue boards sidebar labels', :js do include BoardHelpers + let_it_be(:group) { create(:group, :public) } let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, :public) } + let_it_be(:project) { create(:project, :public, namespace: group) } let_it_be(:development) { create(:label, project: project, name: 'Development') } let_it_be(:bug) { create(:label, project: project, name: 'Bug') } let_it_be(:regression) { create(:label, project: project, name: 'Regression') } diff --git a/spec/features/boards/user_adds_lists_to_board_spec.rb b/spec/features/boards/user_adds_lists_to_board_spec.rb index 0db3fe12a3edb657a070cf72beca31720c18cec6..26c310a6f56dab41d3ef4d016ee8400f790c4011 100644 --- a/spec/features/boards/user_adds_lists_to_board_spec.rb +++ b/spec/features/boards/user_adds_lists_to_board_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe 'User adds lists', :js do - using RSpec::Parameterized::TableSyntax - let_it_be(:group) { create(:group, :nested) } let_it_be(:project) { create(:project, :public, namespace: group) } let_it_be(:group_board) { create(:board, group: group) } @@ -27,11 +25,8 @@ group.add_owner(user) end - where(:board_type, :graphql_board_lists_enabled) do - :project | true - :project | false - :group | true - :group | false + where(:board_type) do + [[:project], [:group]] end with_them do @@ -40,10 +35,6 @@ set_cookie('sidebar_collapsed', 'true') - stub_feature_flags( - graphql_board_lists: graphql_board_lists_enabled - ) - if board_type == :project visit project_board_path(project, project_board) elsif board_type == :group @@ -53,14 +44,12 @@ wait_for_all_requests end - it 'creates new column for label containing labeled issue' do + it 'creates new column for label containing labeled issue', :aggregate_failures do click_button 'Create list' wait_for_all_requests select_label(group_label) - wait_for_all_requests - expect(page).to have_selector('.board', text: group_label.title) expect(find('.board:nth-child(2) .board-card')).to have_content(issue.title) end diff --git a/spec/features/groups/board_sidebar_spec.rb b/spec/features/groups/board_sidebar_spec.rb index e2dd2fecab7b8240547f132563cac834412cacec..69a6788e4381e9d77bdac63efd05778dd778279f 100644 --- a/spec/features/groups/board_sidebar_spec.rb +++ b/spec/features/groups/board_sidebar_spec.rb @@ -42,30 +42,4 @@ end end end - - context 'when graphql_board_lists FF disabled' do - before do - stub_feature_flags(graphql_board_lists: false) - sign_in(user) - - visit group_board_path(group, board) - wait_for_requests - end - - it 'only shows valid labels for the issue project and group' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - page.within('.selectbox') do - expect(page).to have_content(project_1_label.title) - expect(page).to have_content(group_label.title) - expect(page).not_to have_content(project_2_label.title) - end - end - end - end end diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 378af53dd7936b6d05525279f7d998081566f96f..a9e695ad15bf3998febf2a2c4b3d248bdd3988df 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -214,44 +214,6 @@ end end - context 'issuable sidebar when graphql_board_lists FF disabled' do - let!(:issue) { create(:issue, project: project_1) } - - before do - stub_feature_flags(graphql_board_lists: false) - end - - context 'on project board issue sidebar' do - before do - project_1.add_developer(user) - board = create(:board, project: project_1) - - visit project_board_path(project_1, board) - - wait_for_requests - - find('.board-card').click - end - - it_behaves_like 'assigning labels from sidebar' - end - - context 'on group board issue sidebar' do - before do - parent.add_developer(user) - board = create(:board, group: parent) - - visit group_board_path(parent, board) - - wait_for_requests - - find('.board-card').click - end - - it_behaves_like 'assigning labels from sidebar' - end - end - context 'issuable filtering' do let!(:labeled_issue) { create(:labeled_issue, project: project_1, labels: [grandparent_group_label, parent_group_label, project_label_1]) } let!(:issue) { create(:issue, project: project_1) } diff --git a/spec/frontend/boards/components/board_add_new_column_spec.js b/spec/frontend/boards/components/board_add_new_column_spec.js index 61f210f566b8de091fbc43dc959e7240078f4ed1..5fae1c4359fe824cf451b77a8635b885e65e5071 100644 --- a/spec/frontend/boards/components/board_add_new_column_spec.js +++ b/spec/frontend/boards/components/board_add_new_column_spec.js @@ -48,7 +48,6 @@ describe('Board card layout', () => { ...actions, }, getters: { - shouldUseGraphQL: () => true, getListByLabelId: () => getListByLabelId, }, state: { diff --git a/spec/frontend/boards/components/board_card_deprecated_spec.js b/spec/frontend/boards/components/board_card_deprecated_spec.js index 266cbc7106d80b20cb98878f4440f35467133006..e21a8941bdcd0c2e8fde583557fe5340cd70d0d7 100644 --- a/spec/frontend/boards/components/board_card_deprecated_spec.js +++ b/spec/frontend/boards/components/board_card_deprecated_spec.js @@ -8,7 +8,6 @@ import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import BoardCardDeprecated from '~/boards/components/board_card_deprecated.vue'; import issueCardInner from '~/boards/components/issue_card_inner_deprecated.vue'; -import eventHub from '~/boards/eventhub'; import store from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; import axios from '~/lib/utils/axios_utils'; @@ -165,46 +164,9 @@ describe('BoardCard', () => { expect(boardsStore.detail.issue).toEqual({}); }); - - it('sets detail issue to card issue on mouse up', () => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - - mountComponent(); - - wrapper.trigger('mousedown'); - wrapper.trigger('mouseup'); - - expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', wrapper.vm.issue, false); - expect(boardsStore.detail.list).toEqual(wrapper.vm.list); - }); - - it('resets detail issue to empty if already set', () => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - const [issue] = list.issues; - boardsStore.detail.issue = issue; - mountComponent(); - - wrapper.trigger('mousedown'); - wrapper.trigger('mouseup'); - - expect(eventHub.$emit).toHaveBeenCalledWith('clearDetailIssue', false); - }); }); describe('sidebarHub events', () => { - it('closes all sidebars before showing an issue if no issues are opened', () => { - jest.spyOn(sidebarEventHub, '$emit').mockImplementation(() => {}); - boardsStore.detail.issue = {}; - mountComponent(); - - // sets conditional so that event is emitted. - wrapper.trigger('mousedown'); - - wrapper.trigger('mouseup'); - - expect(sidebarEventHub.$emit).toHaveBeenCalledWith('sidebar.closeAll'); - }); - it('it does not closes all sidebars before showing an issue if an issue is opened', () => { jest.spyOn(sidebarEventHub, '$emit').mockImplementation(() => {}); const [issue] = list.issues; diff --git a/spec/frontend/boards/components/board_card_layout_deprecated_spec.js b/spec/frontend/boards/components/board_card_layout_deprecated_spec.js index 9853c9f434ff1d8881e3f68f762094db8cb8d3df..5a81f3ecc2693931f518b1e40b06b2f73ed0df14 100644 --- a/spec/frontend/boards/components/board_card_layout_deprecated_spec.js +++ b/spec/frontend/boards/components/board_card_layout_deprecated_spec.js @@ -111,18 +111,14 @@ describe('Board card layout', () => { expect(wrapper.vm.showDetail).toBe(false); }); - it("calls 'setActiveId' when 'graphqlBoardLists' feature flag is turned on", async () => { + it("calls 'setActiveId'", async () => { const setActiveId = jest.fn(); createStore({ actions: { setActiveId, }, }); - mountComponent({ - provide: { - glFeatures: { graphqlBoardLists: true }, - }, - }); + mountComponent(); wrapper.trigger('mouseup'); await wrapper.vm.$nextTick(); diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js index 5a799b6388e7e1aea763887ae5e451fcd528193c..bbda544367b0fee2e504a0fb6b5fd3e1e9aa3b90 100644 --- a/spec/frontend/boards/components/board_content_spec.js +++ b/spec/frontend/boards/components/board_content_spec.js @@ -5,7 +5,7 @@ import Draggable from 'vuedraggable'; import Vuex from 'vuex'; import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue'; import getters from 'ee_else_ce/boards/stores/getters'; -import BoardColumnDeprecated from '~/boards/components/board_column_deprecated.vue'; +import BoardColumn from '~/boards/components/board_column.vue'; import BoardContent from '~/boards/components/board_content.vue'; import { mockLists, mockListsWithModel } from '../mock_data'; @@ -33,12 +33,7 @@ describe('BoardContent', () => { }); }; - const createComponent = ({ - state, - props = {}, - graphqlBoardListsEnabled = false, - canAdminList = true, - } = {}) => { + const createComponent = ({ state, props = {}, canAdminList = true } = {}) => { const store = createStore({ ...defaultState, ...state, @@ -51,63 +46,41 @@ describe('BoardContent', () => { }, provide: { canAdminList, - glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled }, }, store, }); }; + beforeEach(() => { + createComponent(); + }); + afterEach(() => { wrapper.destroy(); }); - it('renders a BoardColumnDeprecated component per list', () => { - createComponent(); - - expect(wrapper.findAllComponents(BoardColumnDeprecated)).toHaveLength( - mockListsWithModel.length, - ); + it('renders a BoardColumn component per list', () => { + expect(wrapper.findAllComponents(BoardColumn)).toHaveLength(mockListsWithModel.length); }); it('does not display EpicsSwimlanes component', () => { - createComponent(); - expect(wrapper.find(EpicsSwimlanes).exists()).toBe(false); expect(wrapper.find(GlAlert).exists()).toBe(false); }); - describe('graphqlBoardLists feature flag enabled', () => { + describe('can admin list', () => { beforeEach(() => { - createComponent({ graphqlBoardListsEnabled: true }); - gon.features = { - graphqlBoardLists: true, - }; + createComponent({ canAdminList: true }); }); - describe('can admin list', () => { - beforeEach(() => { - createComponent({ graphqlBoardListsEnabled: true, canAdminList: true }); - }); - - it('renders draggable component', () => { - expect(wrapper.find(Draggable).exists()).toBe(true); - }); - }); - - describe('can not admin list', () => { - beforeEach(() => { - createComponent({ graphqlBoardListsEnabled: true, canAdminList: false }); - }); - - it('does not render draggable component', () => { - expect(wrapper.find(Draggable).exists()).toBe(false); - }); + it('renders draggable component', () => { + expect(wrapper.find(Draggable).exists()).toBe(true); }); }); - describe('graphqlBoardLists feature flag disabled', () => { + describe('can not admin list', () => { beforeEach(() => { - createComponent({ graphqlBoardListsEnabled: false }); + createComponent({ canAdminList: false }); }); it('does not render draggable component', () => { diff --git a/spec/frontend/boards/components/board_filtered_search_spec.js b/spec/frontend/boards/components/board_filtered_search_spec.js index 50f86e92adbe529e8accc14b6abae21c6cda0ff9..dc93890f27a69477e9d42fb1847930912af33264 100644 --- a/spec/frontend/boards/components/board_filtered_search_spec.js +++ b/spec/frontend/boards/components/board_filtered_search_spec.js @@ -2,7 +2,6 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue'; -import { createStore } from '~/boards/stores'; import * as urlUtility from '~/lib/utils/url_utility'; import { __ } from '~/locale'; import FilteredSearchBarRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; @@ -44,6 +43,12 @@ describe('BoardFilteredSearch', () => { ]; const createComponent = ({ initialFilterParams = {} } = {}) => { + store = new Vuex.Store({ + actions: { + performSearch: jest.fn(), + }, + }); + wrapper = shallowMount(BoardFilteredSearch, { provide: { initialFilterParams, fullPath: '' }, store, @@ -55,22 +60,15 @@ describe('BoardFilteredSearch', () => { const findFilteredSearch = () => wrapper.findComponent(FilteredSearchBarRoot); - beforeEach(() => { - // this needed for actions call for performSearch - window.gon = { features: {} }; - }); - afterEach(() => { wrapper.destroy(); }); describe('default', () => { beforeEach(() => { - store = createStore(); + createComponent(); jest.spyOn(store, 'dispatch'); - - createComponent(); }); it('renders FilteredSearch', () => { @@ -103,8 +101,6 @@ describe('BoardFilteredSearch', () => { describe('when searching', () => { beforeEach(() => { - store = createStore(); - createComponent(); jest.spyOn(wrapper.vm, 'performSearch').mockImplementation(); @@ -133,11 +129,9 @@ describe('BoardFilteredSearch', () => { describe('when url params are already set', () => { beforeEach(() => { - store = createStore(); + createComponent({ initialFilterParams: { authorUsername: 'root', labelName: ['label'] } }); jest.spyOn(store, 'dispatch'); - - createComponent({ initialFilterParams: { authorUsername: 'root', labelName: ['label'] } }); }); it('passes the correct props to FilterSearchBar', () => { diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js index 20a08be6c19d19bdda77126ab6bd5ec73c7b004c..60d4d1cb8e3f78aa4b50d1dadb5d95b39b660da9 100644 --- a/spec/frontend/boards/components/board_settings_sidebar_spec.js +++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js @@ -1,36 +1,47 @@ import '~/boards/models/list'; import { GlDrawer, GlLabel } from '@gitlab/ui'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; +import { shallowMount } from '@vue/test-utils'; import { MountingPortal } from 'portal-vue'; +import Vue from 'vue'; import Vuex from 'vuex'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue'; import { inactiveId, LIST } from '~/boards/constants'; -import { createStore } from '~/boards/stores'; -import boardsStore from '~/boards/stores/boards_store'; +import actions from '~/boards/stores/actions'; +import getters from '~/boards/stores/getters'; +import mutations from '~/boards/stores/mutations'; import sidebarEventHub from '~/sidebar/event_hub'; +import { mockLabelList } from '../mock_data'; -const localVue = createLocalVue(); - -localVue.use(Vuex); +Vue.use(Vuex); describe('BoardSettingsSidebar', () => { let wrapper; - let mock; - let store; - const labelTitle = 'test'; - const labelColor = '#FFFF'; - const listId = 1; + const labelTitle = mockLabelList.label.title; + const labelColor = mockLabelList.label.color; + const listId = mockLabelList.id; const findRemoveButton = () => wrapper.findByTestId('remove-list'); - const createComponent = ({ canAdminList = false } = {}) => { + const createComponent = ({ + canAdminList = false, + list = {}, + sidebarType = LIST, + activeId = inactiveId, + } = {}) => { + const boardLists = { + [listId]: list, + }; + const store = new Vuex.Store({ + state: { sidebarType, activeId, boardLists }, + getters, + mutations, + actions, + }); + wrapper = extendedWrapper( shallowMount(BoardSettingsSidebar, { store, - localVue, provide: { canAdminList, }, @@ -40,16 +51,10 @@ describe('BoardSettingsSidebar', () => { const findLabel = () => wrapper.find(GlLabel); const findDrawer = () => wrapper.find(GlDrawer); - beforeEach(() => { - store = createStore(); - store.state.activeId = inactiveId; - store.state.sidebarType = LIST; - boardsStore.create(); - }); - afterEach(() => { jest.restoreAllMocks(); wrapper.destroy(); + wrapper = null; }); it('finds a MountingPortal component', () => { @@ -100,86 +105,40 @@ describe('BoardSettingsSidebar', () => { }); describe('when activeId is greater than zero', () => { - beforeEach(() => { - mock = new MockAdapter(axios); - - boardsStore.addList({ - id: listId, - label: { title: labelTitle, color: labelColor }, - list_type: 'label', - }); - store.state.activeId = 1; - store.state.sidebarType = LIST; - }); - - afterEach(() => { - boardsStore.removeList(listId); - }); - - it('renders GlDrawer with open false', () => { - createComponent(); + it('renders GlDrawer with open true', () => { + createComponent({ list: mockLabelList, activeId: listId }); expect(findDrawer().props('open')).toBe(true); }); }); - describe('when activeId is in boardsStore', () => { - beforeEach(() => { - mock = new MockAdapter(axios); - - boardsStore.addList({ - id: listId, - label: { title: labelTitle, color: labelColor }, - list_type: 'label', - }); - - store.state.activeId = listId; - store.state.sidebarType = LIST; - - createComponent(); - }); - - afterEach(() => { - mock.restore(); - }); - + describe('when activeId is in state', () => { it('renders label title', () => { + createComponent({ list: mockLabelList, activeId: listId }); + expect(findLabel().props('title')).toBe(labelTitle); }); it('renders label background color', () => { + createComponent({ list: mockLabelList, activeId: listId }); + expect(findLabel().props('backgroundColor')).toBe(labelColor); }); }); - describe('when activeId is not in boardsStore', () => { - beforeEach(() => { - mock = new MockAdapter(axios); - - boardsStore.addList({ id: listId, label: { title: labelTitle, color: labelColor } }); - - store.state.activeId = inactiveId; - - createComponent(); - }); - - afterEach(() => { - mock.restore(); - }); - + describe('when activeId is not in state', () => { it('does not render GlLabel', () => { + createComponent({ list: mockLabelList }); + expect(findLabel().exists()).toBe(false); }); }); }); describe('when sidebarType is not List', () => { - beforeEach(() => { - store.state.sidebarType = ''; - createComponent(); - }); - it('does not render GlDrawer', () => { + createComponent({ sidebarType: '' }); + expect(findDrawer().exists()).toBe(false); }); }); @@ -191,20 +150,9 @@ describe('BoardSettingsSidebar', () => { }); describe('when user can admin the boards list', () => { - beforeEach(() => { - store.state.activeId = listId; - store.state.sidebarType = LIST; - - boardsStore.addList({ - id: listId, - label: { title: labelTitle, color: labelColor }, - list_type: 'label', - }); - - createComponent({ canAdminList: true }); - }); - it('renders "Remove list" button', () => { + createComponent({ canAdminList: true, activeId: listId, list: mockLabelList }); + expect(findRemoveButton().exists()).toBe(true); }); }); diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js index 47a41868b1541826874ca85eb75d115d7337f9a5..97e8b6898195c330367500d812351473889b4696 100644 --- a/spec/frontend/boards/mock_data.js +++ b/spec/frontend/boards/mock_data.js @@ -336,6 +336,22 @@ export const mockLabelList = { issuesCount: 0, }; +export const mockMilestoneList = { + id: 'gid://gitlab/List/3', + title: 'To Do', + position: 0, + listType: 'milestone', + collapsed: false, + label: null, + assignee: null, + milestone: { + webUrl: 'https://gitlab.com/h5bp/html5-boilerplate/-/milestones/1', + title: 'Backlog', + }, + loading: false, + issuesCount: 0, +}; + export const mockLists = [mockList, mockLabelList]; export const mockListsById = keyBy(mockLists, 'id'); diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index 1272a573d2f57f01ddf0290641b7fe643957684c..680b5ca670bbe3c6a707246ee07aaddb177309f8 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -107,12 +107,7 @@ describe('setFilters', () => { }); describe('performSearch', () => { - it('should dispatch setFilters action', (done) => { - testAction(actions.performSearch, {}, {}, [], [{ type: 'setFilters', payload: {} }], done); - }); - - it('should dispatch setFilters, fetchLists and resetIssues action when graphqlBoardLists FF is on', (done) => { - window.gon = { features: { graphqlBoardLists: true } }; + it('should dispatch setFilters, fetchLists and resetIssues action', (done) => { testAction( actions.performSearch, {}, @@ -496,12 +491,9 @@ describe('fetchLabels', () => { jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse); const commit = jest.fn(); - const getters = { - shouldUseGraphQL: () => true, - }; const state = { boardType: 'group' }; - await actions.fetchLabels({ getters, state, commit }); + await actions.fetchLabels({ state, commit }); expect(commit).toHaveBeenCalledWith(types.RECEIVE_LABELS_SUCCESS, labels); }); @@ -954,7 +946,7 @@ describe('moveIssue', () => { }); describe('moveIssueCard and undoMoveIssueCard', () => { - describe('card should move without clonning', () => { + describe('card should move without cloning', () => { let state; let params; let moveMutations;