diff options
57 files changed, 540 insertions, 201 deletions
diff --git a/.rubocop_todo/capybara/testid_finders.yml b/.rubocop_todo/capybara/testid_finders.yml index a1e0c7f642e..5b22bdb75b7 100644 --- a/.rubocop_todo/capybara/testid_finders.yml +++ b/.rubocop_todo/capybara/testid_finders.yml @@ -1,24 +1,6 @@ --- Capybara/TestidFinders: Exclude: - - 'ee/spec/features/admin/admin_dev_ops_reports_spec.rb' - - 'ee/spec/features/admin/admin_merge_requests_approvals_spec.rb' - - 'ee/spec/features/admin/admin_sends_notification_spec.rb' - - 'ee/spec/features/admin/admin_settings_spec.rb' - - 'ee/spec/features/admin/geo/admin_geo_projects_spec.rb' - - 'ee/spec/features/admin/groups/admin_subscription_alerts_spec.rb' - - 'ee/spec/features/admin/subscriptions/admin_views_subscription_spec.rb' - - 'ee/spec/features/billings/billing_plans_spec.rb' - - 'ee/spec/features/boards/boards_licensed_features_spec.rb' - - 'ee/spec/features/boards/boards_spec.rb' - - 'ee/spec/features/boards/group_boards/board_deletion_spec.rb' - - 'ee/spec/features/boards/new_issue_spec.rb' - - 'ee/spec/features/boards/scoped_issue_board_spec.rb' - - 'ee/spec/features/boards/sidebar_spec.rb' - - 'ee/spec/features/boards/swimlanes/epics_swimlanes_drag_drop_spec.rb' - - 'ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_labels_spec.rb' - - 'ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_spec.rb' - - 'ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb' - 'ee/spec/features/boards/user_adds_lists_to_board_spec.rb' - 'ee/spec/features/ci/ci_catalog_spec.rb' - 'ee/spec/features/ci/ci_minutes_spec.rb' diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue index 25c03496a76..d9314916ac4 100644 --- a/app/assets/javascripts/content_editor/components/content_editor.vue +++ b/app/assets/javascripts/content_editor/components/content_editor.vue @@ -238,11 +238,7 @@ export default { @keydown="$emit('keydown', $event)" /> <content-editor-alert /> - <div - data-testid="content-editor" - data-qa-selector="content_editor_container" - :class="{ 'is-focused': focused }" - > + <div data-testid="content-editor" :class="{ 'is-focused': focused }"> <formatting-toolbar ref="toolbar" :supports-quick-actions="supportsQuickActions" diff --git a/app/assets/javascripts/content_editor/components/toolbar_attachment_button.vue b/app/assets/javascripts/content_editor/components/toolbar_attachment_button.vue index 4cf150dd948..78a01693f14 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_attachment_button.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_attachment_button.vue @@ -58,7 +58,7 @@ export default { name="content_editor_image" class="gl-display-none" :aria-label="$options.i18n.inputLabel" - data-qa-selector="file_upload_field" + data-testid="file-upload-field" @change="onFileSelect" /> </span> diff --git a/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue b/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue index bd30bdcea0c..4b1e14665de 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue @@ -75,7 +75,7 @@ export default { :selected="activeItemLabel" :disabled="!activeItem" :data-qa-text-style="activeItemLabel" - data-qa-selector="text_style_dropdown" + data-testid="text-style-dropdown" size="small" toggle-class="btn-default-tertiary" @select="execute" diff --git a/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue index 3792dad376b..3c070d2708d 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue @@ -77,7 +77,7 @@ export default { v-gl-modal="$options.modal.modalId" category="secondary" variant="danger" - data-qa-selector="delete_button" + data-qa-selector="delete-button" > {{ $options.i18n.deletePageText }} </gl-button> diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue index 553cb1f0464..eaa99556994 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue @@ -317,7 +317,7 @@ export default { name="wiki[title]" type="text" class="form-control" - data-qa-selector="wiki_title_textbox" + data-testid="wiki-title-textbox" :required="true" :autofocus="!pageInfo.persisted" :placeholder="$options.i18n.title.placeholder" @@ -397,7 +397,7 @@ export default { name="wiki[message]" type="text" class="form-control" - data-qa-selector="wiki_message_textbox" + data-testid="wiki-message-textbox" :placeholder="$options.i18n.commitMessage.label" /> </gl-form-group> @@ -409,7 +409,6 @@ export default { category="primary" variant="confirm" type="submit" - data-qa-selector="wiki_submit_button" data-testid="wiki-submit-button" :disabled="disableSubmitButton" >{{ submitButtonText }}</gl-button diff --git a/app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue b/app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue index f19dcca5166..9ffed6245ba 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue @@ -30,7 +30,7 @@ export default { <gl-button :id="$options.richTextEditorButtonId" class="btn btn-default btn-sm gl-button btn-default-tertiary gl-font-sm! gl-text-secondary! gl-px-4!" - data-qa-selector="editing_mode_switcher" + data-testid="editing-mode-switcher" @click="$emit('switch')" >{{ text }}</gl-button > diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue index fc7e0a7c732..246dc4da801 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue @@ -292,7 +292,7 @@ export default { class="note-textarea js-gfm-input markdown-area" dir="auto" :data-supports-quick-actions="supportsQuickActions" - :data-qa-selector="formFieldProps['data-qa-selector'] || 'markdown_editor_form_field'" + :data-testid="formFieldProps['data-testid'] || 'markdown-editor-form-field'" :disabled="disabled" @input="updateMarkdownFromMarkdownField" @keydown="$emit('keydown', $event)" @@ -323,7 +323,7 @@ export default { <input v-bind="formFieldProps" :value="markdown" - data-qa-selector="markdown_editor_form_field" + data-testid="markdown-editor-form-field" type="hidden" /> </div> diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue index 4657954c8cc..bb36df0a778 100644 --- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue +++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue @@ -268,7 +268,6 @@ export default { class="issue-title-text" dir="auto" :href="webUrl" - data-qa-selector="issuable_title_link" data-testid="issuable-title-link" v-bind="issuableTitleProps" @click="handleIssuableItemClick" diff --git a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue index 279e6ad01b3..cd6064d4dd0 100644 --- a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue +++ b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue @@ -53,6 +53,7 @@ export default { :can-update="canUpdate" :child-path="linkedItemPath(workItemFullPath, linkedItem.workItem.iid)" @click="$emit('showModal', { event: $event, child: linkedItem.workItem })" + @removeChild="$emit('removeLinkedItem', linkedItem.workItem)" /> </li> </ul> diff --git a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue index 6f02ea507e5..8d8aa22e544 100644 --- a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue +++ b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue @@ -1,10 +1,13 @@ <script> -import { GlLoadingIcon, GlIcon, GlButton } from '@gitlab/ui'; +import { produce } from 'immer'; +import { GlLoadingIcon, GlIcon, GlButton, GlLink } from '@gitlab/ui'; import { s__ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; import groupWorkItemByIidQuery from '../../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; +import removeLinkedItemsMutation from '../../graphql/remove_linked_items.mutation.graphql'; import { WIDGET_TYPE_LINKED_ITEMS, LINKED_CATEGORIES_MAP } from '../../constants'; import WidgetWrapper from '../widget_wrapper.vue'; @@ -12,10 +15,12 @@ import WorkItemRelationshipList from './work_item_relationship_list.vue'; import WorkItemAddRelationshipForm from './work_item_add_relationship_form.vue'; export default { + helpPath: helpPagePath('/user/okrs.md#linked-items-in-okrs'), components: { GlLoadingIcon, GlIcon, GlButton, + GlLink, WidgetWrapper, WorkItemRelationshipList, WorkItemAddRelationshipForm, @@ -124,12 +129,71 @@ export default { hideLinkItemForm() { this.isShownLinkItemForm = false; }, + async removeLinkedItem(linkedItem) { + try { + const { + data: { + workItemRemoveLinkedItems: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: removeLinkedItemsMutation, + variables: { + input: { + id: this.workItemId, + workItemsIds: [linkedItem.id], + }, + }, + update: (cache, { data: { workItemRemoveLinkedItems } }) => { + const errorMessages = workItemRemoveLinkedItems?.errors; + if (errorMessages && errorMessages.length > 0) { + [this.error] = errorMessages; + return; + } + const queryArgs = { + query: workItemByIidQuery, + variables: { fullPath: this.workItemFullPath, iid: this.workItemIid }, + }; + const sourceData = cache.readQuery(queryArgs); + + if (!sourceData) { + return; + } + + cache.writeQuery({ + ...queryArgs, + data: produce(sourceData, (draftState) => { + const linkedItems = + draftState.workspace.workItems.nodes[0].widgets?.find( + (widget) => widget.type === WIDGET_TYPE_LINKED_ITEMS, + )?.linkedItems?.nodes || []; + const index = linkedItems.findIndex((item) => { + return item.workItem.id === linkedItem.id; + }); + linkedItems.splice(index, 1); + }), + }); + }, + }); + + if (errors.length > 0) { + [this.error] = errors; + return; + } + + this.$toast.show(s__('WorkItem|Linked item removed')); + } catch { + this.error = this.$options.i18n.removeLinkedItemErrorMessage; + } + }, }, i18n: { title: s__('WorkItem|Linked Items'), - fetchError: s__('WorkItem|Something went wrong when fetching tasks. Please refresh this page.'), + fetchError: s__('WorkItem|Something went wrong when fetching items. Please refresh this page.'), emptyStateMessage: s__( - "WorkItem|Link work items together to show that they're related or that one is blocking others.", + "WorkItem|Link items together to show that they're related or that one is blocking others.", + ), + removeLinkedItemErrorMessage: s__( + 'WorkItem|Something went wrong when removing item. Please refresh this page.', ), addChildButtonLabel: s__('WorkItem|Add'), relatedToTitle: s__('WorkItem|Related to'), @@ -182,9 +246,12 @@ export default { /> <gl-loading-icon v-if="isLoading" color="dark" class="gl-my-2" /> <template v-else> - <div v-if="isEmptyRelatedWorkItems" data-testid="links-empty"> + <div v-if="!isShownLinkItemForm && isEmptyRelatedWorkItems" data-testid="links-empty"> <p class="gl-new-card-empty"> {{ $options.i18n.emptyStateMessage }} + <gl-link :href="$options.helpPath" data-testid="help-link"> + {{ __('Learn more.') }} + </gl-link> </p> </div> <template v-else> @@ -197,8 +264,9 @@ export default { :linked-items="linksBlocks" :heading="$options.i18n.blockingTitle" :work-item-full-path="workItemFullPath" - :can-update="false" + :can-update="canAdminWorkItemLink" @showModal="$emit('showModal', { event: $event.event, modalWorkItem: $event.child })" + @removeLinkedItem="removeLinkedItem" /> <work-item-relationship-list v-if="linksIsBlockedBy.length" @@ -209,16 +277,18 @@ export default { :linked-items="linksIsBlockedBy" :heading="$options.i18n.blockedByTitle" :work-item-full-path="workItemFullPath" - :can-update="false" + :can-update="canAdminWorkItemLink" @showModal="$emit('showModal', { event: $event.event, modalWorkItem: $event.child })" + @removeLinkedItem="removeLinkedItem" /> <work-item-relationship-list v-if="linksRelatesTo.length" :linked-items="linksRelatesTo" :heading="$options.i18n.relatedToTitle" :work-item-full-path="workItemFullPath" - :can-update="false" + :can-update="canAdminWorkItemLink" @showModal="$emit('showModal', { event: $event.event, modalWorkItem: $event.child })" + @removeLinkedItem="removeLinkedItem" /> </template> </template> diff --git a/app/assets/javascripts/work_items/graphql/remove_linked_items.mutation.graphql b/app/assets/javascripts/work_items/graphql/remove_linked_items.mutation.graphql new file mode 100644 index 00000000000..f83f5474606 --- /dev/null +++ b/app/assets/javascripts/work_items/graphql/remove_linked_items.mutation.graphql @@ -0,0 +1,6 @@ +mutation removeLinkedItems($input: WorkItemRemoveLinkedItemsInput!) { + workItemRemoveLinkedItems(input: $input) { + errors + message + } +} diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 9f5db91bcda..031b32a712a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -53,6 +53,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:issues_grid_view) push_frontend_feature_flag(:service_desk_ticket) push_frontend_feature_flag(:issues_list_drawer, project) + push_frontend_feature_flag(:linked_work_items, project) end before_action only: [:index, :show] do diff --git a/app/graphql/resolvers/user_notes_count_resolver.rb b/app/graphql/resolvers/user_notes_count_resolver.rb index b91815c72f5..ebc54a1c6e8 100644 --- a/app/graphql/resolvers/user_notes_count_resolver.rb +++ b/app/graphql/resolvers/user_notes_count_resolver.rb @@ -20,7 +20,7 @@ module Resolvers def authorized_resource?(object) ability = "read_#{object.class.name.underscore}".to_sym - context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object) + Ability.allowed?(context[:current_user], ability, object) end end end diff --git a/app/models/analytics/cycle_analytics/value_stream.rb b/app/models/analytics/cycle_analytics/value_stream.rb index 16446a5b463..7f8c6eef704 100644 --- a/app/models/analytics/cycle_analytics/value_stream.rb +++ b/app/models/analytics/cycle_analytics/value_stream.rb @@ -51,3 +51,4 @@ module Analytics end end end +Analytics::CycleAnalytics::ValueStream.prepend_mod_with('Analytics::CycleAnalytics::ValueStream') diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index a7e2be0eae5..2eed693ca76 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -205,7 +205,7 @@ class WikiPage update_attributes(attrs) save do - wiki.create_page(title, content, format, attrs[:message]) + wiki.create_page(title, raw_content, format, attrs[:message]) end end diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index dde4ec3cf52..4b39ec52837 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -5,18 +5,18 @@ %span.js-clone-dropdown-label = enabled_protocol_button(container, enabled_protocol) - else - %a#clone-dropdown.input-group-text.gl-button.btn.btn-default.btn-icon.clone-dropdown-btn{ href: '#', data: { toggle: 'dropdown', qa_selector: 'clone_dropdown' } } + %a#clone-dropdown.input-group-text.gl-button.btn.btn-default.btn-icon.clone-dropdown-btn{ href: '#', data: { toggle: 'dropdown', testid: 'clone-dropdown' } } %span.js-clone-dropdown-label = default_clone_protocol.upcase = sprite_icon('chevron-down', css_class: 'gl-icon') - %ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown{ data: { qa_selector: 'clone_dropdown_content' } } + %ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown{ data: { testid: 'clone-dropdown-content' } } %li = ssh_clone_button(container) %li = http_clone_button(container) = render_if_exists 'shared/kerberos_clone_button', container: container - = text_field_tag :clone_url, default_url_to_repo(container), class: "js-select-on-focus btn gl-button", readonly: true, aria: { label: _('Repository clone URL') }, data: { qa_selector: 'clone_url_content' } + = text_field_tag :clone_url, default_url_to_repo(container), class: "js-select-on-focus btn gl-button", readonly: true, aria: { label: _('Repository clone URL') }, data: { testid: 'clone-url-content' } .input-group-append = clipboard_button(target: '#clone_url', title: _("Copy URL"), variant: :default, category: :primary, size: :medium) diff --git a/app/views/shared/empty_states/_wikis.html.haml b/app/views/shared/empty_states/_wikis.html.haml index 567c4a2d444..e152390b0df 100644 --- a/app/views/shared/empty_states/_wikis.html.haml +++ b/app/views/shared/empty_states/_wikis.html.haml @@ -4,7 +4,7 @@ - if !hide_create && can?(current_user, :create_wiki, @wiki.container) - create_path = wiki_page_path(@wiki, params[:id], view: 'create') - - create_link = link_button_to s_('WikiEmpty|Create your first page'), create_path, title: s_('WikiEmpty|Create your first page'), data: { qa_selector: 'create_first_page_link' }, variant: :confirm + - create_link = link_button_to s_('WikiEmpty|Create your first page'), create_path, title: s_('WikiEmpty|Create your first page'), data: { testid: 'create-first-page-link' }, variant: :confirm = render layout: layout_path, locals: { image_path: 'illustrations/empty-state/empty-wiki-md.svg' } do %h4.text-left diff --git a/app/views/shared/empty_states/_wikis_layout.html.haml b/app/views/shared/empty_states/_wikis_layout.html.haml index 03054c959fd..831bdcac073 100644 --- a/app/views/shared/empty_states/_wikis_layout.html.haml +++ b/app/views/shared/empty_states/_wikis_layout.html.haml @@ -1,6 +1,6 @@ .row.empty-state.empty-state-wiki .col-12 - .svg-content.svg-150{ data: { qa_selector: 'svg_content' } } + .svg-content.svg-150{ data: { testid: 'svg-content' } } = image_tag image_path .col-12 .text-content.text-center diff --git a/app/views/shared/wikis/_form.html.haml b/app/views/shared/wikis/_form.html.haml index 34bedbd928a..cdf4b50a99d 100644 --- a/app/views/shared/wikis/_form.html.haml +++ b/app/views/shared/wikis/_form.html.haml @@ -1,4 +1,4 @@ -- page_info = { last_commit_sha: @page.last_commit_sha, persisted: @page.persisted?, title: @page.title, content: @page.content || '', format: @page.format.to_s, uploads_path: uploads_path, path: wiki_page_path(@wiki, @page), wiki_path: wiki_path(@wiki), help_path: help_page_path('user/project/wiki/index'), markdown_help_path: help_page_path('user/markdown'), markdown_preview_path: wiki_page_path(@wiki, @page, action: :preview_markdown), create_path: wiki_path(@wiki, action: :create) } +- page_info = { last_commit_sha: @page.last_commit_sha, persisted: @page.persisted?, title: @page.title, content: @page.raw_content || '', format: @page.format.to_s, uploads_path: uploads_path, path: wiki_page_path(@wiki, @page), wiki_path: wiki_path(@wiki), help_path: help_page_path('user/project/wiki/index'), markdown_help_path: help_page_path('user/markdown'), markdown_preview_path: wiki_page_path(@wiki, @page, action: :preview_markdown), create_path: wiki_path(@wiki, action: :create) } .gl-mt-3 = form_errors(@page, truncate: :title) diff --git a/app/views/shared/wikis/_main_links.html.haml b/app/views/shared/wikis/_main_links.html.haml index 41831c95198..9a76069e8f6 100644 --- a/app/views/shared/wikis/_main_links.html.haml +++ b/app/views/shared/wikis/_main_links.html.haml @@ -1,6 +1,6 @@ - if @page&.persisted? - = link_button_to wiki_page_path(@wiki, @page, action: :history), role: "button", data: { qa_selector: 'page_history_button' } do + = link_button_to wiki_page_path(@wiki, @page, action: :history), role: "button", data: { testid: 'page-history-button' } do = s_("Wiki|Page history") - if can?(current_user, :create_wiki, @wiki.container) - = link_button_to wiki_path(@wiki, action: :new), role: "button", data: { qa_selector: 'new_page_button' }, variant: :confirm, category: :secondary do + = link_button_to wiki_path(@wiki, action: :new), role: "button", data: { testid: 'new-page-button' }, variant: :confirm, category: :secondary do = s_("Wiki|New page") diff --git a/app/views/shared/wikis/_pages_wiki_page.html.haml b/app/views/shared/wikis/_pages_wiki_page.html.haml index fb6f58d044d..23931bbbc32 100644 --- a/app/views/shared/wikis/_pages_wiki_page.html.haml +++ b/app/views/shared/wikis/_pages_wiki_page.html.haml @@ -1,5 +1,5 @@ %li - = link_to wiki_page.human_title, wiki_page_path(@wiki, wiki_page), data: { qa_selector: 'wiki_page_link', qa_page_name: wiki_page.slug } + = link_to wiki_page.human_title, wiki_page_path(@wiki, wiki_page), data: { testid: 'wiki-page-link', qa_page_name: wiki_page.slug } %small (#{wiki_page.format}) .float-right - if wiki_page.last_version diff --git a/app/views/shared/wikis/_sidebar.html.haml b/app/views/shared/wikis/_sidebar.html.haml index a34827602ab..cd752d31643 100644 --- a/app/views/shared/wikis/_sidebar.html.haml +++ b/app/views/shared/wikis/_sidebar.html.haml @@ -8,7 +8,7 @@ .gl-display-flex.gl-flex-wrap - git_access_url = wiki_path(@wiki, action: :git_access) - = link_to git_access_url, class: 'gl-mr-5' + (active_nav_link?(path: 'wikis#git_access') ? ' active' : ''), data: { qa_selector: 'clone_repository_link' } do + = link_to git_access_url, class: 'gl-mr-5' + (active_nav_link?(path: 'wikis#git_access') ? ' active' : ''), data: { testid: 'clone-repository-link' } do = sprite_icon('download', css_class: 'gl-mr-2') %span= _("Clone repository") @@ -32,5 +32,5 @@ = render partial: entry.to_partial_path, object: entry, locals: { context: 'sidebar' } .block.w-100 - if @sidebar_limited - = link_button_to wiki_path(@wiki, action: :pages), data: { qa_selector: 'view_all_pages_button' }, block: true do + = link_button_to wiki_path(@wiki, action: :pages), data: { testid: 'view-all-pages-button' }, block: true do = s_("Wiki|View All Pages") diff --git a/app/views/shared/wikis/_sidebar_wiki_page.html.haml b/app/views/shared/wikis/_sidebar_wiki_page.html.haml index 2c5c3aa68a3..710ecf6196e 100644 --- a/app/views/shared/wikis/_sidebar_wiki_page.html.haml +++ b/app/views/shared/wikis/_sidebar_wiki_page.html.haml @@ -3,5 +3,5 @@ %li{ class: active_when(params[:id] == wiki_page.slug) } .gl-relative.gl-display-flex.gl-align-items-center.js-wiki-list-toggle.wiki-list{ data: { testid: 'wiki-list' } } = render Pajamas::ButtonComponent.new(icon: 'plus', href: "#{wiki_path}/{new_page_title}", button_options: { class: 'wiki-list-create-child-button gl-bg-transparent! gl-hover-bg-gray-50! gl-focus-bg-gray-50! gl-absolute gl-top-half gl-translate-y-n50 gl-cursor-pointer gl-right-3' }) - = link_to wiki_path, data: { qa_selector: 'wiki_page_link', qa_page_name: wiki_page.human_title } do + = link_to wiki_path, data: { testid: 'wiki-page-link', qa_page_name: wiki_page.human_title } do = wiki_page.human_title diff --git a/app/views/shared/wikis/_wiki_content.html.haml b/app/views/shared/wikis/_wiki_content.html.haml index 780e4c4746d..b5210b340f3 100644 --- a/app/views/shared/wikis/_wiki_content.html.haml +++ b/app/views/shared/wikis/_wiki_content.html.haml @@ -1,2 +1,2 @@ -.js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json } } +.js-wiki-page-content.md.gl-pt-2{ data: { testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json } } = render_wiki_content(@page) diff --git a/app/views/shared/wikis/_wiki_directory.html.haml b/app/views/shared/wikis/_wiki_directory.html.haml index 6a066e0a838..cce81257691 100644 --- a/app/views/shared/wikis/_wiki_directory.html.haml +++ b/app/views/shared/wikis/_wiki_directory.html.haml @@ -1,11 +1,11 @@ - wiki_path = wiki_page_path(@wiki, wiki_directory) -%li{ class: active_when(params[:id] == wiki_directory.slug), data: { qa_selector: 'wiki_directory_content' } } +%li{ class: active_when(params[:id] == wiki_directory.slug), data: { testid: 'wiki-directory-content' } } .gl-relative.gl-display-flex.gl-align-items-center.js-wiki-list-toggle.wiki-list{ data: { testid: 'wiki-list' } }< = sprite_icon('chevron-right', css_class: 'js-wiki-list-expand-button wiki-list-expand-button gl-mr-2 gl-cursor-pointer') = sprite_icon('chevron-down', css_class: 'js-wiki-list-collapse-button wiki-list-collapse-button gl-mr-2 gl-cursor-pointer') = render Pajamas::ButtonComponent.new(icon: 'plus', href: "#{wiki_path}/{new_page_title}", button_options: { class: 'wiki-list-create-child-button gl-bg-transparent! gl-hover-bg-gray-50! gl-focus-bg-gray-50! gl-absolute gl-top-half gl-translate-y-n50 gl-cursor-pointer gl-right-3' }) - = link_to wiki_path, data: { qa_selector: 'wiki_dir_page_link', qa_page_name: wiki_directory.title } do + = link_to wiki_path, data: { testid: 'wiki-dir-page-link', qa_page_name: wiki_directory.title } do = wiki_directory.title %ul - wiki_directory.entries.each do |entry| diff --git a/app/views/shared/wikis/show.html.haml b/app/views/shared/wikis/show.html.haml index be1f43f44de..9537d6fec15 100644 --- a/app/views/shared/wikis/show.html.haml +++ b/app/views/shared/wikis/show.html.haml @@ -29,10 +29,10 @@ .gl-mt-5.gl-mb-3 .gl-display-flex.gl-justify-content-space-between - %h2.gl-mt-0.gl-mb-5{ data: { qa_selector: 'wiki_page_title', testid: 'wiki_page_title' } }= @page.human_title + %h2.gl-mt-0.gl-mb-5{ data: { testid: 'wiki-page-title' } }= @page.human_title %div - if can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding - = render Pajamas::ButtonComponent.new(href: wiki_page_path(@wiki, @page, action: :edit), icon: 'pencil', button_options: { class: 'js-wiki-edit', title: "Edit", data: { qa_selector: 'edit_page_button', testid: 'wiki_edit_button' }}) + = render Pajamas::ButtonComponent.new(href: wiki_page_path(@wiki, @page, action: :edit), icon: 'pencil', button_options: { class: 'js-wiki-edit', title: "Edit", data: { testid: 'wiki-edit-button' }}) .js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } } diff --git a/config/feature_flags/development/rate_limit_oauth_api.yml b/config/feature_flags/development/rate_limit_oauth_api.yml new file mode 100644 index 00000000000..bf4db5aa617 --- /dev/null +++ b/config/feature_flags/development/rate_limit_oauth_api.yml @@ -0,0 +1,8 @@ +--- +name: rate_limit_oauth_api +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133109 +rollout_issue_url: +milestone: '16.5' +type: development +group: group::authentication and authorization +default_enabled: false diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index ba702101019..74300e8c81a 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -1980,3 +1980,44 @@ It renders as: ``` ::EndTabs + +### Changes for a version upgrade + +To document upgrade notes and changes, create a new page for each major version of GitLab. +For an example, see [GitLab 16 changes](../../../update/versions/gitlab_16_changes.md). +Use the following template to add information to the page. + +```markdown +# GitLab X changes **(FREE SELF)** + +This page contains upgrade information for minor and patch versions of GitLab X. Review these instructions for: + +- Your installation type. +- All versions between your current version and your target version. + +For more information about upgrading GitLab Helm Chart, see [the release notes for X.0](https://docs.gitlab.com/charts/releases/X_0.html). + +## X.Y.1 (add the latest version at the top of the page) + +- General upgrade notes and issues. +- ... + +### Linux package installations + +- Information specific to Linux package installations. +- ... + +### Self-compiled installations + +- Information specific to self-compiled installations. +- ... + +### Geo installations **(PREMIUM SELF)** + + - Information specific to Geo. + - ... + +## X.Y.0 + + ... +``` diff --git a/doc/development/fe_guide/frontend_goals.md b/doc/development/fe_guide/frontend_goals.md index c25e858ae7a..4f39e82c72e 100644 --- a/doc/development/fe_guide/frontend_goals.md +++ b/doc/development/fe_guide/frontend_goals.md @@ -29,3 +29,16 @@ The realistic goal is to move to _multiple SPAs_ experience where we define the All of them have the same context (project path, current user etc.), we could easily fetch more data with issue-specific parameter (issue `iid`) and store the results on the client (so that opening the same issue won't require more API calls). This leads to a smooth user experience for navigating through issues. For navigation between clusters, we can still rely on Rails routing. These cases should be relatively more scarce than navigation within clusters. + +## Reusable components + +Currently, we keep generically reusable components in two main places: + +- GitLab UI +- `vue_shared` folder + +While GitLab UI is well-documented and components are abstract enough to be reused anywhere in Vue applications, our `vue_shared` components are somewhat chaotic, often can be used only in certain context (for example, they can be bound to an existing Vuex store) and have duplicates (we have multiple components for notes). + +We should perform an audit of `vue_shared`, find out what can and what cannot be moved to GitLab UI, and refactor existing components to remove duplicates and increase reusability. The ideal outcome would be having application-specific components moved to application folders, and keep reusable "smart" components in the shared folder/library, ensuring that every single piece of reusable functionality has _only one implementation_. + +This is currently under development. Follow the [GitLab Modular Monolith for FE](https://gitlab.com/gitlab-org/gitlab/-/issues/422903) for updates on how we will enforce encapsulation on top-level folders like `vue_shared`. diff --git a/doc/user/application_security/dependency_list/index.md b/doc/user/application_security/dependency_list/index.md index d8726cbd456..91145b10f81 100644 --- a/doc/user/application_security/dependency_list/index.md +++ b/doc/user/application_security/dependency_list/index.md @@ -10,6 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w > - System dependencies [introduced](https://gitlab.com/groups/gitlab-org/-/epics/6698) in GitLab 14.6. > - Group-level dependency list [introduced](https://gitlab.com/groups/gitlab-org/-/epics/8090) in GitLab 16.2 [with a flag](../../../administration/feature_flags.md) named `group_level_dependencies`. Disabled by default. > - Group-level dependency list [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/411257) in GitLab 16.4. +> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132015) in GitLab 16.5. Feature flag `group_level_dependencies` removed. Use the dependency list to review your project or group's dependencies and key details about those dependencies, including their known vulnerabilities. This list is a collection of dependencies in your diff --git a/doc/user/group/saml_sso/example_saml_config.md b/doc/user/group/saml_sso/example_saml_config.md index 70e01e1d9a9..c69546f710e 100644 --- a/doc/user/group/saml_sso/example_saml_config.md +++ b/doc/user/group/saml_sso/example_saml_config.md @@ -50,7 +50,7 @@ Provisioning: ![Azure AD SCIM Provisioning](img/AzureAD-scim_provisioning.png) -Attribute mapping: +### Attribute mapping ![Azure AD SCIM Attribute Mapping](img/AzureAD-scim_attribute_mapping.png) @@ -70,15 +70,15 @@ If available, you can add user-friendly group names instead. When setting up Azu ## Google Workspace -Basic SAML app configuration: +### Basic SAML app configuration ![Google Workspace basic SAML](img/GoogleWorkspace-basic-SAML_v14_10.png) -User claims and attributes: +### User claims and attributes ![Google Workspace user claims](img/GoogleWorkspace-claims_v14_10.png) -IdP links and certificate: +### IdP links and certificate NOTE: Google Workspace displays a SHA256 fingerprint. To retrieve the SHA1 fingerprint required by GitLab for configuring SAML, download the certificate and calculate the SHA1 certificate @@ -88,53 +88,55 @@ fingerprint. ## Okta -Basic SAML app configuration for GitLab.com groups: +### Basic SAML app configuration for GitLab.com groups ![Okta basic SAML](img/Okta-GroupSAML.png) -Basic SAML app configuration for GitLab self-managed: +### Basic SAML app configuration for GitLab self-managed ![Okta admin panel view](img/Okta-SM.png) -User claims and attributes: +### User claims and attributes ![Okta Attributes](img/Okta-attributes.png) -Groups attribute: +### Group Sync ![Okta Group attribute](img/Okta-GroupAttribute.png) -Advanced SAML app settings (defaults): +### Advanced SAML app settings (defaults) ![Okta Advanced Settings](img/Okta-advancedsettings.png) -IdP Links and Certificate: +### IdP links and certificate ![Okta Links and Certificate](img/Okta-linkscert.png) -Sign on settings: +### SAML sign on settings ![Okta SAML settings](img/okta_saml_settings.png) +### SCIM settings + Setting the username for the newly provisioned users when assigning them the SCIM app: ![Assigning SCIM app to users on Okta](img/okta_setting_username.png) ## OneLogin -Application details: +### Basic SAML app configuration ![OneLogin application details](img/OneLogin-app_details.png) -Parameters: +### Parameters ![OneLogin application details](img/OneLogin-parameters.png) -Adding a user: +### Adding a user ![OneLogin user add](img/OneLogin-userAdd.png) -SSO settings: +### SSO settings ![OneLogin SSO settings](img/OneLogin-SSOsettings.png) diff --git a/doc/user/group/saml_sso/group_sync.md b/doc/user/group/saml_sso/group_sync.md index 4002472e760..7641c04de10 100644 --- a/doc/user/group/saml_sso/group_sync.md +++ b/doc/user/group/saml_sso/group_sync.md @@ -69,9 +69,11 @@ For example, Azure AD sends the Azure Group Object ID instead of the name. Use t ``` Other attribute names such as `http://schemas.microsoft.com/ws/2008/06/identity/claims/groups` -are not accepted as a source of groups. For more information on configuring the -required attribute name in the SAML identity provider's settings, see -[example group SAML and SCIM configurations](../../../user/group/saml_sso/example_saml_config.md). +are not accepted as a source of groups. + +For more information on configuring the +required group attribute name in the SAML identity provider's settings, see +example configurations for [Azure AD](../../../user/group/saml_sso/example_saml_config.md#group-sync) and [Okta](../../../user/group/saml_sso/example_saml_config.md#group-sync-1). ## Configure SAML Group Links diff --git a/lib/api/entities/wiki_page.rb b/lib/api/entities/wiki_page.rb index 9d2a031cee8..0f3fdd586a3 100644 --- a/lib/api/entities/wiki_page.rb +++ b/lib/api/entities/wiki_page.rb @@ -15,7 +15,7 @@ module API current_user: options[:current_user] ) else - wiki_page.content + wiki_page.raw_content end end diff --git a/lib/gitlab/rack_attack/request.rb b/lib/gitlab/rack_attack/request.rb index 03ead81fd1c..e45782b8be0 100644 --- a/lib/gitlab/rack_attack/request.rb +++ b/lib/gitlab/rack_attack/request.rb @@ -5,6 +5,7 @@ module Gitlab module Request include ::Gitlab::Utils::StrongMemoize + API_PATH_REGEX = %r{^/api/|/oauth/} FILES_PATH_REGEX = %r{^/api/v\d+/projects/[^/]+/repository/files/.+} GROUP_PATH_REGEX = %r{^/api/v\d+/groups/[^/]+/?$} @@ -32,7 +33,11 @@ module Gitlab end def api_request? - logical_path.start_with?('/api') + if ::Feature.enabled?(:rate_limit_oauth_api, ::Feature.current_request) + matches?(API_PATH_REGEX) + else + logical_path.start_with?('/api') + end end def logical_path diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 15cdfcca05b..addd7dd46a6 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -53784,12 +53784,15 @@ msgstr "" msgid "WorkItem|Key result" msgstr "" -msgid "WorkItem|Link work items together to show that they're related or that one is blocking others." +msgid "WorkItem|Link items together to show that they're related or that one is blocking others." msgstr "" msgid "WorkItem|Linked Items" msgstr "" +msgid "WorkItem|Linked item removed" +msgstr "" + msgid "WorkItem|Mark as done" msgstr "" @@ -53874,6 +53877,9 @@ msgstr "" msgid "WorkItem|Something went wrong when deleting the task. Please try again." msgstr "" +msgid "WorkItem|Something went wrong when fetching items. Please refresh this page." +msgstr "" + msgid "WorkItem|Something went wrong when fetching iterations. Please try again." msgstr "" @@ -53889,6 +53895,9 @@ msgstr "" msgid "WorkItem|Something went wrong when fetching work items. Please try again." msgstr "" +msgid "WorkItem|Something went wrong when removing item. Please refresh this page." +msgstr "" + msgid "WorkItem|Something went wrong when trying to add a child. Please try again." msgstr "" diff --git a/package.json b/package.json index 41ec8339baa..0e58b667356 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "@apollo/client": "^3.5.10", "@babel/core": "^7.18.5", "@babel/preset-env": "^7.18.2", - "@cubejs-client/core": "^0.33.59", - "@cubejs-client/vue": "^0.33.65", + "@cubejs-client/core": "^0.34.0", + "@cubejs-client/vue": "^0.34.0", "@floating-ui/dom": "^1.2.9", "@gitlab/application-sdk-browser": "^0.2.8", "@gitlab/at.js": "1.5.7", diff --git a/qa/qa/page/component/content_editor.rb b/qa/qa/page/component/content_editor.rb index f2733e4b065..d5f93bbc8c9 100644 --- a/qa/qa/page/component/content_editor.rb +++ b/qa/qa/page/component/content_editor.rb @@ -10,41 +10,41 @@ module QA super base.view 'app/assets/javascripts/content_editor/components/content_editor.vue' do - element :content_editor_container + element 'content-editor' end - base.view 'app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue' do - element :text_style_dropdown + base.view 'app/assets/javascripts/content_editor/components/formatting_toolbar.vue' do + element 'text-styles' end base.view 'app/assets/javascripts/content_editor/components/toolbar_attachment_button.vue' do - element :file_upload_field + element 'file-upload-field' end base.view 'app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue' do - element :markdown_editor_form_field + element 'markdown-editor-form-field' end end def add_heading(heading, text) - within_element(:content_editor_container) do + within_element('content-editor') do text_area.set(text) # wait for text style option to become active after typing - has_active_element?(:text_style_dropdown, wait: 1) - click_element(:text_style_dropdown) - find_element(:text_style_dropdown).find('li', text: heading).click + has_active_element?('text-styles', wait: 1) + click_element('text-styles') + find_element('.gl-new-dropdown-contents li', text: heading).click end end def upload_image(image_path) - within_element(:content_editor_container) do + within_element('content-editor') do # add image on a new line text_area.send_keys(:return) - find_element(:file_upload_field, visible: false).send_keys(image_path) + find_element('file-upload-field', visible: false).send_keys(image_path) end QA::Support::Retrier.retry_on_exception do - source = find_element(:markdown_editor_form_field, visible: false) + source = find_element('markdown-editor-form-field', visible: false) source.value =~ %r{uploads/.*#{::File.basename(image_path)}} end end diff --git a/qa/qa/page/component/legacy_clone_panel.rb b/qa/qa/page/component/legacy_clone_panel.rb index 8c3c25f6e41..2534865c2f4 100644 --- a/qa/qa/page/component/legacy_clone_panel.rb +++ b/qa/qa/page/component/legacy_clone_panel.rb @@ -10,9 +10,9 @@ module QA super base.view 'app/views/shared/_clone_panel.html.haml' do - element :clone_dropdown - element :clone_dropdown_content - element :clone_url_content + element 'clone-dropdown' + element 'clone-dropdown-content' + element 'clone-url-content' end end @@ -28,16 +28,16 @@ module QA end def repository_location - Git::Location.new(find_element(:clone_url_content).value) + Git::Location.new(find_element('clone-url-content').value) end private def choose_repository_clone(kind, detect_text) wait_until(reload: false) do - click_element :clone_dropdown + click_element 'clone-dropdown' - within_element(:clone_dropdown_content) do + within_element('clone-dropdown-content') do click_link(kind) end diff --git a/qa/qa/page/component/wiki.rb b/qa/qa/page/component/wiki.rb index e34e1f4b589..d5daaf7d8ea 100644 --- a/qa/qa/page/component/wiki.rb +++ b/qa/qa/page/component/wiki.rb @@ -10,25 +10,25 @@ module QA super base.view 'app/views/shared/wikis/show.html.haml' do - element :wiki_page_title - element :edit_page_button + element 'wiki-page-title' + element 'wiki-edit-button' end base.view 'app/views/shared/wikis/_wiki_content.html.haml' do - element :wiki_page_content + element 'wiki-page-content' end base.view 'app/views/shared/wikis/_main_links.html.haml' do - element :new_page_button - element :page_history_button + element 'new-page-button' + element 'page-history-button' end base.view 'app/views/shared/empty_states/_wikis.html.haml' do - element :create_first_page_link + element 'create-first-page-link' end base.view 'app/views/shared/empty_states/_wikis_layout.html.haml' do - element :svg_content + element 'svg-content' end end @@ -37,49 +37,49 @@ module QA # "Create your first page" button shifts up a bit. This can cause # webdriver to miss the hit so we wait for the svg to load before # clicking the button. - within_element(:svg_content) do + within_element('svg-content') do has_element?('js-lazy-loaded-content') end - click_element(:create_first_page_link) + click_element('create-first-page-link') end def click_new_page - click_element(:new_page_button) + click_element('new-page-button') end def click_page_history - click_element(:page_history_button) + click_element('page-history-button') end def click_edit - click_element(:edit_page_button) + click_element('wiki-edit-button') end def has_title?(title) - has_element?(:wiki_page_title, title) + has_element?('wiki-page-title', title) end def has_content?(content) - has_element?(:wiki_page_content, content) + has_element?('wiki-page-content', content) end def has_no_content?(content) - has_no_element?(:wiki_page_content, content) + has_no_element?('wiki-page-content', content) end def has_no_page? - has_element?(:create_first_page_link) + has_element?('create-first-page-link') end def has_heading?(heading_type, text) - within_element(:wiki_page_content) do + within_element('wiki-page-content') do has_css?(heading_type, text: text) end end def has_image?(image_file_name) - within_element(:wiki_page_content) do + within_element('wiki-page-content') do has_css?("img[src$='#{image_file_name}']") end end diff --git a/qa/qa/page/component/wiki_page_form.rb b/qa/qa/page/component/wiki_page_form.rb index 335790c5b27..454121b457e 100644 --- a/qa/qa/page/component/wiki_page_form.rb +++ b/qa/qa/page/component/wiki_page_form.rb @@ -10,34 +10,34 @@ module QA super base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do - element :wiki_title_textbox - element :wiki_message_textbox - element :wiki_submit_button + element 'wiki-title-textbox' + element 'wiki-message-textbox' + element 'wiki-submit-button' end base.view 'app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue' do - element :markdown_editor_form_field + element 'markdown-editor-form-field' end base.view 'app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue' do - element :editing_mode_switcher + element 'editing-mode-switcher' end base.view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do - element :delete_button + element 'delete-button' end end def set_title(title) - fill_element(:wiki_title_textbox, title) + fill_element('wiki-title-textbox', title) end def set_content(content) - fill_element(:markdown_editor_form_field, content) + fill_element('markdown-editor-form-field', content) end def set_message(message) - fill_element(:wiki_message_textbox, message) + fill_element('wiki-message-textbox', message) end def click_submit @@ -45,23 +45,23 @@ module QA # before clicking submit. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97693#note_1098728562 sleep 0.5 - click_element(:wiki_submit_button) + click_element('wiki-submit-button') QA::Support::Retrier.retry_on_exception do - has_no_element?(:wiki_title_textbox) + has_no_element?('wiki-title-textbox') end end def delete_page - click_element(:delete_button, Page::Modal::DeleteWiki) + click_element('delete-button', Page::Modal::DeleteWiki) Page::Modal::DeleteWiki.perform(&:confirm_deletion) end def use_new_editor - click_element(:editing_mode_switcher) + click_element('editing-mode-switcher') wait_until(reload: false) do - has_element?(:content_editor_container) + has_element?('content-editor') end end end diff --git a/qa/qa/page/component/wiki_sidebar.rb b/qa/qa/page/component/wiki_sidebar.rb index 7543b9655f9..8c9741c3086 100644 --- a/qa/qa/page/component/wiki_sidebar.rb +++ b/qa/qa/page/component/wiki_sidebar.rb @@ -10,42 +10,42 @@ module QA super base.view 'app/views/shared/wikis/_sidebar.html.haml' do - element :clone_repository_link - element :view_all_pages_button + element 'clone-repository-link' + element 'view-all-pages-button' end base.view 'app/views/shared/wikis/_sidebar_wiki_page.html.haml' do - element :wiki_page_link + element 'wiki-page-link' end base.view 'app/views/shared/wikis/_wiki_directory.html.haml' do - element :wiki_directory_content - element :wiki_dir_page_link + element 'wiki-directory-content' + element 'wiki-dir-page-link' end end def click_clone_repository - click_element(:clone_repository_link) + click_element('clone-repository-link') end def click_view_all_pages - click_element(:view_all_pages_button) + click_element('view-all-pages-button') end def click_page_link(page_title) - click_element(:wiki_page_link, page_name: page_title) + click_element('wiki-page-link', page_name: page_title) end def has_page_listed?(page_title) - has_element?(:wiki_page_link, page_name: page_title) + has_element?('wiki-page-link', page_name: page_title) end def has_directory?(directory) - has_element?(:wiki_directory_content, text: directory) + has_element?('wiki-directory-content', text: directory) end def has_dir_page?(dir_page) - has_element?(:wiki_dir_page_link, page_name: dir_page) + has_element?('wiki-dir-page-link', page_name: dir_page) end end end diff --git a/qa/qa/page/project/wiki/list.rb b/qa/qa/page/project/wiki/list.rb index 785847011bf..224199256e1 100644 --- a/qa/qa/page/project/wiki/list.rb +++ b/qa/qa/page/project/wiki/list.rb @@ -6,15 +6,15 @@ module QA module Wiki class List < Base view 'app/views/shared/wikis/_pages_wiki_page.html.haml' do - element :wiki_page_link + element 'wiki-page-link' end def click_page_link(page_title) - click_element :wiki_page_link, page_name: page_title + click_element 'wiki-page-link', page_name: page_title end def has_page_listed?(page_title) - has_element? :wiki_page_link, page_name: page_title + has_element? 'wiki-page-link', page_name: page_title end end end diff --git a/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb index 7be18f9d949..959d691bad7 100644 --- a/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb @@ -14,6 +14,7 @@ module QA describe 'Project import', product_group: :import_and_integrate do # rubocop:disable RSpec/MultipleMemoizedHelpers let(:github_repo) { ENV['QA_LARGE_IMPORT_REPO'] || 'rspec/rspec-core' } let(:import_max_duration) { ENV['QA_LARGE_IMPORT_DURATION']&.to_i || 7200 } + let(:api_parallel_threads) { ENV['QA_LARGE_IMPORT_GH_API_PARALLEL']&.to_i || Etc.nprocessors } let(:logger) { Runtime::Logger.logger } let(:differ) { RSpec::Support::Differ.new(color: true) } let(:gitlab_address) { QA::Runtime::Scenario.gitlab_address.chomp("/") } @@ -359,11 +360,8 @@ module QA comments: [*gh_pr_comments[id], *gh_issue_comments[id]].compact } end - logger.info("- Fetching pr events 8 parallel threads -") - Parallel.map(prs, in_threads: 8) do |id, pr| - logger.debug("Fetching events for pr !#{id}") - [id, pr.merge({ events: fetch_github_events(id) })] - end.to_h + + fetch_github_events(prs, "pr") end end @@ -383,22 +381,26 @@ module QA comments: gh_issue_comments[id] } end - logger.info("- Fetching issue events in 8 parallel threads -") - Parallel.map(issues, in_threads: 8) do |id, issue| - logger.debug("Fetching events for issue !#{id}") - [id, issue.merge({ events: fetch_github_events(id) })] - end.to_h + + fetch_github_events(issues, "issue") end end - # Fetch github events for issue/pr + # Fetch github events and add to issue object # - # @param [Integer] id - # @return [Array] - def fetch_github_events(id) - with_paginated_request { github_client.issue_events(github_repo, id) } - .map { |event| event[:event] } - .reject { |event| unsupported_events.include?(event) } + # @param [Hash] issuables + # @param [String] type + # @return [Hash] + def fetch_github_events(issuables, type) + logger.info("- Fetching #{type} events in #{api_parallel_threads} parallel threads -") + Parallel.map(issuables, in_threads: 8) do |id, issuable| + logger.debug("[tid: #{current_thread}] Fetching events for #{type} !#{id}") + events = with_paginated_request { github_client.issue_events(github_repo, id) } + .map { |event| event[:event] } + .reject { |event| unsupported_events.include?(event) } + + [id, issuable.merge({ events: events })] + end.to_h end # Verify imported mrs or issues and return missing items @@ -530,8 +532,8 @@ module QA logger.debug("= Fetching merge requests =") imported_mrs = imported_project.merge_requests(**api_request_params) - logger.debug("- Fetching merge request comments #{Etc.nprocessors} parallel threads -") - Parallel.map(imported_mrs, in_threads: Etc.nprocessors) do |mr| + logger.debug("- Fetching merge request comments #{api_parallel_threads} parallel threads -") + Parallel.map(imported_mrs, in_threads: api_parallel_threads) do |mr| resource = Resource::MergeRequest.init do |resource| resource.project = imported_project resource.iid = mr[:iid] @@ -562,8 +564,8 @@ module QA logger.debug("= Fetching issues =") imported_issues = imported_project.issues(**api_request_params) - logger.debug("- Fetching issue comments #{Etc.nprocessors} parallel threads -") - Parallel.map(imported_issues, in_threads: Etc.nprocessors) do |issue| + logger.debug("- Fetching issue comments #{api_parallel_threads} parallel threads -") + Parallel.map(imported_issues, in_threads: api_parallel_threads) do |issue| resource = build(:issue, project: imported_project, iid: issue[:iid], api_client: api_client) comments = resource.comments(**api_request_params) @@ -666,7 +668,7 @@ module QA next_link = github_client.last_response.rels[:next]&.href break unless next_link - logger.debug("Fetching resources from next page: '#{next_link}'") + logger.debug("[tid: #{current_thread}] Fetching resources from next page: '#{next_link}'") resources.concat(with_rate_limit { github_client.get(next_link) }) end @@ -682,11 +684,19 @@ module QA raise e unless e.response[:status] == 403 wait = github_client.rate_limit.resets_in + 5 - logger.warn("GitHub rate api rate limit reached, resuming in '#{wait}' seconds") + logger.warn("[tid: #{current_thread}] GitHub rate api rate limit reached, resuming in '#{wait}' seconds") + logger.debug("[tid: #{current_thread}] #{JSON.parse(e.response[:body])['message']}") sleep(wait) retry end + + # Get current thread id for better logging + # + # @return [Integer] + def current_thread + Thread.current.object_id + end end end end diff --git a/spec/features/projects/work_items/linked_work_items_spec.rb b/spec/features/projects/work_items/linked_work_items_spec.rb index c071a7501f2..1fa2f66e71e 100644 --- a/spec/features/projects/work_items/linked_work_items_spec.rb +++ b/spec/features/projects/work_items/linked_work_items_spec.rb @@ -82,5 +82,33 @@ RSpec.describe 'Work item linked items', :js, feature_category: :team_planning d expect(find('.work-items-list')).to have_content('Task 1') end end + + it 'removes a linked item', :aggregate_failures do + page.within('.work-item-relationships') do + click_button 'Add' + + within_testid('link-work-item-form') do + expect(page).to have_button('Add', disabled: true) + find_by_testid('work-item-token-select-input').set(task.title) + wait_for_all_requests + click_button task.title + + expect(page).to have_button('Add', disabled: false) + + click_button 'Add' + + wait_for_all_requests + end + + expect(find('.work-items-list')).to have_content('Task 1') + + find_by_testid('links-menu').click + click_button 'Remove' + + wait_for_all_requests + + expect(page).not_to have_content('Task 1') + end + end end end diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js index 4a5d9bb1175..edf0fcee904 100644 --- a/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js +++ b/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js @@ -1,7 +1,6 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { GlForm, GlFormRadioGroup, GlAlert } from '@gitlab/ui'; -import { s__ } from '~/locale'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; @@ -61,12 +60,12 @@ describe('WorkItemAddRelationshipForm', () => { it('renders link work item form with default values', () => { expect(findLinkWorkItemForm().exists()).toBe(true); expect(findRadioGroup().props('options')).toEqual([ - { text: s__('WorkItem|relates to'), value: LINKED_ITEM_TYPE_VALUE.RELATED }, - { text: s__('WorkItem|blocks'), value: LINKED_ITEM_TYPE_VALUE.BLOCKS }, - { text: s__('WorkItem|is blocked by'), value: LINKED_ITEM_TYPE_VALUE.BLOCKED_BY }, + { text: 'relates to', value: LINKED_ITEM_TYPE_VALUE.RELATED }, + { text: 'blocks', value: LINKED_ITEM_TYPE_VALUE.BLOCKS }, + { text: 'is blocked by', value: LINKED_ITEM_TYPE_VALUE.BLOCKED_BY }, ]); expect(findLinkWorkItemButton().attributes('disabled')).toBe('true'); - expect(findMaxWorkItemNote().text()).toBe(s__('WorkItem|Add a maximum of 3 items at a time.')); + expect(findMaxWorkItemNote().text()).toBe('Add a maximum of 3 items at a time.'); }); it('renders work item token input with default props', () => { diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js index 83f4cf5081e..7178fa1aae7 100644 --- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js +++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js @@ -12,12 +12,14 @@ import WorkItemRelationshipList from '~/work_items/components/work_item_relation import WorkItemAddRelationshipForm from '~/work_items/components/work_item_relationships/work_item_add_relationship_form.vue'; import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; +import removeLinkedItemsMutation from '~/work_items/graphql/remove_linked_items.mutation.graphql'; import { groupWorkItemByIidResponseFactory, workItemByIidResponseFactory, mockLinkedItems, mockBlockingLinkedItem, + removeLinkedWorkItemResponse, } from '../../mock_data'; describe('WorkItemRelationships', () => { @@ -30,17 +32,30 @@ describe('WorkItemRelationships', () => { const groupWorkItemsQueryHandler = jest .fn() .mockResolvedValue(groupWorkItemByIidResponseFactory()); + const removeLinkedWorkItemSuccessMutationHandler = jest + .fn() + .mockResolvedValue(removeLinkedWorkItemResponse('Successfully unlinked IDs: 2.')); + const removeLinkedWorkItemErrorMutationHandler = jest + .fn() + .mockResolvedValue(removeLinkedWorkItemResponse(null, ['Linked item removal failed'])); + const $toast = { + show: jest.fn(), + }; const createComponent = async ({ workItemQueryHandler = emptyLinkedWorkItemsQueryHandler, workItemType = 'Task', isGroup = false, + removeLinkedWorkItemMutationHandler = removeLinkedWorkItemSuccessMutationHandler, } = {}) => { + const mockApollo = createMockApollo([ + [workItemByIidQuery, workItemQueryHandler], + [removeLinkedItemsMutation, removeLinkedWorkItemMutationHandler], + [groupWorkItemByIidQuery, groupWorkItemsQueryHandler], + ]); + wrapper = shallowMountExtended(WorkItemRelationships, { - apolloProvider: createMockApollo([ - [workItemByIidQuery, workItemQueryHandler], - [groupWorkItemByIidQuery, groupWorkItemsQueryHandler], - ]), + apolloProvider: mockApollo, propsData: { workItemId: 'gid://gitlab/WorkItem/1', workItemIid: '1', @@ -50,6 +65,9 @@ describe('WorkItemRelationships', () => { provide: { isGroup, }, + mocks: { + $toast, + }, }); await waitForPromises(); @@ -59,6 +77,7 @@ describe('WorkItemRelationships', () => { const findWidgetWrapper = () => wrapper.findComponent(WidgetWrapper); const findEmptyRelatedMessageContainer = () => wrapper.findByTestId('links-empty'); const findLinkedItemsCountContainer = () => wrapper.findByTestId('linked-items-count'); + const findLinkedItemsHelpLink = () => wrapper.findByTestId('help-link'); const findAllWorkItemRelationshipListComponents = () => wrapper.findAllComponents(WorkItemRelationshipList); const findAddButton = () => wrapper.findByTestId('link-item-add-button'); @@ -77,6 +96,9 @@ describe('WorkItemRelationships', () => { expect(findEmptyRelatedMessageContainer().exists()).toBe(true); expect(findAddButton().exists()).toBe(true); expect(findWorkItemRelationshipForm().exists()).toBe(false); + expect(findLinkedItemsHelpLink().attributes('href')).toBe( + '/help/user/okrs.md#linked-items-in-okrs', + ); }); it('renders blocking linked item lists', async () => { @@ -158,4 +180,55 @@ describe('WorkItemRelationships', () => { expect(groupWorkItemsQueryHandler).toHaveBeenCalled(); }); }); + + it('removes linked item and shows toast message when removeLinkedItem event is emitted', async () => { + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })), + }); + + expect(findLinkedItemsCountContainer().text()).toBe('3'); + + await findAllWorkItemRelationshipListComponents() + .at(0) + .vm.$emit('removeLinkedItem', { id: 'gid://gitlab/WorkItem/2' }); + + await waitForPromises(); + + expect(removeLinkedWorkItemSuccessMutationHandler).toHaveBeenCalledWith({ + input: { + id: 'gid://gitlab/WorkItem/1', + workItemsIds: ['gid://gitlab/WorkItem/2'], + }, + }); + + expect($toast.show).toHaveBeenCalledWith('Linked item removed'); + + expect(findLinkedItemsCountContainer().text()).toBe('2'); + }); + + it.each` + errorType | mutationMock | errorMessage + ${'an error in the mutation response'} | ${removeLinkedWorkItemErrorMutationHandler} | ${'Linked item removal failed'} + ${'a network error'} | ${jest.fn().mockRejectedValue(new Error('Network Error'))} | ${'Something went wrong when removing item. Please refresh this page.'} + `( + 'shows an error message when there is $errorType while removing items', + async ({ mutationMock, errorMessage }) => { + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })), + removeLinkedWorkItemMutationHandler: mutationMock, + }); + + await findAllWorkItemRelationshipListComponents() + .at(0) + .vm.$emit('removeLinkedItem', { id: 'gid://gitlab/WorkItem/2' }); + + await waitForPromises(); + + expect(findWidgetWrapper().props('error')).toBe(errorMessage); + }, + ); }); diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 9cd76c0651e..820802e92d9 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -3484,6 +3484,18 @@ export const linkedWorkItemResponse = (options, errors = []) => { }; }; +export const removeLinkedWorkItemResponse = (message, errors = []) => { + return { + data: { + workItemRemoveLinkedItems: { + errors, + message, + __typename: 'WorkItemRemoveLinkedItemsPayload', + }, + }, + }; +}; + export const groupWorkItemsQueryResponse = { data: { group: { diff --git a/spec/graphql/resolvers/user_notes_count_resolver_spec.rb b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb index b3368d532b2..810dfc9c324 100644 --- a/spec/graphql/resolvers/user_notes_count_resolver_spec.rb +++ b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb @@ -27,6 +27,14 @@ RSpec.describe Resolvers::UserNotesCountResolver do it 'returns the number of non-system notes for the issue' do expect(subject).to eq(2) end + + context 'when not logged in' do + let(:user) { nil } + + it 'returns the number of non-system notes for the issue' do + expect(subject).to eq(2) + end + end end context 'when a user has permission to view notes' do @@ -65,6 +73,14 @@ RSpec.describe Resolvers::UserNotesCountResolver do it 'returns the number of non-system notes for the merge request' do expect(subject).to eq(2) end + + context 'when not logged in' do + let(:user) { nil } + + it 'returns the number of non-system notes for the merge request' do + expect(subject).to eq(2) + end + end end context 'when a user has permission to view notes' do diff --git a/spec/lib/api/entities/wiki_page_spec.rb b/spec/lib/api/entities/wiki_page_spec.rb index c75bba12484..a3566293c5c 100644 --- a/spec/lib/api/entities/wiki_page_spec.rb +++ b/spec/lib/api/entities/wiki_page_spec.rb @@ -22,6 +22,19 @@ RSpec.describe API::Entities::WikiPage do expect(subject[:content]).to eq wiki_page.content end + context "with front matter content" do + let(:wiki_page) { create(:wiki_page) } + let(:content_with_front_matter) { "---\nxxx: abc\n---\nHome Page" } + + before do + wiki_page.update(content: content_with_front_matter) # rubocop:disable Rails/SaveBang + end + + it 'returns the raw wiki page content' do + expect(subject[:content]).to eq content_with_front_matter + end + end + context 'when render_html param is passed' do context 'when it is true' do let(:params) { { render_html: true } } diff --git a/spec/lib/gitlab/rack_attack/request_spec.rb b/spec/lib/gitlab/rack_attack/request_spec.rb index 9d2144f75db..92c9acb83cf 100644 --- a/spec/lib/gitlab/rack_attack/request_spec.rb +++ b/spec/lib/gitlab/rack_attack/request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::RackAttack::Request do +RSpec.describe Gitlab::RackAttack::Request, feature_category: :rate_limiting do using RSpec::Parameterized::TableSyntax let(:path) { '/' } @@ -38,8 +38,12 @@ RSpec.describe Gitlab::RackAttack::Request do '/groups' | false '/foo/api' | false - '/api' | true + '/api' | false + '/api/' | true '/api/v4/groups/1' | true + + '/oauth/tokens' | true + '/oauth/userinfo' | true end with_them do @@ -53,6 +57,36 @@ RSpec.describe Gitlab::RackAttack::Request do it { is_expected.to eq(expected) } end end + + context 'when rate_limit_oauth_api feature flag is disabled' do + before do + stub_feature_flags(rate_limit_oauth_api: false) + end + + where(:path, :expected) do + '/' | false + '/groups' | false + '/foo/api' | false + + '/api' | true + '/api/v4/groups/1' | true + + '/oauth/tokens' | false + '/oauth/userinfo' | false + end + + with_them do + it { is_expected.to eq(expected) } + + context 'when the application is mounted at a relative URL' do + before do + stub_config_setting(relative_url_root: '/gitlab/root') + end + + it { is_expected.to eq(expected) } + end + end + end end describe '#api_internal_request?' do @@ -196,7 +230,8 @@ RSpec.describe Gitlab::RackAttack::Request do '/groups' | true '/foo/api' | true - '/api' | false + '/api' | true + '/api/' | false '/api/v4/groups/1' | false end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index ee61f191f05..2e1cb9d3d9b 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -394,6 +394,22 @@ RSpec.describe WikiPage, feature_category: :wiki do expect { subject.create(title: '') }.not_to change { wiki.list_pages.length } end end + + context "with front matter context" do + let(:attributes) do + { + title: SecureRandom.hex, + content: "---\nxxx: abc\n---\nHome Page", + format: "markdown", + message: 'Custom Commit Message' + } + end + + it 'create the page with front matter' do + subject.create(attributes) + expect(wiki.find_page(title).front_matter).to eq({ xxx: "abc" }) + end + end end describe "dot in the title" do diff --git a/spec/support/helpers/features/dom_helpers.rb b/spec/support/helpers/features/dom_helpers.rb index ac6523f3360..258a8c1ada9 100644 --- a/spec/support/helpers/features/dom_helpers.rb +++ b/spec/support/helpers/features/dom_helpers.rb @@ -2,8 +2,8 @@ module Features module DomHelpers - def find_by_testid(testid) - page.find("[data-testid='#{testid}']") + def find_by_testid(testid, **kwargs) + page.find("[data-testid='#{testid}']", **kwargs) end def within_testid(testid, &block) diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb index 05eab2953d7..784de102f4f 100644 --- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb @@ -45,7 +45,7 @@ RSpec.shared_examples 'User updates wiki page' do first(:link, text: 'three').click - expect(find('[data-testid="wiki_page_title"]')).to have_content('three') + expect(find('[data-testid="wiki-page-title"]')).to have_content('three') click_on('Edit') diff --git a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb index 767caffd417..254682e1a3a 100644 --- a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb @@ -57,7 +57,7 @@ RSpec.shared_examples 'User views a wiki page' do first(:link, text: 'three').click - expect(find('[data-testid="wiki_page_title"]')).to have_content('three') + expect(find('[data-testid="wiki-page-title"]')).to have_content('three') click_on('Edit') @@ -122,7 +122,7 @@ RSpec.shared_examples 'User views a wiki page' do it 'shows the page history' do visit(wiki_page_path(wiki, wiki_page)) - expect(page).to have_selector('[data-testid="wiki_edit_button"]') + expect(page).to have_selector('[data-testid="wiki-edit-button"]') click_on('Page history') @@ -134,7 +134,7 @@ RSpec.shared_examples 'User views a wiki page' do it 'does not show the "Edit" button' do visit(wiki_page_path(wiki, wiki_page, version_id: wiki_page.versions.last.id)) - expect(page).not_to have_selector('[data-testid="wiki_edit_button"]') + expect(page).not_to have_selector('[data-testid="wiki-edit-button"]') end context 'show the diff' do @@ -209,7 +209,7 @@ RSpec.shared_examples 'User views a wiki page' do it 'preserves the special characters' do visit(wiki_page_path(wiki, wiki_page)) - expect(page).to have_css('[data-testid="wiki_page_title"]', text: title) + expect(page).to have_css('[data-testid="wiki-page-title"]', text: title) expect(page).to have_css('.wiki-pages li', text: title) end end @@ -224,7 +224,7 @@ RSpec.shared_examples 'User views a wiki page' do it 'safely displays the page' do visit(wiki_page_path(wiki, wiki_page)) - expect(page).to have_selector('[data-testid="wiki_page_title"]', text: title) + expect(page).to have_selector('[data-testid="wiki-page-title"]', text: title) expect(page).to have_content('foo bar') end end @@ -251,7 +251,7 @@ RSpec.shared_examples 'User views a wiki page' do end it 'does not show "Edit" button' do - expect(page).not_to have_selector('[data-testid="wiki_edit_button"]') + expect(page).not_to have_selector('[data-testid="wiki-edit-button"]') end it 'shows error' do diff --git a/spec/views/admin/sessions/new.html.haml_spec.rb b/spec/views/admin/sessions/new.html.haml_spec.rb index c1ed8d4f4ef..81275fa8750 100644 --- a/spec/views/admin/sessions/new.html.haml_spec.rb +++ b/spec/views/admin/sessions/new.html.haml_spec.rb @@ -36,14 +36,15 @@ RSpec.describe 'admin/sessions/new.html.haml' do context 'omniauth authentication enabled' do before do allow(view).to receive(:omniauth_enabled?).and_return(true) - allow(view).to receive(:button_based_providers_enabled?).and_return(true) + allow(view).to receive(:password_authentication_enabled_for_web?).and_return(true) end it 'shows omniauth form' do render expect(rendered).not_to have_content _('No authentication methods configured.') - expect(rendered).to have_content _('or') + expect(rendered).to have_css('.omniauth-divider') + expect(rendered).to have_content(_('or sign in with')) expect(rendered).to have_css('.omniauth-container') end end diff --git a/yarn.lock b/yarn.lock index 1fe9e2a3169..9bd7969f657 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1025,10 +1025,10 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz#798622546b63847e82389e473fd67f2707d82247" integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g== -"@cubejs-client/core@^0.33.59": - version "0.33.59" - resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.33.59.tgz#985ec795ff94411f508a4c28b0f92347f1eaafc6" - integrity sha512-BNJnxDPYrLjiZU+OAp+qL/twHVdjKPH8aeVDx3oxrk8GR7hA9xN2bjoJxScdGqUNwtyXn9a9iu/tzsXcut0IBQ== +"@cubejs-client/core@^0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.34.0.tgz#f02504619a77be1fb70c3faf0b2576f975c9f05d" + integrity sha512-kzwpdPruuZrCiKRmO69G92TdXTb5mZvW2vyvdW6v71avYO10698cM5hivgeENz2bCeLkwKrc1Vx9KihZnDNr1Q== dependencies: "@babel/runtime" "^7.1.2" core-js "^3.6.5" @@ -1038,12 +1038,12 @@ url-search-params-polyfill "^7.0.0" uuid "^8.3.2" -"@cubejs-client/vue@^0.33.65": - version "0.33.65" - resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.33.65.tgz#1b1f371393173c2cb2fe23a1be4e8d91bf240083" - integrity sha512-vzFxMGlYrBxa+7fIEqG17mZqqKPSE83a+I1incb67ICqabN1lG8yw23Es/sl+mkM50/UprKF3kM48vfpyBK0KQ== +"@cubejs-client/vue@^0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.34.0.tgz#a61c1e6139b298ac19c1955b05ca2950efd82cfd" + integrity sha512-UYHRWbGufxt4AkB4CPP9D/+MRI9TTOi37GHJKBPux8DMSUNyR9u6pre+VPRAdgSRJk6LYUoENTAvaxRfytdnmQ== dependencies: - "@cubejs-client/core" "^0.33.59" + "@cubejs-client/core" "^0.34.0" core-js "^3.6.5" ramda "^0.27.2" |