Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-07-13 21:08:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-07-13 21:08:35 +0300
commite62d575077d6265497d7bbc7a31548ab15908a9e (patch)
tree5e10d28367cfe2652aacadd2ea0cdc8094c503cd
parentf62886ebffeda00b6972f1644097a6b9f5016453 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--app/assets/images/callouts/rich_text_editor_illustration.svg79
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor.vue2
-rw-r--r--app/assets/javascripts/deprecated_notes.js6
-rw-r--r--app/assets/javascripts/diffs/components/app.vue18
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue45
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue1
-rw-r--r--app/assets/javascripts/diffs/components/pre_renderer.vue83
-rw-r--r--app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js14
-rw-r--r--app/assets/javascripts/diffs/store/actions.js54
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js14
-rw-r--r--app/assets/javascripts/diffs/store/utils.js2
-rw-r--r--app/assets/javascripts/pages/shared/wikis/edit.js7
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue97
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js11
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue33
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/utils.js7
-rw-r--r--app/assets/javascripts/vue_shared/constants.js1
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue40
-rw-r--r--app/helpers/admin/application_settings/settings_helper.rb19
-rw-r--r--app/models/namespaces/traversal/linear_scopes.rb9
-rw-r--r--app/models/users/callout.rb3
-rw-r--r--app/views/admin/application_settings/_ai_access.html.haml5
-rw-r--r--app/views/discussions/_notes.html.haml24
-rw-r--r--app/views/shared/notes/_form.html.haml2
-rw-r--r--config/feature_flags/development/content_editor_on_issues.yml2
-rw-r--r--config/feature_flags/development/dynamically_compute_deployment_approval.yml8
-rw-r--r--config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml8
-rw-r--r--doc/api/graphql/reference/index.md8
-rw-r--r--doc/development/testing_guide/end_to_end/best_practices.md37
-rw-r--r--doc/user/admin_area/reporting/git_abuse_rate_limit.md4
-rw-r--r--doc/user/project/repository/code_suggestions.md6
-rw-r--r--locale/gitlab.pot30
-rw-r--r--package.json2
-rw-r--r--qa/qa/page/component/note.rb1
-rw-r--r--qa/qa/page/component/rich_text_popover.rb31
-rw-r--r--qa/qa/page/issuable/new.rb3
-rw-r--r--qa/qa/page/merge_request/show.rb2
-rw-r--r--qa/qa/page/project/issue/show.rb1
-rw-r--r--spec/features/discussion_comments/issue_spec.rb3
-rw-r--r--spec/features/groups/milestone_spec.rb6
-rw-r--r--spec/features/issuables/markdown_references/jira_spec.rb3
-rw-r--r--spec/features/issues/form_spec.rb2
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb4
-rw-r--r--spec/features/issues/markdown_toolbar_spec.rb3
-rw-r--r--spec/features/issues/note_polling_spec.rb3
-rw-r--r--spec/features/issues/notes_on_issues_spec.rb3
-rw-r--r--spec/features/issues/user_comments_on_issue_spec.rb2
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb2
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb2
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb2
-rw-r--r--spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb5
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/labels_hierarchy_spec.rb4
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb9
-rw-r--r--spec/features/merge_request/user_edits_merge_request_spec.rb4
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb6
-rw-r--r--spec/features/merge_request/user_merges_immediately_spec.rb3
-rw-r--r--spec/features/merge_request/user_merges_merge_request_spec.rb3
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb3
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb4
-rw-r--r--spec/features/merge_request/user_reverts_merge_request_spec.rb3
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb5
-rw-r--r--spec/features/merge_request/user_sees_discussions_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb6
-rw-r--r--spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb2
-rw-r--r--spec/features/merge_request/user_squashes_merge_request_spec.rb8
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb3
-rw-r--r--spec/features/projects/issuable_templates_spec.rb6
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb5
-rw-r--r--spec/frontend/diffs/components/diff_file_spec.js18
-rw-r--r--spec/frontend/diffs/store/actions_spec.js54
-rw-r--r--spec/frontend/diffs/store/mutations_spec.js1
-rw-r--r--spec/frontend/diffs/store/utils_spec.js6
-rw-r--r--spec/frontend/notes/components/comment_form_spec.js9
-rw-r--r--spec/frontend/notes/components/note_form_spec.js9
-rw-r--r--spec/frontend/pages/shared/wikis/components/wiki_form_spec.js9
-rw-r--r--spec/frontend/pipeline_wizard/components/editor_spec.js46
-rw-r--r--spec/frontend/vue_shared/components/markdown/editor_mode_switcher_spec.js100
-rw-r--r--spec/frontend/vue_shared/components/markdown/field_spec.js9
-rw-r--r--spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js11
-rw-r--r--spec/frontend/vue_shared/components/markdown/toolbar_spec.js58
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js25
-rw-r--r--spec/frontend_integration/content_editor/content_editor_integration_spec.js9
-rw-r--r--spec/helpers/admin/application_settings/settings_helper_spec.rb6
-rw-r--r--spec/models/namespace_spec.rb16
-rw-r--r--spec/support/helpers/content_editor_helpers.rb8
-rw-r--r--spec/support/shared_contexts/merge_request_create_shared_context.rb4
-rw-r--r--spec/support/shared_contexts/merge_request_edit_shared_context.rb3
-rw-r--r--spec/support/shared_examples/features/discussion_comments_shared_example.rb6
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb2
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
-rw-r--r--yarn.lock8
102 files changed, 844 insertions, 456 deletions
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 4b8d22fb6c6..6c3bb88f439 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-14.24.0
+14.23.0
diff --git a/app/assets/images/callouts/rich_text_editor_illustration.svg b/app/assets/images/callouts/rich_text_editor_illustration.svg
new file mode 100644
index 00000000000..b07d8871fe6
--- /dev/null
+++ b/app/assets/images/callouts/rich_text_editor_illustration.svg
@@ -0,0 +1,79 @@
+<svg width="280" height="130" viewBox="0 0 280 130" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_191_42179)">
+<circle cx="189.5" cy="-42.5" r="131.5" fill="url(#paint0_radial_191_42179)"/>
+<circle cx="-41.5" cy="-97.5" r="198.5" fill="url(#paint1_radial_191_42179)"/>
+<circle cx="309.5" cy="-7.5" r="121.5" fill="url(#paint2_radial_191_42179)"/>
+<g filter="url(#filter0_b_191_42179)">
+<path d="M0 4C0 1.79086 1.79086 0 4 0H276C278.209 0 280 1.79086 280 4V130H0V4Z" fill="white" fill-opacity="0.01"/>
+</g>
+</g>
+<g transform="translate(64, 16)">
+<path d="M135.455 109.089H47.0349C30.7979 109.089 17.6364 95.8523 17.6364 79.5229V0H106.056C122.293 0 135.455 13.2364 135.455 29.5658V109.091V109.089Z" fill="white"/>
+<path d="M37.0022 29H116C116 46 116 63 116 80C116 84.4183 112.549 88 108.293 88L37 88V29H37.0022Z" fill="white"/>
+<path d="M116 16H37V29H116V16Z" fill="#AEA5D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M36 15H101V16H37V42H36V15Z" fill="#171321"/>
+<path d="M53 22.5C53 23.8807 51.8807 25 50.5 25C49.1193 25 48 23.8807 48 22.5C48 21.1193 49.1193 20 50.5 20C51.8807 20 53 21.1193 53 22.5Z" fill="#A888F4"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M50.5 24C51.3284 24 52 23.3284 52 22.5C52 21.6716 51.3284 21 50.5 21C49.6716 21 49 21.6716 49 22.5C49 23.3284 49.6716 24 50.5 24ZM50.5 25C51.8807 25 53 23.8807 53 22.5C53 21.1193 51.8807 20 50.5 20C49.1193 20 48 21.1193 48 22.5C48 23.8807 49.1193 25 50.5 25Z" fill="#171321"/>
+<path d="M60 22.5C60 23.8807 58.8807 25 57.5 25C56.1193 25 55 23.8807 55 22.5C55 21.1193 56.1193 20 57.5 20C58.8807 20 60 21.1193 60 22.5Z" fill="#FF9D73"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M57.5 24C58.3284 24 59 23.3284 59 22.5C59 21.6716 58.3284 21 57.5 21C56.6716 21 56 21.6716 56 22.5C56 23.3284 56.6716 24 57.5 24ZM57.5 25C58.8807 25 60 23.8807 60 22.5C60 21.1193 58.8807 20 57.5 20C56.1193 20 55 21.1193 55 22.5C55 23.8807 56.1193 25 57.5 25Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M22.5 10.5C30.3325 10.5 38.152 14.4668 42.923 22.3723L43 22.5L42.923 22.6277C38.152 30.5332 30.3325 34.5 22.5 34.5C14.6675 34.5 6.84799 30.5332 2.07704 22.6277L2 22.5L2.07704 22.3723C6.84799 14.4668 14.6675 10.5 22.5 10.5Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41.8274 22.5C37.2212 15.1579 29.8576 11.5 22.5 11.5C15.1424 11.5 7.77878 15.1579 3.1726 22.5C7.77878 29.8421 15.1424 33.5 22.5 33.5C29.8576 33.5 37.2212 29.8421 41.8274 22.5ZM2 22.5L2.07704 22.6277C6.84799 30.5332 14.6675 34.5 22.5 34.5C30.3325 34.5 38.152 30.5332 42.923 22.6277L43 22.5L42.923 22.3723C38.152 14.4668 30.3325 10.5 22.5 10.5C14.6675 10.5 6.84799 14.4668 2.07704 22.3723L2 22.5Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 22.5C0 21.3954 0.895434 20.5 2 20.5C3.10457 20.5 4 21.3954 4 22.5C4 23.6046 3.10457 24.5 2 24.5C0.895434 24.5 0 23.6046 0 22.5Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2 21.5C1.44772 21.5 1 21.9477 1 22.5C1 23.0523 1.44772 23.5 2 23.5C2.55229 23.5 3 23.0523 3 22.5C3 21.9477 2.55229 21.5 2 21.5ZM2 20.5C0.895434 20.5 0 21.3954 0 22.5C0 23.6046 0.895434 24.5 2 24.5C3.10457 24.5 4 23.6046 4 22.5C4 21.3954 3.10457 20.5 2 20.5Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41 22.5C41 21.3954 41.8954 20.5 43 20.5C44.1046 20.5 45 21.3954 45 22.5C45 23.6046 44.1046 24.5 43 24.5C41.8954 24.5 41 23.6046 41 22.5Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M43 21.5C42.4477 21.5 42 21.9477 42 22.5C42 23.0523 42.4477 23.5 43 23.5C43.5523 23.5 44 23.0523 44 22.5C44 21.9477 43.5523 21.5 43 21.5ZM43 20.5C41.8954 20.5 41 21.3954 41 22.5C41 23.6046 41.8954 24.5 43 24.5C44.1046 24.5 45 23.6046 45 22.5C45 21.3954 44.1046 20.5 43 20.5Z" fill="#171321"/>
+<path d="M22.5 30C26.6421 30 30 26.6421 30 22.5C30 18.3579 26.6421 15 22.5 15C18.3579 15 15 18.3579 15 22.5C15 26.6421 18.3579 30 22.5 30Z" fill="#10B1B1"/>
+<path d="M27.0838 22.3192C27.0838 23.5746 25.3096 22.4248 23.8629 20.9715C22.4317 19.5337 21.3192 17.7969 22.5614 17.7969C25.0589 17.7969 27.0838 19.8217 27.0838 22.3192Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M37 34V65H36V34H37Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M37 93V70.0117H36V94H57V93H37Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M64 93H93V94H64V93Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M116 65V38H117V65H116Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M144 104H122V103H144V104Z" fill="#AEA5D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M138 97H129V96H138V97Z" fill="#AEA5D6"/>
+<path d="M104 34H47V46H104V34Z" fill="#E7E4F2"/>
+<path d="M74 51H48V83H74V51Z" fill="#FF9D73"/>
+<path d="M60.5 70C61.8807 70 63 68.8807 63 67.5C63 66.1193 61.8807 65 60.5 65C59.1193 65 58 66.1193 58 67.5C58 68.8807 59.1193 70 60.5 70Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M60.5 69C61.3284 69 62 68.3284 62 67.5C62 66.6716 61.3284 66 60.5 66C59.6716 66 59 66.6716 59 67.5C59 68.3284 59.6716 69 60.5 69ZM63 67.5C63 68.8807 61.8807 70 60.5 70C59.1193 70 58 68.8807 58 67.5C58 66.1193 59.1193 65 60.5 65C61.8807 65 63 66.1193 63 67.5Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M74 50V84H73V50H74Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M47 84V70H48V84H47Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M47 65V50H48V65H47Z" fill="#171321"/>
+<path d="M104 51H78V71H104V51Z" fill="#E7E4F2"/>
+<path d="M104 76H78V83H104V76Z" fill="#E7E4F2"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M23 34V58.5C23 63.1944 26.8056 67 31.5 67H59V68H31.5C26.2533 68 22 63.7467 22 58.5V34H23Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M46 51H75V52H46V51Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M75 83H46V82H75V83Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14 67H32V68H14V67Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2 67H10V68H2V67Z" fill="#AEA5D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M58.5 69.5V95.5C58.5 100.194 62.3056 104 67 104H94L93.5 105H67C61.7533 105 57.5 100.747 57.5 95.5V69.5H58.5Z" fill="#171321"/>
+<rect x="130.598" y="54.4473" width="19" height="46" transform="rotate(45 130.598 54.4473)" fill="white"/>
+<path d="M111.506 100.41L98.0714 86.9746L93.4752 105.006L111.506 100.41Z" fill="#FF9D73"/>
+<path d="M140.498 44.5479L144.033 48.0834C146.666 50.7156 147.982 52.0318 148.701 53.443C150.154 56.2951 150.154 59.6706 148.701 62.5228C147.982 63.934 146.666 65.2501 144.033 67.8824L144.033 67.8824L130.598 54.4473L140.498 44.5479Z" fill="#5829CB"/>
+<path d="M130.598 54.4473L131.305 55.1544L98.7785 87.6813L98.0714 86.9742L130.598 54.4473Z" fill="#171321"/>
+<path d="M143.326 67.1758L144.033 67.8829L111.506 100.41L110.799 99.7027L143.326 67.1758Z" fill="#171321"/>
+<path d="M136.962 60.8115L137.669 61.5186L105.142 94.0455L104.435 93.3384L136.962 60.8115Z" fill="#171321"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M95.4861 97.1172L93.4752 105.006L101.364 102.995L95.4861 97.1172Z" fill="#171321"/>
+</g>
+<defs>
+<filter id="filter0_b_191_42179" x="-50" y="-50" width="380" height="230" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feGaussianBlur in="BackgroundImageFix" stdDeviation="25"/>
+<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_191_42179"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_191_42179" result="shape"/>
+</filter>
+<radialGradient id="paint0_radial_191_42179" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(189.5 -42.5) rotate(89.5818) scale(125.986)">
+<stop stop-color="#7759C2"/>
+<stop offset="1" stop-color="#7759C2" stop-opacity="0"/>
+</radialGradient>
+<radialGradient id="paint1_radial_191_42179" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-41.5 -97.5) rotate(89.5818) scale(190.176)">
+<stop stop-color="#D64028"/>
+<stop offset="1" stop-color="#D64028" stop-opacity="0"/>
+</radialGradient>
+<radialGradient id="paint2_radial_191_42179" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(309.5 -7.5) rotate(89.5818) scale(116.405)">
+<stop stop-color="#EF76F1"/>
+<stop offset="1" stop-color="#EF76F1" stop-opacity="0"/>
+</radialGradient>
+<clipPath id="clip0_191_42179">
+<path d="M0 4C0 1.79086 1.79086 0 4 0H276C278.209 0 280 1.79086 280 4V130H0V4Z" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index c6b605cd92f..1036b6552d1 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -264,7 +264,7 @@ export default {
<div
class="gl-display-flex gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base gl-px-2 gl-border-t gl-border-gray-100 gl-text-secondary"
>
- <editor-mode-switcher size="small" value="richText" @input="handleEditorModeChanged" />
+ <editor-mode-switcher size="small" value="richText" @switch="handleEditorModeChanged" />
<gl-button
v-gl-tooltip
icon="markdown-mark"
diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js
index 489c6f7e1c4..6dbf12054cf 100644
--- a/app/assets/javascripts/deprecated_notes.js
+++ b/app/assets/javascripts/deprecated_notes.js
@@ -1462,7 +1462,11 @@ export default class Notes {
$note.addClass('fade-in-full');
renderGFM(Notes.getNodeToRender($note));
- $notesList.append($note);
+ if ($notesList.find('.discussion-reply-holder').length) {
+ $notesList.children('.timeline-entry').last().after($note);
+ } else {
+ $notesList.append($note);
+ }
return $note;
}
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index c0a9643e59e..b44688aa467 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -58,7 +58,6 @@ import HiddenFilesWarning from './hidden_files_warning.vue';
import NoChanges from './no_changes.vue';
import TreeList from './tree_list.vue';
import VirtualScrollerScrollSync from './virtual_scroller_scroll_sync';
-import PreRenderer from './pre_renderer.vue';
export default {
name: 'DiffsApp',
@@ -66,7 +65,6 @@ export default {
FindingsDrawer,
DynamicScroller,
DynamicScrollerItem,
- PreRenderer,
VirtualScrollerScrollSync,
CompareVersions,
DiffFile,
@@ -665,22 +663,6 @@ export default {
</dynamic-scroller-item>
</template>
<template #before>
- <pre-renderer :max-length="diffFilesLength">
- <template #default="{ item, index, active }">
- <dynamic-scroller-item :item="item" :active="active">
- <diff-file
- :file="item"
- :reviewed="fileReviews[item.id]"
- :is-first-file="index === 0"
- :is-last-file="index === diffFilesLength - 1"
- :help-page-path="helpPagePath"
- :can-current-user-fork="canCurrentUserFork"
- :view-diffs-file-by-file="viewDiffsFileByFile"
- pre-render
- />
- </dynamic-scroller-item>
- </template>
- </pre-renderer>
<virtual-scroller-scroll-sync v-model="virtualScrollCurrentIndex" />
</template>
</dynamic-scroller>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index dfb2e192711..4dfa9186c6d 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -88,11 +88,6 @@ export default {
required: false,
default: true,
},
- preRender: {
- type: Boolean,
- required: false,
- default: false,
- },
},
idState() {
return {
@@ -122,7 +117,7 @@ export default {
return getShortShaFromFile(this.file);
},
showLoadingIcon() {
- return this.idState.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
+ return this.idState.isLoadingCollapsedDiff;
},
hasDiff() {
return hasDiff(this.file);
@@ -205,8 +200,6 @@ export default {
watch: {
'file.id': {
handler: function fileIdHandler() {
- if (this.preRender) return;
-
this.manageViewedEffects();
},
},
@@ -219,7 +212,6 @@ export default {
newHash &&
oldHash &&
!this.hasDiff &&
- !this.preRender &&
!this.idState.hasLoadedCollapsedDiff
) {
this.requestDiff();
@@ -228,14 +220,10 @@ export default {
},
},
created() {
- if (this.preRender) return;
-
notesEventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff);
eventHub.$on(EVT_EXPAND_ALL_FILES, this.expandAllListener);
},
mounted() {
- if (this.preRender) return;
-
if (this.hasDiff) {
this.postRender();
}
@@ -243,15 +231,12 @@ export default {
this.manageViewedEffects();
},
beforeDestroy() {
- if (this.preRender) return;
-
eventHub.$off(EVT_EXPAND_ALL_FILES, this.expandAllListener);
},
methods: {
...mapActions('diffs', [
'loadCollapsedDiff',
'assignDiscussionsToDiff',
- 'setRenderIt',
'setFileCollapsedByUser',
'saveDiffDiscussion',
'toggleFileCommentForm',
@@ -315,10 +300,6 @@ export default {
.then(() => {
idState.isLoadingCollapsedDiff = false;
idState.hasLoadedCollapsedDiff = true;
-
- if (this.file.file_hash === file.file_hash) {
- this.setRenderIt(this.file);
- }
})
.then(() => {
if (this.file.file_hash !== file.file_hash) return;
@@ -378,15 +359,14 @@ export default {
<template>
<div
- :id="!preRender && active && file.file_hash"
+ :id="file.file_hash"
:class="{
- 'is-active': currentDiffFileId === file.file_hash,
'comments-disabled': Boolean(file.brokenSymlink),
'has-body': showBody,
'is-virtual-scrolling': isVirtualScrollingEnabled,
}"
:data-path="file.new_path"
- class="diff-file file-holder gl-border-none"
+ class="diff-file file-holder gl-border-none gl-mb-0! gl-pb-5"
>
<diff-file-header
:can-current-user-fork="canCurrentUserFork"
@@ -426,7 +406,7 @@ export default {
</div>
<template v-else>
<div
- :id="!preRender && active && `diff-content-${file.file_hash}`"
+ :id="`diff-content-${file.file_hash}`"
:class="hasBodyClasses.contentByHash"
data-testid="content-area"
>
@@ -550,20 +530,3 @@ export default {
</template>
</div>
</template>
-
-<style>
-@keyframes shadow-fade {
- from {
- box-shadow: 0 0 4px #919191;
- }
-
- to {
- box-shadow: 0 0 0 #dfdfdf;
- }
-}
-
-.diff-file.is-active {
- box-shadow: 0 0 0 #dfdfdf;
- animation: shadow-fade 1.2s 0.1s 1;
-}
-</style>
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 4d2bbc8dd6f..a0679bafddc 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -428,6 +428,7 @@ export default {
toggle-class="btn-icon js-diff-more-actions"
class="gl-pt-0!"
data-qa-selector="dropdown_button"
+ lazy
@show="setMoreActionsShown(true)"
@hidden="setMoreActionsShown(false)"
>
diff --git a/app/assets/javascripts/diffs/components/pre_renderer.vue b/app/assets/javascripts/diffs/components/pre_renderer.vue
deleted file mode 100644
index e4320c40d2c..00000000000
--- a/app/assets/javascripts/diffs/components/pre_renderer.vue
+++ /dev/null
@@ -1,83 +0,0 @@
-<script>
-export default {
- inject: ['vscrollParent'],
- props: {
- maxLength: {
- type: Number,
- required: true,
- },
- },
- data() {
- return {
- nextIndex: -1,
- nextItem: null,
- startedRender: false,
- width: 0,
- };
- },
- mounted() {
- this.width = this.$el.parentNode.offsetWidth;
-
- this.$_itemsWithSizeWatcher = this.$watch('vscrollParent.itemsWithSize', async () => {
- await this.$nextTick();
-
- const nextItem = this.findNextToRender();
-
- if (nextItem) {
- this.startedRender = true;
- requestIdleCallback(() => {
- this.nextItem = nextItem;
-
- if (this.nextIndex === this.maxLength - 1) {
- this.$nextTick(() => {
- if (this.vscrollParent.itemsWithSize[this.maxLength - 1].size !== 0) {
- this.clearRendering();
- }
- });
- }
- });
- } else if (this.startedRender) {
- this.clearRendering();
- }
- });
- },
- beforeDestroy() {
- this.$_itemsWithSizeWatcher();
- },
- methods: {
- clearRendering() {
- this.nextItem = null;
-
- if (this.maxLength === this.vscrollParent.itemsWithSize.length) {
- this.$_itemsWithSizeWatcher();
- }
- },
- findNextToRender() {
- return this.vscrollParent.itemsWithSize.find(({ size }, index) => {
- const isNext = size === 0;
-
- if (isNext) {
- this.nextIndex = index;
- }
-
- return isNext;
- });
- },
- },
-};
-</script>
-
-<template>
- <div v-if="nextItem" :style="{ width: `${width}px` }" class="gl-absolute diff-file-offscreen">
- <slot
- v-bind="{ item: nextItem.item, index: nextIndex, active: true, itemWithSize: nextItem }"
- ></slot>
- </div>
-</template>
-
-<style scoped>
-.diff-file-offscreen {
- top: -200%;
- left: -200%;
-}
-</style>
diff --git a/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js b/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js
index d44dffecc38..cd9faffd99d 100644
--- a/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js
+++ b/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js
@@ -18,17 +18,18 @@ export default {
if (index < 0) return;
- if (this.vscrollParent.itemsWithSize[index].size) {
- this.scrollToIndex(index);
- } else {
+ this.scrollToIndex(index);
+
+ if (!this.vscrollParent.itemsWithSize[index].size) {
this.$_itemsWithSizeWatcher = this.$watch('vscrollParent.itemsWithSize', async () => {
await this.$nextTick();
if (this.vscrollParent.itemsWithSize[index].size) {
this.$_itemsWithSizeWatcher();
- this.scrollToIndex(index);
await this.$nextTick();
+
+ this.scrollToIndex(index);
}
});
}
@@ -40,12 +41,15 @@ export default {
if (this.$_itemsWithSizeWatcher) this.$_itemsWithSizeWatcher();
},
methods: {
- scrollToIndex(index) {
+ async scrollToIndex(index) {
this.vscrollParent.scrollToItem(index);
this.$emit('update', -1);
+ await this.$nextTick();
+
setTimeout(() => {
handleLocationHash();
+ window.scrollBy(0, 150);
});
},
},
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 2f401e78f9c..2a557017953 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -6,7 +6,6 @@ import {
scrollToElement,
} from '~/lib/utils/common_utils';
import { createAlert, VARIANT_WARNING } from '~/alert';
-import { diffViewerModes } from '~/ide/constants';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
@@ -60,7 +59,6 @@ import {
ERROR_DISMISSING_SUGESTION_POPOVER,
} from '../i18n';
import eventHub from '../event_hub';
-import { isCollapsed } from '../utils/diff_file';
import { markFileReview, setReviewsForMergeRequest } from '../utils/file_reviews';
import { getDerivedMergeRequestInformation } from '../utils/merge_request';
import { queueRedisHllEvents } from '../utils/queue_events';
@@ -179,7 +177,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
};
const hash = window.location.hash.replace('#', '').split('diff-content-').pop();
let totalLoaded = 0;
- let scrolledVirtualScroller = false;
+ let scrolledVirtualScroller = hash === '';
commit(types.SET_BATCH_LOADING_STATE, 'loading');
commit(types.SET_RETRIEVING_BATCHES, true);
@@ -251,8 +249,6 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
return nextPage;
})
.then((nextPage) => {
- dispatch('startRenderDiffsQueue');
-
if (nextPage) {
return getBatch(nextPage);
}
@@ -385,10 +381,6 @@ export const renderFileForDiscussionId = ({ commit, rootState, state }, discussi
const file = state.diffFiles.find((f) => f.file_hash === discussion.diff_file.file_hash);
if (file) {
- if (!file.renderIt) {
- commit(types.RENDER_FILE, file);
- }
-
if (file.viewer.automaticallyCollapsed) {
notesEventHub.$emit(`loadCollapsedDiff/${file.file_hash}`);
scrollToElement(document.getElementById(file.file_hash));
@@ -406,46 +398,6 @@ export const renderFileForDiscussionId = ({ commit, rootState, state }, discussi
}
};
-export const startRenderDiffsQueue = ({ state, commit }) => {
- const diffFilesToRender = state.diffFiles.filter(
- (file) =>
- !file.renderIt &&
- file.viewer &&
- (!isCollapsed(file) || file.viewer.name !== diffViewerModes.text),
- );
- let currentDiffFileIndex = 0;
-
- const checkItem = () => {
- const nextFile = diffFilesToRender[currentDiffFileIndex];
-
- if (nextFile) {
- let retryCount = 0;
- currentDiffFileIndex += 1;
- commit(types.RENDER_FILE, nextFile);
-
- const requestIdle = () =>
- requestIdleCallback((idleDeadline) => {
- // Wait for at least 5ms before trying to render
- // or for 5 tries and then force render the file
- if (idleDeadline.timeRemaining() >= 5 || retryCount > 4) {
- checkItem();
- } else {
- requestIdle();
- retryCount += 1;
- }
- });
-
- requestIdle();
- }
- };
-
- if (diffFilesToRender.length) {
- checkItem();
- }
-};
-
-export const setRenderIt = ({ commit }, file) => commit(types.RENDER_FILE, file);
-
export const setInlineDiffViewType = ({ commit }) => {
commit(types.SET_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE);
@@ -846,7 +798,7 @@ export const toggleFullDiff = ({ dispatch, commit, getters, state }, filePath) =
}
};
-export function switchToFullDiffFromRenamedFile({ commit, dispatch }, { diffFile }) {
+export function switchToFullDiffFromRenamedFile({ commit }, { diffFile }) {
return axios
.get(diffFile.context_lines_path, {
params: {
@@ -873,8 +825,6 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch }, { diffFile
},
});
commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, { filePath: diffFile.file_path, lines });
-
- dispatch('startRenderDiffsQueue');
});
}
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 1ea207d0043..4855ca87e91 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -23,12 +23,6 @@ function updateDiffFilesInState(state, files) {
return Object.assign(state, { diffFiles: files });
}
-function renderFile(file) {
- Object.assign(file, {
- renderIt: true,
- });
-}
-
export default {
[types.SET_BASE_CONFIG](state, options) {
const {
@@ -102,10 +96,6 @@ export default {
Object.assign(state, { coverageFiles, coverageLoaded: true });
},
- [types.RENDER_FILE](state, file) {
- renderFile(file);
- },
-
[types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) {
Object.assign(state, {
mergeRequestDiffs,
@@ -355,10 +345,6 @@ export default {
file.viewer.manuallyCollapsed = null;
}
}
-
- if (file && !collapsed) {
- renderFile(file);
- }
},
[types.SET_CURRENT_VIEW_DIFF_FILE_LINES](state, { filePath, lines }) {
const file = state.diffFiles.find((f) => f.file_path === filePath);
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 68536d36ac0..97dfd351e67 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -307,7 +307,6 @@ function mergeTwoFiles(target, source) {
...target,
[INLINE_DIFF_LINES_KEY]: missingInline ? source[INLINE_DIFF_LINES_KEY] : originalInline,
parallel_diff_lines: null,
- renderIt: source.renderIt,
collapsed: source.collapsed,
};
}
@@ -388,7 +387,6 @@ function prepareDiffFileLines(file) {
function finalizeDiffFile(file) {
Object.assign(file, {
- renderIt: true,
isShowingFullFile: false,
isLoadingFullFile: false,
discussions: [],
diff --git a/app/assets/javascripts/pages/shared/wikis/edit.js b/app/assets/javascripts/pages/shared/wikis/edit.js
index 0044575de62..a0a7cc0b07a 100644
--- a/app/assets/javascripts/pages/shared/wikis/edit.js
+++ b/app/assets/javascripts/pages/shared/wikis/edit.js
@@ -1,5 +1,7 @@
import $ from 'jquery';
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createApolloClient from '~/lib/graphql';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
import Translate from '~/vue_shared/translate';
@@ -64,9 +66,14 @@ const createWikiFormApp = () => {
if (el) {
const { pageInfo, formatOptions } = el.dataset;
+ Vue.use(VueApollo);
+
+ const apolloProvider = new VueApollo({ defaultClient: createApolloClient() });
+
// eslint-disable-next-line no-new
new Vue({
el,
+ apolloProvider,
provide: {
formatOptions: JSON.parse(formatOptions),
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
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 81456aed8d7..2426a917a53 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
@@ -1,10 +1,16 @@
<script>
-import { GlButton } from '@gitlab/ui';
+import { GlButton, GlPopover, GlLink } from '@gitlab/ui';
+import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import { __ } from '~/locale';
+import RICH_TEXT_EDITOR_ILLUSTRATION from '../../../../images/callouts/rich_text_editor_illustration.svg?url';
+import { counter } from './utils';
export default {
components: {
GlButton,
+ GlLink,
+ GlPopover,
+ UserCalloutDismisser,
},
props: {
value: {
@@ -12,7 +18,15 @@ export default {
required: true,
},
},
+ data() {
+ return {
+ counter: counter(),
+ };
+ },
computed: {
+ showPromoPopover() {
+ return this.markdownEditorSelected && this.counter === 0;
+ },
markdownEditorSelected() {
return this.value === 'markdown';
},
@@ -22,15 +36,84 @@ export default {
: __('Switch to plain text editing');
},
},
+ methods: {
+ switchEditorType(insertTemplate = false) {
+ this.$emit('switch', insertTemplate);
+ },
+ },
+ richTextEditorButtonId: 'switch-to-rich-text-editor',
+ RICH_TEXT_EDITOR_ILLUSTRATION,
};
</script>
<template>
<div class="content-editor-switcher gl-display-inline-flex gl-align-items-center">
- <gl-button
- 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"
- @click="$emit('input')"
- >{{ text }}</gl-button
- >
+ <user-callout-dismisser feature-name="rich_text_editor">
+ <template #default="{ dismiss, shouldShowCallout }">
+ <div>
+ <gl-popover
+ :target="$options.richTextEditorButtonId"
+ :show="Boolean(showPromoPopover && shouldShowCallout)"
+ show-close-button
+ :css-classes="['rich-text-promo-popover gl-p-2']"
+ triggers="manual"
+ data-testid="rich-text-promo-popover"
+ @close-button-clicked="dismiss"
+ >
+ <img
+ :src="$options.RICH_TEXT_EDITOR_ILLUSTRATION"
+ :alt="''"
+ class="rich-text-promo-popover-illustration"
+ width="280"
+ height="130"
+ />
+ <h5 class="gl-mt-3 gl-mb-3">{{ __('Writing just got easier') }}</h5>
+ <p class="gl-m-0">
+ {{
+ __(
+ 'Use the new rich text editor to see your text and tables fully formatted as you type. No need to remember any formatting syntax, or switch between preview and editing modes!',
+ )
+ }}
+ </p>
+ <gl-link
+ class="gl-button btn btn-confirm block gl-mb-2 gl-mt-4"
+ variant="confirm"
+ category="primary"
+ target="_blank"
+ block
+ @click="
+ switchEditorType(showPromoPopover);
+ dismiss();
+ "
+ >
+ {{ __('Try the rich text editor now') }}
+ </gl-link>
+ </gl-popover>
+ <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"
+ @click="
+ switchEditorType();
+ dismiss();
+ "
+ >{{ text }}</gl-button
+ >
+ </div>
+ </template>
+ </user-callout-dismisser>
</div>
</template>
+<style>
+.rich-text-promo-popover {
+ box-shadow: 0 0 18px -1.9px rgba(119, 89, 194, 0.16), 0 0 12.9px -1.7px rgba(119, 89, 194, 0.16),
+ 0 0 9.2px -1.4px rgba(119, 89, 194, 0.16), 0 0 6.4px -1.1px rgba(119, 89, 194, 0.16),
+ 0 0 4.5px -0.8px rgba(119, 89, 194, 0.16), 0 0 3px -0.6px rgba(119, 89, 194, 0.16),
+ 0 0 1.8px -0.3px rgba(119, 89, 194, 0.16), 0 0 0.6px rgba(119, 89, 194, 0.16);
+ z-index: 999;
+}
+
+.rich-text-promo-popover-illustration {
+ width: calc(100% + 32px);
+ margin: -32px -16px 0;
+}
+</style>
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 186b70a9ecd..8b8247a5b2c 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
@@ -5,6 +5,7 @@ import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { updateDraft, clearDraft, getDraft } from '~/lib/utils/autosave';
import { setUrlParams, joinPaths } from '~/lib/utils/url_utility';
import {
+ EDITING_MODE_KEY,
EDITING_MODE_MARKDOWN_FIELD,
EDITING_MODE_CONTENT_EDITOR,
CLEAR_AUTOSAVE_ENTRY_EVENT,
@@ -223,6 +224,7 @@ export default {
}
},
},
+ EDITING_MODE_KEY,
};
</script>
<template>
@@ -230,7 +232,7 @@ export default {
<local-storage-sync
:value="editingMode"
as-string
- storage-key="gl-markdown-editor-mode"
+ :storage-key="$options.EDITING_MODE_KEY"
@input="onEditingModeRestored"
/>
<markdown-field
diff --git a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js
index 2aa8f3b2442..0b0867ae84c 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js
+++ b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js
@@ -95,15 +95,8 @@ export function mountMarkdownEditor(options = {}) {
const setFacade = (props) => Object.assign(facade, props);
const autosaveKey = `autosave/${document.location.pathname}/${searchTerm}/description`;
- if (options.useApollo || options.apolloProvider) {
- let { apolloProvider } = options;
-
- if (!apolloProvider) {
- apolloProvider = new VueApollo({ defaultClient: createApolloClient() });
- }
-
- componentConfiguration.apolloProvider = apolloProvider;
- }
+ componentConfiguration.apolloProvider =
+ options.apolloProvider || new VueApollo({ defaultClient: createApolloClient() });
// eslint-disable-next-line no-new
new Vue({
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index 7cce42629d6..d4b1abedc02 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -1,5 +1,8 @@
<script>
import { GlButton, GlLoadingIcon, GlSprintf, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { updateText } from '~/lib/utils/text_markdown';
+import { __, sprintf } from '~/locale';
+import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
import EditorModeSwitcher from './editor_mode_switcher.vue';
export default {
@@ -40,7 +43,33 @@ export default {
},
},
methods: {
- handleEditorModeChanged() {
+ insertIntoTextarea(...lines) {
+ const text = lines.join('\n');
+ const textArea = this.$el.closest('.md-area')?.querySelector('textarea');
+ if (textArea && !textArea.value) {
+ updateText({
+ textArea,
+ tag: text,
+ cursorOffset: 0,
+ wrap: false,
+ });
+ }
+ },
+ handleEditorModeChanged(isFirstSwitch) {
+ if (isFirstSwitch) {
+ this.insertIntoTextarea(
+ __(`### Rich text editor`),
+ '',
+ sprintf(
+ __(
+ 'Try out **styling** _your_ content right here or read the [direction](%{directionUrl}).',
+ ),
+ {
+ directionUrl: `${PROMO_URL}/direction/plan/knowledge/content_editor/`,
+ },
+ ),
+ );
+ }
this.$emit('enableContentEditor');
},
},
@@ -61,7 +90,7 @@ export default {
v-if="showEditorModeSwitcher"
size="small"
value="markdown"
- @input="handleEditorModeChanged"
+ @switch="handleEditorModeChanged"
/>
<div class="gl-display-flex">
<div v-if="canAttachFile" class="uploading-container gl-font-sm gl-line-height-32 gl-mr-3">
diff --git a/app/assets/javascripts/vue_shared/components/markdown/utils.js b/app/assets/javascripts/vue_shared/components/markdown/utils.js
new file mode 100644
index 00000000000..0227d5a0fbc
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/markdown/utils.js
@@ -0,0 +1,7 @@
+let i = 0;
+
+export const counter = () => {
+ const n = i;
+ i += 1;
+ return n;
+};
diff --git a/app/assets/javascripts/vue_shared/constants.js b/app/assets/javascripts/vue_shared/constants.js
index 3896e963a53..8946a02e663 100644
--- a/app/assets/javascripts/vue_shared/constants.js
+++ b/app/assets/javascripts/vue_shared/constants.js
@@ -94,6 +94,7 @@ export const confidentialityInfoText = (workspaceType, issuableType) =>
},
);
+export const EDITING_MODE_KEY = 'gl-markdown-editor-mode';
export const EDITING_MODE_MARKDOWN_FIELD = 'markdownField';
export const EDITING_MODE_CONTENT_EDITOR = 'contentEditor';
export const CLEAR_AUTOSAVE_ENTRY_EVENT = 'markdown_clear_autosave_entry';
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
index 3d4eebb9524..53e976d698b 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
@@ -9,14 +9,16 @@ import {
} from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { STATUS_OPEN } from '~/issues/constants';
+import { issuableStatusText, STATUS_OPEN } from '~/issues/constants';
import { isExternal } from '~/lib/utils/url_utility';
import { n__, sprintf } from '~/locale';
+import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
export default {
components: {
+ ConfidentialityBadge,
GlIcon,
GlBadge,
GlButton,
@@ -77,8 +79,16 @@ export default {
required: false,
default: false,
},
+ workspaceType: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
+ badgeText() {
+ return issuableStatusText[this.issuableState];
+ },
badgeVariant() {
return this.issuableState === STATUS_OPEN ? 'success' : 'info';
},
@@ -109,6 +119,7 @@ export default {
},
methods: {
handleRightSidebarToggleClick() {
+ this.$emit('toggle');
if (this.toggleSidebarButtonEl) {
this.toggleSidebarButtonEl.dispatchEvent(new Event('click'));
}
@@ -118,21 +129,23 @@ export default {
</script>
<template>
- <div class="detail-page-header">
+ <div class="detail-page-header gl-flex-direction-column gl-sm-flex-direction-row">
<div class="detail-page-header-body">
<gl-badge class="issuable-status-badge gl-mr-3" :variant="badgeVariant">
<gl-icon v-if="statusIcon" :name="statusIcon" :class="statusIconClass" />
- <span class="gl-display-none gl-sm-display-block"><slot name="status-badge"></slot></span>
+ <span class="gl-display-none gl-sm-display-block gl-ml-2">
+ <slot name="status-badge">{{ badgeText }}</slot>
+ </span>
</gl-badge>
- <div class="issuable-meta gl-display-flex! gl-align-items-center">
- <div v-if="blocked || confidential" class="gl-display-inline-block">
- <div v-if="blocked" data-testid="blocked" class="issuable-warning-icon inline">
- <gl-icon name="lock" :aria-label="__('Blocked')" />
- </div>
- <div v-if="confidential" data-testid="confidential" class="issuable-warning-icon inline">
- <gl-icon name="eye-slash" :aria-label="__('Confidential')" />
- </div>
+ <div class="issuable-meta gl-display-flex! gl-align-items-center gl-flex-wrap">
+ <div v-if="blocked" data-testid="blocked" class="issuable-warning-icon inline">
+ <gl-icon name="lock" :aria-label="__('Blocked')" />
</div>
+ <confidentiality-badge
+ v-if="confidential"
+ :issuable-type="issuableType"
+ :workspace-type="workspaceType"
+ />
<span>
<template v-if="showWorkItemTypeIcon">
<work-item-type-icon :work-item-type="issuableType" show-text />
@@ -182,10 +195,7 @@ export default {
@click="handleRightSidebarToggleClick"
/>
</div>
- <div
- data-testid="header-actions"
- class="detail-page-header-actions gl-display-flex gl-md-display-block"
- >
+ <div data-testid="header-actions" class="detail-page-header-actions gl-display-flex">
<slot name="header-actions"></slot>
</div>
</div>
diff --git a/app/helpers/admin/application_settings/settings_helper.rb b/app/helpers/admin/application_settings/settings_helper.rb
index 0a7f20caa02..9ea07ba4e6e 100644
--- a/app/helpers/admin/application_settings/settings_helper.rb
+++ b/app/helpers/admin/application_settings/settings_helper.rb
@@ -16,12 +16,23 @@ module Admin
project.repository&.gitlab_ci_yml.blank?
end
+ def code_suggestions_description
+ link_start = code_suggestions_link_start(code_suggestions_docs_url)
+
+ # rubocop:disable Layout/LineLength
+ # rubocop:disable Style/FormatString
+ s_('CodeSuggestionsSM|Enable Code Suggestions for users of this instance. %{link_start}What are Code Suggestions?%{link_end}')
+ .html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
+ # rubocop:enable Style/FormatString
+ # rubocop:enable Layout/LineLength
+ end
+
def code_suggestions_token_explanation
link_start = code_suggestions_link_start(code_suggestions_pat_docs_url)
# rubocop:disable Layout/LineLength
# rubocop:disable Style/FormatString
- s_('CodeSuggestionsSM|Your personal access token from GitLab.com. See the %{link_start}documentation%{link_end} for information on creating a personal access token.')
+ s_('CodeSuggestionsSM|On GitLab.com, create a token. This token is required to use Code Suggestions on your self-managed instance. %{link_start}How do I create a token?%{link_end}')
.html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
# rubocop:enable Style/FormatString
# rubocop:enable Layout/LineLength
@@ -33,8 +44,8 @@ module Admin
# rubocop:disable Layout/LineLength
# rubocop:disable Style/FormatString
- s_('CodeSuggestionsSM|&#8226; Agree to the %{terms_link_start}GitLab Testing Agreement%{link_end}.%{br} &#8226; Acknowledge that GitLab will send data from the instance, including personal data, to Google for cloud hosting.%{br} &nbsp;&nbsp;&nbsp;We may also send data to %{ai_docs_link_start}third-party AI providers%{link_end} to provide this feature.')
- .html_safe % { terms_link_start: terms_link_start, ai_docs_link_start: ai_docs_link_start, link_end: '</a>'.html_safe, br: '</br>'.html_safe }
+ s_('CodeSuggestionsSM|By enabling this feature, you agree to the %{terms_link_start}GitLab Testing Agreement%{link_end} and acknowledge that GitLab will send data from the instance, including personal data, to our %{ai_docs_link_start}AI providers%{link_end} to provide this feature.')
+ .html_safe % { terms_link_start: terms_link_start, ai_docs_link_start: ai_docs_link_start, link_end: '</a>'.html_safe }
# rubocop:enable Style/FormatString
# rubocop:enable Layout/LineLength
end
@@ -53,7 +64,7 @@ module Admin
end
def code_suggestions_ai_docs_url
- 'https://docs.gitlab.com/ee/user/ai_features.html'
+ 'https://docs.gitlab.com/ee/user/ai_features.html#third-party-services'
end
def code_suggestions_pat_docs_url
diff --git a/app/models/namespaces/traversal/linear_scopes.rb b/app/models/namespaces/traversal/linear_scopes.rb
index 52eb0d47593..6e79e3ac9a1 100644
--- a/app/models/namespaces/traversal/linear_scopes.rb
+++ b/app/models/namespaces/traversal/linear_scopes.rb
@@ -37,13 +37,13 @@ module Namespaces
end
def self_and_descendants(include_self: true)
- return super unless use_traversal_ids_for_descendants_scopes?
+ return super unless use_traversal_ids?
self_and_descendants_with_comparison_operators(include_self: include_self)
end
def self_and_descendant_ids(include_self: true)
- return super unless use_traversal_ids_for_descendants_scopes?
+ return super unless use_traversal_ids?
self_and_descendants(include_self: include_self).as_ids
end
@@ -78,11 +78,6 @@ module Namespaces
Feature.enabled?(:use_traversal_ids)
end
- def use_traversal_ids_for_descendants_scopes?
- Feature.enabled?(:use_traversal_ids_for_descendants_scopes) &&
- use_traversal_ids?
- end
-
def use_traversal_ids_for_self_and_hierarchy_scopes?
Feature.enabled?(:use_traversal_ids_for_self_and_hierarchy_scopes) &&
use_traversal_ids?
diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb
index e039386c4f8..0d02a3b99aa 100644
--- a/app/models/users/callout.rb
+++ b/app/models/users/callout.rb
@@ -72,7 +72,8 @@ module Users
project_repository_limit_alert_error_threshold: 70, # EE-only
new_navigation_callout: 71,
code_suggestions_third_party_callout: 72, # EE-only
- namespace_over_storage_users_combined_alert: 73 # EE-only
+ namespace_over_storage_users_combined_alert: 73, # EE-only
+ rich_text_editor: 74
}
validates :feature_name,
diff --git a/app/views/admin/application_settings/_ai_access.html.haml b/app/views/admin/application_settings/_ai_access.html.haml
index 41b0a08128e..97f46adef51 100644
--- a/app/views/admin/application_settings/_ai_access.html.haml
+++ b/app/views/admin/application_settings/_ai_access.html.haml
@@ -12,8 +12,7 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
- = s_('CodeSuggestionsSM|Enable Code Suggestion for users of this GitLab instance.')
- = link_to sprite_icon('question-o'), code_suggestions_docs_url, target: '_blank', class: 'has-tooltip', title: _('More information')
+ = code_suggestions_description
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-ai-access-settings'), html: { class: 'fieldset-form', id: 'ai-access-settings' } do |f|
@@ -22,7 +21,7 @@
%fieldset
.form-group
= f.gitlab_ui_checkbox_component :instance_level_code_suggestions_enabled,
- s_('CodeSuggestionsSM|Turn on Code Suggestions for this instance. By turning on this feature, you:'),
+ s_('CodeSuggestionsSM|Enable Code Suggestions for this instance %{beta}').html_safe % { beta: gl_badge_tag(_('Beta'), variant: :neutral, size: :sm) },
help_text: code_suggestions_agreement
= f.label :ai_access_token, token_label, class: 'label-bold'
= f.password_field :ai_access_token, value: token_value, autocomplete: 'on', class: 'form-control gl-form-input', aria: { describedby: 'code_suggestions_token_explanation' }
diff --git a/app/views/discussions/_notes.html.haml b/app/views/discussions/_notes.html.haml
index a35ba12dd52..e34a5cebe78 100644
--- a/app/views/discussions/_notes.html.haml
+++ b/app/views/discussions/_notes.html.haml
@@ -15,16 +15,14 @@
= badge_counter
= render partial: "shared/notes/note", collection: discussion.notes, as: :note, locals: { badge_counter: badge_counter, show_image_comment_badge: show_image_comment_badge }
- .flash-container
-
- .discussion-reply-holder
- - if can_create_note?
- .discussion-with-resolve-btn
- = link_to_reply_discussion(discussion)
- - elsif !current_user
- .disabled-comment.text-center
- Please
- = link_to "register", new_session_path(:user, redirect_to_referer: 'yes')
- or
- = link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes')
- to reply
+ %li.discussion-reply-holder.clearfix{ class: 'gl-border-t-0! gl-pb-5!' }
+ - if can_create_note?
+ .discussion-with-resolve-btn
+ = link_to_reply_discussion(discussion)
+ - elsif !current_user
+ .disabled-comment.text-center
+ Please
+ = link_to "register", new_session_path(:user, redirect_to_referer: 'yes')
+ or
+ = link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes')
+ to reply
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index 6f4b35266c2..9a5e9b2179f 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -5,7 +5,7 @@
- else
- preview_url = preview_markdown_path(@project)
-= form_for form_resources, url: new_form_url, remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new-note js-new-note-form js-quick-submit common-note-form discussion-reply-holder", "data-noteable-iid" => @note.noteable.try(:iid), }, authenticity_token: true do |f|
+= form_for form_resources, url: new_form_url, remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new-note js-new-note-form js-quick-submit common-note-form discussion-reply-holder gl-border-top-0!", "data-noteable-iid" => @note.noteable.try(:iid), }, authenticity_token: true do |f|
= hidden_field_tag :view, diff_view
= hidden_field_tag :line_type
= hidden_field_tag :merge_request_diff_head_sha, @note.noteable.try(:diff_head_sha)
diff --git a/config/feature_flags/development/content_editor_on_issues.yml b/config/feature_flags/development/content_editor_on_issues.yml
index 4527ea3b807..7e8a6870952 100644
--- a/config/feature_flags/development/content_editor_on_issues.yml
+++ b/config/feature_flags/development/content_editor_on_issues.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98703
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375172
milestone: '15.5'
type: development
-group: group::editor
+group: group::knowledge
default_enabled: false
diff --git a/config/feature_flags/development/dynamically_compute_deployment_approval.yml b/config/feature_flags/development/dynamically_compute_deployment_approval.yml
deleted file mode 100644
index 6a7e8972613..00000000000
--- a/config/feature_flags/development/dynamically_compute_deployment_approval.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: dynamically_compute_deployment_approval
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120699
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/411370
-milestone: '16.2'
-type: development
-group: group::environments
-default_enabled: false
diff --git a/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml b/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml
deleted file mode 100644
index 74b6d6d2f70..00000000000
--- a/config/feature_flags/development/use_traversal_ids_for_descendants_scopes.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: use_traversal_ids_for_descendants_scopes
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78542
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350637
-milestone: '14.8'
-type: development
-group: group::tenant scale
-default_enabled: true
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 8029aba9d06..eb463c47dfe 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -3582,6 +3582,7 @@ Input type: `ExternalAuditEventDestinationCreateInput`
| <a id="mutationexternalauditeventdestinationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationexternalauditeventdestinationcreatedestinationurl"></a>`destinationUrl` | [`String!`](#string) | Destination URL. |
| <a id="mutationexternalauditeventdestinationcreategrouppath"></a>`groupPath` | [`ID!`](#id) | Group path. |
+| <a id="mutationexternalauditeventdestinationcreatename"></a>`name` | [`String`](#string) | Destination name. |
| <a id="mutationexternalauditeventdestinationcreateverificationtoken"></a>`verificationToken` | [`String`](#string) | Verification token. |
#### Fields
@@ -3621,6 +3622,7 @@ Input type: `ExternalAuditEventDestinationUpdateInput`
| <a id="mutationexternalauditeventdestinationupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationexternalauditeventdestinationupdatedestinationurl"></a>`destinationUrl` | [`String`](#string) | Destination URL to change. |
| <a id="mutationexternalauditeventdestinationupdateid"></a>`id` | [`AuditEventsExternalAuditEventDestinationID!`](#auditeventsexternalauditeventdestinationid) | ID of external audit event destination to update. |
+| <a id="mutationexternalauditeventdestinationupdatename"></a>`name` | [`String`](#string) | Destination name. |
#### Fields
@@ -3877,6 +3879,7 @@ Input type: `InstanceExternalAuditEventDestinationCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationinstanceexternalauditeventdestinationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationinstanceexternalauditeventdestinationcreatedestinationurl"></a>`destinationUrl` | [`String!`](#string) | Destination URL. |
+| <a id="mutationinstanceexternalauditeventdestinationcreatename"></a>`name` | [`String`](#string) | Destination name. |
#### Fields
@@ -3915,6 +3918,7 @@ Input type: `InstanceExternalAuditEventDestinationUpdateInput`
| <a id="mutationinstanceexternalauditeventdestinationupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationinstanceexternalauditeventdestinationupdatedestinationurl"></a>`destinationUrl` | [`String`](#string) | Destination URL to change. |
| <a id="mutationinstanceexternalauditeventdestinationupdateid"></a>`id` | [`AuditEventsInstanceExternalAuditEventDestinationID!`](#auditeventsinstanceexternalauditeventdestinationid) | ID of the external instance audit event destination to update. |
+| <a id="mutationinstanceexternalauditeventdestinationupdatename"></a>`name` | [`String`](#string) | Destination name. |
#### Fields
@@ -15405,6 +15409,7 @@ Represents an external resource to send audit events to.
| <a id="externalauditeventdestinationgroup"></a>`group` | [`Group!`](#group) | Group the destination belongs to. |
| <a id="externalauditeventdestinationheaders"></a>`headers` | [`AuditEventStreamingHeaderConnection!`](#auditeventstreamingheaderconnection) | List of additional HTTP headers sent with each event. (see [Connections](#connections)) |
| <a id="externalauditeventdestinationid"></a>`id` | [`ID!`](#id) | ID of the destination. |
+| <a id="externalauditeventdestinationname"></a>`name` | [`String!`](#string) | Name of the external destination to send audit events to. |
| <a id="externalauditeventdestinationverificationtoken"></a>`verificationToken` | [`String!`](#string) | Verification token to validate source of event. |
### `ExternalIssue`
@@ -17107,6 +17112,7 @@ Represents an external resource to send instance audit events to.
| <a id="instanceexternalauditeventdestinationeventtypefilters"></a>`eventTypeFilters` | [`[String!]!`](#string) | List of event type filters added for streaming. |
| <a id="instanceexternalauditeventdestinationheaders"></a>`headers` | [`AuditEventsStreamingInstanceHeaderConnection!`](#auditeventsstreaminginstanceheaderconnection) | List of additional HTTP headers sent with each event. (see [Connections](#connections)) |
| <a id="instanceexternalauditeventdestinationid"></a>`id` | [`ID!`](#id) | ID of the destination. |
+| <a id="instanceexternalauditeventdestinationname"></a>`name` | [`String!`](#string) | Name of the external destination to send audit events to. |
| <a id="instanceexternalauditeventdestinationverificationtoken"></a>`verificationToken` | [`String!`](#string) | Verification token to validate source of event. |
### `InstanceSecurityDashboard`
@@ -26866,6 +26872,7 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumproject_repository_limit_alert_error_threshold"></a>`PROJECT_REPOSITORY_LIMIT_ALERT_ERROR_THRESHOLD` | Callout feature name for project_repository_limit_alert_error_threshold. |
| <a id="usercalloutfeaturenameenumproject_repository_limit_alert_warning_threshold"></a>`PROJECT_REPOSITORY_LIMIT_ALERT_WARNING_THRESHOLD` | Callout feature name for project_repository_limit_alert_warning_threshold. |
| <a id="usercalloutfeaturenameenumregistration_enabled_callout"></a>`REGISTRATION_ENABLED_CALLOUT` | Callout feature name for registration_enabled_callout. |
+| <a id="usercalloutfeaturenameenumrich_text_editor"></a>`RICH_TEXT_EDITOR` | Callout feature name for rich_text_editor. |
| <a id="usercalloutfeaturenameenumsecurity_configuration_devops_alert"></a>`SECURITY_CONFIGURATION_DEVOPS_ALERT` | Callout feature name for security_configuration_devops_alert. |
| <a id="usercalloutfeaturenameenumsecurity_configuration_upgrade_banner"></a>`SECURITY_CONFIGURATION_UPGRADE_BANNER` | Callout feature name for security_configuration_upgrade_banner. |
| <a id="usercalloutfeaturenameenumsecurity_newsletter_callout"></a>`SECURITY_NEWSLETTER_CALLOUT` | Callout feature name for security_newsletter_callout. |
@@ -28190,6 +28197,7 @@ Implementations:
| <a id="externalauditeventdestinationinterfacedestinationurl"></a>`destinationUrl` | [`String!`](#string) | External destination to send audit events to. |
| <a id="externalauditeventdestinationinterfaceeventtypefilters"></a>`eventTypeFilters` | [`[String!]!`](#string) | List of event type filters added for streaming. |
| <a id="externalauditeventdestinationinterfaceid"></a>`id` | [`ID!`](#id) | ID of the destination. |
+| <a id="externalauditeventdestinationinterfacename"></a>`name` | [`String!`](#string) | Name of the external destination to send audit events to. |
| <a id="externalauditeventdestinationinterfaceverificationtoken"></a>`verificationToken` | [`String!`](#string) | Verification token to validate source of event. |
#### `MemberInterface`
diff --git a/doc/development/testing_guide/end_to_end/best_practices.md b/doc/development/testing_guide/end_to_end/best_practices.md
index c1f06bb9a66..96bd02d235b 100644
--- a/doc/development/testing_guide/end_to_end/best_practices.md
+++ b/doc/development/testing_guide/end_to_end/best_practices.md
@@ -99,6 +99,43 @@ end
We recommend creating four associated test cases, two for each shared example.
+## Test naming
+
+Test names should form a readable sentence defining the purpose of the test. Our [testing guide](index.md) extends the [Thoughtbot testing style guide](https://github.com/thoughtbot/guides/tree/master/testing-rspec). This page clarifies the guidelines, along with input from [https://www.betterspecs.org/](https://www.betterspecs.org/) and [the RSpec naming guide](https://rspec.rubystyle.guide/#naming.)
+
+### Recommended approach
+
+The following block generates a test named `Plan wiki content creation in a project adds a home page`
+
+``` ruby
+# `RSpec.describe` is the DevOps Stage being covered
+RSpec.describe 'Plan', product_group: :knowledge do
+ # `describe` is the feature being tested
+ describe 'wiki content creation' do
+ # `context` provides the condition being covered
+ context 'in a project'
+ # `it` defines the expected result of the test
+ it 'adds a home page'
+ ...
+ end
+ ...
+ end
+ ...
+ end
+end
+```
+
+1. Every `describe`, `context`, and `it` blocks should have a short description attached
+1. Keep descriptions as concise as possible.
+ 1. Long descriptions or multiple conditionals could be a sign it should be split up (additional `context` blocks).
+ 1. The [Documentation Style Guide](../../documentation/styleguide/index.md) gives recommendations on how to write concisely and with [active voice](../../documentation/styleguide/word_list.md#active-voice).
+1. The outermost `Rspec.describe` block should be [the DevOps stage name](https://about.gitlab.com/handbook/product/categories/#devops-stages)
+1. Inside the `Rspec.describe` block is a `describe` block with the name of the feature being tested
+1. Optional `context` blocks define what the conditions being tested are
+ 1. `context` blocks descriptions should begin with `when`, `with`, `without`, `for`, `and`, `on`, `in`, `as`, or `if` to match the [rubocop rule](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextWording)
+1. The `it` block describes the pass/fail criteria for the test
+ 1. In `shared_examples` with a single example a `specify` block can be used instead of a named `it` block
+
## Prefer API over UI
The end-to-end testing framework has the ability to fabricate its resources on a case-by-case basis.
diff --git a/doc/user/admin_area/reporting/git_abuse_rate_limit.md b/doc/user/admin_area/reporting/git_abuse_rate_limit.md
index 75e42c09474..0e607b03b82 100644
--- a/doc/user/admin_area/reporting/git_abuse_rate_limit.md
+++ b/doc/user/admin_area/reporting/git_abuse_rate_limit.md
@@ -1,9 +1,9 @@
---
-redirect_to: '../../../administration//reporting/git_abuse_rate_limit.md'
+redirect_to: '../../../administration/reporting/git_abuse_rate_limit.md'
remove_date: '2023-10-12'
---
-This document was moved to [another location](../../../administration//reporting/git_abuse_rate_limit.md).
+This document was moved to [another location](../../../administration/reporting/git_abuse_rate_limit.md).
<!-- This redirect file can be deleted after <2023-10-12>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
diff --git a/doc/user/project/repository/code_suggestions.md b/doc/user/project/repository/code_suggestions.md
index 6078ac2b1be..e5c17d9107b 100644
--- a/doc/user/project/repository/code_suggestions.md
+++ b/doc/user/project/repository/code_suggestions.md
@@ -25,7 +25,7 @@ Write code more efficiently by using generative AI to suggest code while you're
Code Suggestions are available:
-- To users of GitLab SaaS (by default) and self-managed GitLab (when requested).
+- To users of GitLab SaaS (by default) and self-managed GitLab Enterprise Edition (when requested). Code Suggestions are not available for GitLab Community Edition.
- In VS Code and Microsoft Visual Studio when you have the corresponding GitLab extension installed.
- In the GitLab WebIDE
@@ -92,11 +92,11 @@ This setting controls Code Suggestions for all IDEs. Support for [more granular
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10653) in GitLab 16.1 as [Beta](../../../policy/experiment-beta-support.md#beta).
-To enable Code Suggestions on a self-managed GitLab instance, you must:
+To enable Code Suggestions on a self-managed GitLab EE instance, you must:
- Be an administrator.
- Have a [GitLab SaaS account](https://gitlab.com/users/sign_up).
- You do not need to have a GitLab SaaS subscription.
+You do not need to have a GitLab SaaS subscription.
Then, you will:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 035a173e9e0..4be697cd8af 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -72,6 +72,9 @@ msgstr ""
msgid "\"%{repository_name}\" size (%{repository_size}) is larger than the limit of %{limit}."
msgstr ""
+msgid "### Rich text editor"
+msgstr ""
+
msgid "##### ERROR ##### You have used %{usage_percentage} of the storage quota for %{namespace_name} (%{current_size} of %{size_limit}). %{namespace_name} is now read-only. Projects under this namespace are locked and actions will be restricted. To manage storage, or purchase additional storage, see %{manage_storage_url}. To learn more about restricted actions, see %{restricted_actions_url}"
msgstr ""
@@ -11152,25 +11155,25 @@ msgstr ""
msgid "CodeSuggestionsAlert|Get started with Code Suggestions, available for free during the beta period."
msgstr ""
-msgid "CodeSuggestionsSM|&#8226; Agree to the %{terms_link_start}GitLab Testing Agreement%{link_end}.%{br} &#8226; Acknowledge that GitLab will send data from the instance, including personal data, to Google for cloud hosting.%{br} &nbsp;&nbsp;&nbsp;We may also send data to %{ai_docs_link_start}third-party AI providers%{link_end} to provide this feature."
+msgid "CodeSuggestionsSM|By enabling this feature, you agree to the %{terms_link_start}GitLab Testing Agreement%{link_end} and acknowledge that GitLab will send data from the instance, including personal data, to our %{ai_docs_link_start}AI providers%{link_end} to provide this feature."
msgstr ""
msgid "CodeSuggestionsSM|Code Suggestions"
msgstr ""
-msgid "CodeSuggestionsSM|Enable Code Suggestion for users of this GitLab instance."
+msgid "CodeSuggestionsSM|Enable Code Suggestions for this instance %{beta}"
msgstr ""
-msgid "CodeSuggestionsSM|Enter new personal access token"
+msgid "CodeSuggestionsSM|Enable Code Suggestions for users of this instance. %{link_start}What are Code Suggestions?%{link_end}"
msgstr ""
-msgid "CodeSuggestionsSM|Personal access token"
+msgid "CodeSuggestionsSM|Enter new personal access token"
msgstr ""
-msgid "CodeSuggestionsSM|Turn on Code Suggestions for this instance. By turning on this feature, you:"
+msgid "CodeSuggestionsSM|On GitLab.com, create a token. This token is required to use Code Suggestions on your self-managed instance. %{link_start}How do I create a token?%{link_end}"
msgstr ""
-msgid "CodeSuggestionsSM|Your personal access token from GitLab.com. See the %{link_start}documentation%{link_end} for information on creating a personal access token."
+msgid "CodeSuggestionsSM|Personal access token"
msgstr ""
msgid "CodeSuggestionsThirdPartyAlert|%{code_suggestions_link_start}Code Suggestions%{link_end} now uses third-party AI services to provide higher quality suggestions. You can %{third_party_link_start}disable third-party services%{link_end} for your group, or disable Code Suggestions entirely in %{profile_settings_link_start}your user profile%{link_end}."
@@ -48565,9 +48568,15 @@ msgstr ""
msgid "Try grouping with different labels"
msgstr ""
+msgid "Try out **styling** _your_ content right here or read the [direction](%{directionUrl})."
+msgstr ""
+
msgid "Try out GitLab Pipelines"
msgstr ""
+msgid "Try the rich text editor now"
+msgstr ""
+
msgid "Try the troubleshooting steps here."
msgstr ""
@@ -49237,6 +49246,9 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "UsageQuotas|(of %{totalStorageSize})"
+msgstr ""
+
msgid "UsageQuotas|An error occurred loading the transfer data. Please refresh the page to try again."
msgstr ""
@@ -49633,6 +49645,9 @@ msgstr ""
msgid "Use the link below to confirm your email address."
msgstr ""
+msgid "Use the new rich text editor to see your text and tables fully formatted as you type. No need to remember any formatting syntax, or switch between preview and editing modes!"
+msgstr ""
+
msgid "Use the public cloud instance URL (%{kroki_public_url}) or %{install_link_start}install Kroki%{install_link_end} on your own infrastructure and use your own instance URL."
msgstr ""
@@ -52356,6 +52371,9 @@ msgstr ""
msgid "Write your release notes or drag your files here…"
msgstr ""
+msgid "Writing just got easier"
+msgstr ""
+
msgid "Wrong extern UID provided. Make sure Auth0 is configured correctly."
msgstr ""
diff --git a/package.json b/package.json
index 860252ccce3..faa53c1d7f3 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/svgs": "3.55.0",
- "@gitlab/ui": "64.19.0",
+ "@gitlab/ui": "64.20.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230620122149",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 900dc62f6b7..6e303c3d535 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -78,6 +78,7 @@ module QA
# Attachment option should be an absolute path
def comment(text, attachment: nil, filter: :all_activities)
+ close_rich_text_promo_popover_if_present
method("select_#{filter}_filter").call
fill_element :comment_field, "#{text}\n"
diff --git a/qa/qa/page/component/rich_text_popover.rb b/qa/qa/page/component/rich_text_popover.rb
new file mode 100644
index 00000000000..6a187dcedc5
--- /dev/null
+++ b/qa/qa/page/component/rich_text_popover.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module RichTextPopover
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.view 'app/assets/javascripts/vue_shared/components/markdown/editor_mode_switcher.vue' do
+ element :rich_text_promo_popover
+ end
+
+ base.view 'app/views/shared/_broadcast_message.html.haml' do
+ element :close_button
+ end
+ end
+
+ def close_rich_text_promo_popover_if_present
+ return unless has_element?(:rich_text_promo_popover, wait: 0)
+
+ within_element(:rich_text_promo_popover) do
+ click_element(:close_button)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/issuable/new.rb b/qa/qa/page/issuable/new.rb
index 63f3beb02e9..0a8be750752 100644
--- a/qa/qa/page/issuable/new.rb
+++ b/qa/qa/page/issuable/new.rb
@@ -4,6 +4,8 @@ module QA
module Page
module Issuable
class New < Page::Base
+ include Page::Component::RichTextPopover
+
view 'app/views/shared/issuable/form/_title.html.haml' do
element :issuable_form_title_field
end
@@ -48,6 +50,7 @@ module QA
end
def choose_template(template_name)
+ close_rich_text_promo_popover_if_present
click_element :template_dropdown
within_element(:template_dropdown) do
click_on template_name
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index d43477b2bda..4fc4126be32 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -6,6 +6,7 @@ module QA
class Show < Page::Base
include Page::Component::Note
include Page::Component::Issuable::Sidebar
+ include Page::Component::RichTextPopover
view 'app/assets/javascripts/batch_comments/components/preview_dropdown.vue' do
element :review_preview_dropdown
@@ -285,6 +286,7 @@ module QA
end
def merge!
+ close_rich_text_promo_popover_if_present
try_to_merge!
finished_loading?
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index c2f334c930a..c95375dbbb9 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -9,6 +9,7 @@ module QA
include Page::Component::DesignManagement
include Page::Component::Issuable::Sidebar
include Page::Component::Issuable::Common
+ include Page::Component::RichTextPopover
# We need to check phone_layout? instead of mobile_layout? here
# since tablets have the regular top navigation bar
prepend Mobile::Page::Project::Issue::Show if Runtime::Env.phone_layout?
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index 90be3f0760d..b270a4c7600 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Thread Comments Issue', :js, feature_category: :source_code_management do
+ include ContentEditorHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
@@ -12,6 +14,7 @@ RSpec.describe 'Thread Comments Issue', :js, feature_category: :source_code_mana
sign_in(user)
visit project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
end
it_behaves_like 'thread comments for issue, epic and merge request', 'issue'
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index bb7cc3db452..d870471d646 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Group milestones', feature_category: :groups_and_projects do
+ include ContentEditorHelpers
+
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project_empty_repo, group: group) }
let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group).user }
@@ -18,6 +20,7 @@ RSpec.describe 'Group milestones', feature_category: :groups_and_projects do
context 'create a milestone', :js do
before do
visit new_group_milestone_path(group)
+ close_rich_text_promo_popover_if_present
end
it 'renders description preview' do
@@ -66,6 +69,7 @@ RSpec.describe 'Group milestones', feature_category: :groups_and_projects do
context 'when no milestones' do
it 'renders no milestones text' do
visit group_milestones_path(group)
+ close_rich_text_promo_popover_if_present
expect(page).to have_content('Use milestones to track issues and merge requests')
end
end
@@ -95,6 +99,7 @@ RSpec.describe 'Group milestones', feature_category: :groups_and_projects do
before do
visit group_milestones_path(group)
+ close_rich_text_promo_popover_if_present
end
it 'counts milestones correctly' do
@@ -170,6 +175,7 @@ RSpec.describe 'Group milestones', feature_category: :groups_and_projects do
before do
visit group_milestone_path(group, milestone)
+ close_rich_text_promo_popover_if_present
end
it 'renders the issues tab' do
diff --git a/spec/features/issuables/markdown_references/jira_spec.rb b/spec/features/issuables/markdown_references/jira_spec.rb
index 887bc7d0c87..e072231c6e9 100644
--- a/spec/features/issuables/markdown_references/jira_spec.rb
+++ b/spec/features/issuables/markdown_references/jira_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe "Jira", :js, feature_category: :team_planning do
+ include ContentEditorHelpers
+
let(:user) { create(:user) }
let(:actual_project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, target_project: actual_project, source_project: actual_project) }
@@ -24,6 +26,7 @@ RSpec.describe "Jira", :js, feature_category: :team_planning do
sign_in(user)
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
build_note
end
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 3dc291718ea..5f7a4f26a98 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
include ActionView::Helpers::JavaScriptHelper
include ListboxHelpers
+ include ContentEditorHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -36,6 +37,7 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
describe 'new issue' do
before do
visit new_project_issue_path(project)
+ close_rich_text_promo_popover_if_present
end
describe 'shorten users API pagination limit' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index a1f2275df6e..47e9575da54 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
include CookieHelper
include Features::AutocompleteHelpers
+ include ContentEditorHelpers
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') }
@@ -32,6 +33,7 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
before do
sign_in(user)
visit new_project_issue_path(project)
+ close_rich_text_promo_popover_if_present
wait_for_requests
end
@@ -50,6 +52,7 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
sign_in(user)
set_cookie('new-actions-popover-viewed', 'true')
visit project_issue_path(project, issue_to_edit)
+ close_rich_text_promo_popover_if_present
wait_for_requests
end
@@ -85,6 +88,7 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
before do
sign_in(user)
visit project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
wait_for_requests
end
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index 5cabaf16960..03c95c275b5 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Issue markdown toolbar', :js, feature_category: :team_planning do
+ include ContentEditorHelpers
+
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
@@ -11,6 +13,7 @@ RSpec.describe 'Issue markdown toolbar', :js, feature_category: :team_planning d
sign_in(user)
visit project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
end
it "doesn't include first new line when adding bold" do
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 0fa4aba9a11..23f9347d726 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Issue notes polling', :js, feature_category: :team_planning do
include NoteInteractionHelpers
+ include ContentEditorHelpers
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
@@ -11,6 +12,7 @@ RSpec.describe 'Issue notes polling', :js, feature_category: :team_planning do
describe 'creates' do
before do
visit project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
end
it 'displays the new comment' do
@@ -31,6 +33,7 @@ RSpec.describe 'Issue notes polling', :js, feature_category: :team_planning do
before do
sign_in(user)
visit project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
end
it 'displays the updated content' do
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index 8d6262efa53..62855c7467f 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -3,9 +3,12 @@
require 'spec_helper'
RSpec.describe 'Create notes on issues', :js, feature_category: :team_planning do
+ include ContentEditorHelpers
+
let(:user) { create(:user) }
def submit_comment(text)
+ close_rich_text_promo_popover_if_present
fill_in 'note[note]', with: text
click_button 'Comment'
wait_for_requests
diff --git a/spec/features/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb
index d954a1d15ff..f18992325d8 100644
--- a/spec/features/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/issues/user_comments_on_issue_spec.rb
@@ -5,6 +5,7 @@ require "spec_helper"
RSpec.describe "User comments on issue", :js, feature_category: :team_planning do
include Features::AutocompleteHelpers
include Features::NotesHelpers
+ include ContentEditorHelpers
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
@@ -15,6 +16,7 @@ RSpec.describe "User comments on issue", :js, feature_category: :team_planning d
sign_in(user)
visit(project_issue_path(project, issue))
+ close_rich_text_promo_popover_if_present
end
context "when adding comments" do
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index 57183b2e8d9..76b07d903bc 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -4,6 +4,7 @@ require "spec_helper"
RSpec.describe "User creates issue", feature_category: :team_planning do
include DropzoneHelper
+ include ContentEditorHelpers
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
@@ -41,6 +42,7 @@ RSpec.describe "User creates issue", feature_category: :team_planning do
sign_in(user)
visit(new_project_issue_path(project))
+ close_rich_text_promo_popover_if_present
end
context 'available metadata' do
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 47c532c3963..0938f9c7d12 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -4,6 +4,7 @@ require "spec_helper"
RSpec.describe "Issues > User edits issue", :js, feature_category: :team_planning do
include CookieHelper
+ include ContentEditorHelpers
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:project_with_milestones) { create(:project_empty_repo, :public) }
@@ -27,6 +28,7 @@ RSpec.describe "Issues > User edits issue", :js, feature_category: :team_plannin
before do
stub_licensed_features(multiple_issue_assignees: false)
visit edit_project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
end
it_behaves_like 'edits content using the content editor'
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index 539e429534e..e1099ba242e 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'User interacts with awards', feature_category: :team_planning do
include MobileHelpers
+ include ContentEditorHelpers
let(:user) { create(:user) }
@@ -16,6 +17,7 @@ RSpec.describe 'User interacts with awards', feature_category: :team_planning do
sign_in(user)
visit(project_issue_path(project, issue))
+ close_rich_text_promo_popover_if_present
end
it 'toggles the thumbsup award emoji', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27959' do
diff --git a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
index 91b18454af5..ef448c06a3f 100644
--- a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
+++ b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Issues > Real-time sidebar', :js, :with_license, feature_category: :team_planning do
+ include ContentEditorHelpers
+
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
@@ -20,6 +22,7 @@ RSpec.describe 'Issues > Real-time sidebar', :js, :with_license, feature_categor
using_session :other_session do
visit project_issue_path(project, issue)
+ close_rich_text_promo_popover_if_present
expect(page.find('.assignee')).to have_content 'None'
end
@@ -43,6 +46,7 @@ RSpec.describe 'Issues > Real-time sidebar', :js, :with_license, feature_categor
using_session :other_session do
visit project_issue_path(project, issue)
wait_for_requests
+ close_rich_text_promo_popover_if_present
expect(labels_value).to have_content('None')
end
@@ -50,6 +54,7 @@ RSpec.describe 'Issues > Real-time sidebar', :js, :with_license, feature_categor
visit project_issue_path(project, issue)
wait_for_requests
+ close_rich_text_promo_popover_if_present
expect(labels_value).to have_content('None')
page.within(labels_widget) do
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index e85a521e242..dc149ccc698 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -9,6 +9,7 @@ require 'spec_helper'
# for each existing quick action unless they test something not tested by existing tests.
RSpec.describe 'Issues > User uses quick actions', :js, feature_category: :team_planning do
include Features::NotesHelpers
+ include ContentEditorHelpers
context "issuable common quick actions" do
let(:new_url_opts) { {} }
@@ -34,6 +35,7 @@ RSpec.describe 'Issues > User uses quick actions', :js, feature_category: :team_
sign_in(user)
visit project_issue_path(project, issue)
wait_for_all_requests
+ close_rich_text_promo_popover_if_present
end
after do
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index e8f40a1ceab..eb79d6e64f3 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do
include FilteredSearchHelpers
+ include ContentEditorHelpers
let!(:user) { create(:user) }
let!(:grandparent) { create(:group) }
@@ -165,6 +166,7 @@ RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do
context 'when creating new issuable' do
before do
visit new_project_issue_path(project_1)
+ close_rich_text_promo_popover_if_present
end
it 'is able to assign ancestor group labels' do
@@ -202,6 +204,7 @@ RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do
context 'when creating new issuable' do
before do
visit new_project_issue_path(project_1)
+ close_rich_text_promo_popover_if_present
end
it 'is able to assign ancestor group labels' do
@@ -233,6 +236,7 @@ RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do
project_1.add_developer(user)
visit project_issue_path(project_1, issue)
+ close_rich_text_promo_popover_if_present
end
it_behaves_like 'assigning labels from sidebar'
diff --git a/spec/features/merge_request/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index e3989a8a192..38291573256 100644
--- a/spec/features/merge_request/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inline, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
@@ -15,6 +17,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
context 'presents merged merge request content' do
it 'when merge method is set to merge commit' do
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
click_merge_button
@@ -30,6 +33,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
merge_request = create(:merge_request, :rebased, source_project: project)
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
click_merge_button
@@ -40,6 +44,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
merge_request = create(:merge_request, :rebased, source_project: project, squash: true)
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
click_merge_button
@@ -51,6 +56,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
context 'with removing the source branch' do
before do
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
end
it 'accepts a merge request' do
@@ -69,6 +75,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
context 'without removing the source branch' do
before do
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
end
it 'accepts a merge request' do
@@ -86,6 +93,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
context 'when a URL has an anchor' do
before do
visit(merge_request_path(merge_request, anchor: 'note_123'))
+ close_rich_text_promo_popover_if_present
end
it 'accepts a merge request' do
@@ -106,6 +114,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
merge_request.mark_as_mergeable
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
end
it 'accepts a merge request' do
diff --git a/spec/features/merge_request/user_edits_merge_request_spec.rb b/spec/features/merge_request/user_edits_merge_request_spec.rb
index 584a17ae33d..b1cff72c374 100644
--- a/spec/features/merge_request/user_edits_merge_request_spec.rb
+++ b/spec/features/merge_request/user_edits_merge_request_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User edits a merge request', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
@@ -85,6 +87,8 @@ RSpec.describe 'User edits a merge request', :js, feature_category: :code_review
describe 'changing target branch' do
it 'allows user to change target branch' do
+ close_rich_text_promo_popover_if_present
+
expect(page).to have_content('From master into feature')
first('.js-target-branch').click
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
index 76588832ee1..ab7183775b9 100644
--- a/spec/features/merge_request/user_edits_mr_spec.rb
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -184,7 +184,11 @@ RSpec.describe 'Merge request > User edits MR', feature_category: :code_review_w
it 'allows to unselect "Remove source branch"', :js do
expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
- visit edit_project_merge_request_path(target_project, merge_request)
+ begin
+ visit edit_project_merge_request_path(target_project, merge_request)
+ rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError
+ end
+
uncheck 'Delete source branch when merge request is accepted'
click_button 'Save changes'
diff --git a/spec/features/merge_request/user_merges_immediately_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb
index 71af2045bab..5fe9947d0df 100644
--- a/spec/features/merge_request/user_merges_immediately_spec.rb
+++ b/spec/features/merge_request/user_merges_immediately_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Merge requests > User merges immediately', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let!(:merge_request) do
@@ -31,6 +33,7 @@ RSpec.describe 'Merge requests > User merges immediately', :js, feature_category
project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
it 'enables merge immediately' do
diff --git a/spec/features/merge_request/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb
index 6ffb33603d5..402405e1fb6 100644
--- a/spec/features/merge_request/user_merges_merge_request_spec.rb
+++ b/spec/features/merge_request/user_merges_merge_request_spec.rb
@@ -3,6 +3,8 @@
require "spec_helper"
RSpec.describe "User merges a merge request", :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:user) { project.first_owner }
before do
@@ -29,6 +31,7 @@ RSpec.describe "User merges a merge request", :js, feature_category: :code_revie
create(:merge_request, source_project: project, source_branch: 'branch-1')
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
expect(page).to have_css('.js-merge-counter', text: '2')
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index 78814e36cfe..62404077cea 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
@@ -114,6 +116,7 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js, fea
it 'allows MR to be merged immediately' do
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
wait_for_requests
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index 146e4d1265a..0278d2af08f 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Merge request > User posts notes', :js, feature_category: :code_review_workflow do
include NoteInteractionHelpers
+ include ContentEditorHelpers
let_it_be(:project) { create(:project, :repository) }
@@ -21,6 +22,7 @@ RSpec.describe 'Merge request > User posts notes', :js, feature_category: :code_
sign_in(user)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
subject { page }
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 4bdef20304a..e8ffca43aa2 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Merge request > User resolves diff notes and threads', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:guest) { create(:user) }
@@ -541,5 +543,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js, feat
# Wait for MR widget to load
wait_for_requests
+
+ close_rich_text_promo_popover_if_present
end
end
diff --git a/spec/features/merge_request/user_reverts_merge_request_spec.rb b/spec/features/merge_request/user_reverts_merge_request_spec.rb
index 8c782056aa4..68adc4d47b6 100644
--- a/spec/features/merge_request/user_reverts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reverts_merge_request_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User reverts a merge request', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
@@ -13,6 +15,7 @@ RSpec.describe 'User reverts a merge request', :js, feature_category: :code_revi
sign_in(user)
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
page.within('.mr-state-widget') do
click_button 'Merge'
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 3fb3ef12fcc..57f378a86b6 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -77,8 +77,9 @@ RSpec.describe 'Merge request > User sees diff', :js, feature_category: :code_re
sign_in(author_user)
visit diffs_project_merge_request_path(project, merge_request)
- # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
- expect(page).to have_selector(".js-edit-blob", visible: false)
+ first(".js-diff-more-actions").click
+
+ expect(page).to have_selector(".js-edit-blob")
end
end
diff --git a/spec/features/merge_request/user_sees_discussions_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb
index 3ca5ac23ddb..3482d468bc1 100644
--- a/spec/features/merge_request/user_sees_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Merge request > User sees threads', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -29,6 +31,7 @@ RSpec.describe 'Merge request > User sees threads', :js, feature_category: :code
before do
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
context 'active threads' do
@@ -71,6 +74,7 @@ RSpec.describe 'Merge request > User sees threads', :js, feature_category: :code
before do
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
# TODO: https://gitlab.com/gitlab-org/gitlab-foss/issues/48034
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 33694c056a0..3cac24838a3 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category:
include ProjectForksHelper
include TestReportsHelper
include ReactiveCachingHelpers
+ include ContentEditorHelpers
let(:project) { create(:project, :repository) }
let(:project_only_mwps) { create(:project, :repository, only_allow_merge_if_pipeline_succeeds: true) }
@@ -57,6 +58,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category:
merge_request.update!(head_pipeline: pipeline)
deployment.update!(status: :success)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
it 'shows environments link' do
@@ -130,6 +132,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category:
create(:ci_build, :pending, pipeline: pipeline)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
it 'has merge button that shows modal when pipeline does not succeeded' do
@@ -403,6 +406,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category:
before do
allow_any_instance_of(Repository).to receive(:merge).and_return(false)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
it 'updates the MR widget', :sidekiq_might_not_need_inline do
@@ -424,6 +428,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category:
sign_in(user2)
merge_request.update!(source_project: forked_project)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
end
it 'user can merge into the target project', :sidekiq_inline do
@@ -461,6 +466,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category:
allow_any_instance_of(MergeRequest).to receive(:merge_ongoing?).and_return(true)
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
wait_for_requests
diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
index 92bedc47718..ad7ed1ceada 100644
--- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Merge request > User sees notes from forked project', :js, feature_category: :code_review_workflow do
include ProjectForksHelper
+ include ContentEditorHelpers
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
@@ -29,6 +30,7 @@ RSpec.describe 'Merge request > User sees notes from forked project', :js, featu
it 'user can reply to the comment', :sidekiq_might_not_need_inline do
visit project_merge_request_path(project, merge_request)
+ close_rich_text_promo_popover_if_present
expect(page).to have_content('A commit comment')
diff --git a/spec/features/merge_request/user_squashes_merge_request_spec.rb b/spec/features/merge_request/user_squashes_merge_request_spec.rb
index 5fd0f353e56..200f310d929 100644
--- a/spec/features/merge_request/user_squashes_merge_request_spec.rb
+++ b/spec/features/merge_request/user_squashes_merge_request_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User squashes a merge request', :js, feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:source_branch) { 'csv' }
@@ -12,6 +14,8 @@ RSpec.describe 'User squashes a merge request', :js, feature_category: :code_rev
shared_examples 'squash' do
it 'squashes the commits into a single commit, and adds a merge commit', :sidekiq_might_not_need_inline do
+ close_rich_text_promo_popover_if_present
+
expect(page).to have_content('Merged')
latest_master_commits = project.repository.commits_between(original_head.sha, 'master').map(&:raw)
@@ -37,12 +41,16 @@ RSpec.describe 'User squashes a merge request', :js, feature_category: :code_rev
shared_examples 'no squash' do
it 'accepts the merge request without squashing', :sidekiq_might_not_need_inline do
+ close_rich_text_promo_popover_if_present
+
expect(page).to have_content('Merged')
expect(project.repository).to be_merged_to_root_ref(source_branch)
end
end
def accept_mr
+ close_rich_text_promo_popover_if_present
+
expect(page).to have_button('Merge')
uncheck 'Delete source branch' unless protected_source_branch
diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
index 6152d9f8259..1a814aeb89d 100644
--- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
+++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'User comments on a diff', :js, feature_category: :code_review_workflow do
include MergeRequestDiffHelpers
include RepoHelpers
+ include ContentEditorHelpers
def expect_suggestion_has_content(element, expected_changing_content, expected_suggested_content)
changing_content = element.all(:css, '.line_holder.old').map { |el| el.text(normalize_ws: true) }
@@ -35,6 +36,7 @@ RSpec.describe 'User comments on a diff', :js, feature_category: :code_review_wo
context 'single suggestion note' do
it 'hides suggestion popover' do
click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
+ close_rich_text_promo_popover_if_present
expect(page).to have_selector('.diff-suggest-popover')
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
index 1a9d40ae926..cd0ea639d4d 100644
--- a/spec/features/merge_request/user_views_open_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User views an open merge request', feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:merge_request) do
create(:merge_request, source_project: project, target_project: project, description: '# Description header')
end
@@ -53,6 +55,7 @@ RSpec.describe 'User views an open merge request', feature_category: :code_revie
sign_in(user)
visit(edit_project_merge_request_path(project, merge_request))
+ close_rich_text_promo_popover_if_present
end
it 'renders empty description preview' do
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index bb43d019701..4221fa26e00 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'issuable templates', :js, feature_category: :groups_and_projects do
include ProjectForksHelper
include CookieHelper
+ include ContentEditorHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
@@ -36,6 +37,7 @@ RSpec.describe 'issuable templates', :js, feature_category: :groups_and_projects
message: 'added issue template',
branch_name: 'master')
visit project_issue_path project, issue
+ close_rich_text_promo_popover_if_present
page.find('.js-issuable-edit').click
fill_in :'issuable-title', with: 'test issue title'
end
@@ -79,6 +81,7 @@ RSpec.describe 'issuable templates', :js, feature_category: :groups_and_projects
message: 'added issue template',
branch_name: 'master')
visit project_issue_path project, issue
+ close_rich_text_promo_popover_if_present
page.find('.js-issuable-edit').click
fill_in :'issuable-title', with: 'test issue title'
fill_in :'issue-description', with: prior_description
@@ -108,6 +111,7 @@ RSpec.describe 'issuable templates', :js, feature_category: :groups_and_projects
it 'does not overwrite autosaved description' do
visit new_project_issue_path project
wait_for_requests
+ close_rich_text_promo_popover_if_present
assert_template # default template is loaded the first time
@@ -141,6 +145,7 @@ RSpec.describe 'issuable templates', :js, feature_category: :groups_and_projects
message: 'added merge request bug template',
branch_name: 'master')
visit edit_project_merge_request_path project, merge_request
+ close_rich_text_promo_popover_if_present
fill_in :'merge_request[title]', with: 'test merge request title'
end
@@ -200,6 +205,7 @@ RSpec.describe 'issuable templates', :js, feature_category: :groups_and_projects
message: 'added merge request template',
branch_name: 'master')
visit edit_project_merge_request_path project, merge_request
+ close_rich_text_promo_popover_if_present
fill_in :'merge_request[title]', with: 'test merge request title'
end
diff --git a/spec/features/user_sees_revert_modal_spec.rb b/spec/features/user_sees_revert_modal_spec.rb
index 2b0ff2cdf01..9ee3fe846a6 100644
--- a/spec/features/user_sees_revert_modal_spec.rb
+++ b/spec/features/user_sees_revert_modal_spec.rb
@@ -4,6 +4,8 @@ require 'spec_helper'
RSpec.describe 'Merge request > User sees revert modal', :js, :sidekiq_might_not_need_inline,
feature_category: :code_review_workflow do
+ include ContentEditorHelpers
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -22,6 +24,8 @@ RSpec.describe 'Merge request > User sees revert modal', :js, :sidekiq_might_not
stub_feature_flags(unbatch_graphql_queries: false)
sign_in(user)
visit(project_merge_request_path(project, merge_request))
+ close_rich_text_promo_popover_if_present
+
page.within('.mr-state-widget') do
click_button 'Merge'
end
@@ -36,6 +40,7 @@ RSpec.describe 'Merge request > User sees revert modal', :js, :sidekiq_might_not
context 'with page reload validates js correctly loaded' do
before do
visit(merge_request_path(merge_request))
+ close_rich_text_promo_popover_if_present
end
it_behaves_like 'showing the revert modal'
diff --git a/spec/frontend/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js
index 9b9a1a84b1d..db6cde883f3 100644
--- a/spec/frontend/diffs/components/diff_file_spec.js
+++ b/spec/frontend/diffs/components/diff_file_spec.js
@@ -546,24 +546,6 @@ describe('DiffFile', () => {
});
});
- it('loads collapsed file on mounted when single file mode is enabled', async () => {
- const file = {
- ...getReadableFile(),
- load_collapsed_diff_url: '/diff_for_path',
- highlighted_diff_lines: [],
- parallel_diff_lines: [],
- viewer: { name: 'collapsed', automaticallyCollapsed: true },
- };
-
- axiosMock.onGet(file.load_collapsed_diff_url).reply(HTTP_STATUS_OK, getReadableFile());
-
- ({ wrapper, store } = createComponent({ file, props: { viewDiffsFileByFile: true } }));
-
- await nextTick();
-
- expect(findLoader(wrapper).exists()).toBe(true);
- });
-
describe('merge conflicts', () => {
it('does not render conflict alert', () => {
const file = {
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index 963346a34b6..bbe748b8e1f 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -365,7 +365,7 @@ describe('DiffsStoreActions', () => {
{ type: types.SET_RETRIEVING_BATCHES, payload: false },
{ type: types.SET_BATCH_LOADING_STATE, payload: 'error' },
],
- [{ type: 'startRenderDiffsQueue' }, { type: 'startRenderDiffsQueue' }],
+ [],
);
});
});
@@ -664,41 +664,6 @@ describe('DiffsStoreActions', () => {
});
});
- describe('startRenderDiffsQueue', () => {
- it('should set all files to RENDER_FILE', () => {
- const state = {
- diffFiles: [
- {
- id: 1,
- renderIt: false,
- viewer: {
- automaticallyCollapsed: false,
- },
- },
- {
- id: 2,
- renderIt: false,
- viewer: {
- automaticallyCollapsed: false,
- },
- },
- ],
- };
-
- const pseudoCommit = (commitType, file) => {
- expect(commitType).toBe(types.RENDER_FILE);
- Object.assign(file, {
- renderIt: true,
- });
- };
-
- diffActions.startRenderDiffsQueue({ state, commit: pseudoCommit });
-
- expect(state.diffFiles[0].renderIt).toBe(true);
- expect(state.diffFiles[1].renderIt).toBe(true);
- });
- });
-
describe('setInlineDiffViewType', () => {
it('should set diff view type to inline and also set the cookie properly', async () => {
await testAction(
@@ -1286,12 +1251,11 @@ describe('DiffsStoreActions', () => {
$emit = jest.spyOn(eventHub, '$emit');
});
- it('renders and expands file for the given discussion id', () => {
+ it('expands the file for the given discussion id', () => {
const localState = state({ collapsed: true, renderIt: false });
diffActions.renderFileForDiscussionId({ rootState, state: localState, commit }, '123');
- expect(commit).toHaveBeenCalledWith('RENDER_FILE', localState.diffFiles[0]);
expect($emit).toHaveBeenCalledTimes(1);
expect(commonUtils.scrollToElement).toHaveBeenCalledTimes(1);
});
@@ -1378,18 +1342,6 @@ describe('DiffsStoreActions', () => {
});
});
- describe('setRenderIt', () => {
- it('commits RENDER_FILE', () => {
- return testAction(
- diffActions.setRenderIt,
- 'file',
- {},
- [{ type: types.RENDER_FILE, payload: 'file' }],
- [],
- );
- });
- });
-
describe('receiveFullDiffError', () => {
it('updates state with the file that did not load', () => {
return testAction(
@@ -1514,7 +1466,7 @@ describe('DiffsStoreActions', () => {
payload: { filePath: testFilePath, lines: [preparedLine, preparedLine] },
},
],
- [{ type: 'startRenderDiffsQueue' }],
+ [],
);
},
);
diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js
index 4f9534ed5db..274cb40dac8 100644
--- a/spec/frontend/diffs/store/mutations_spec.js
+++ b/spec/frontend/diffs/store/mutations_spec.js
@@ -105,7 +105,6 @@ describe('DiffsStoreMutations', () => {
mutations[types.SET_DIFF_DATA_BATCH](state, diffMock);
- expect(state.diffFiles[0].renderIt).toEqual(true);
expect(state.diffFiles[0].collapsed).toEqual(false);
expect(state.treeEntries[mockFile.file_path].diffLoaded).toBe(true);
});
diff --git a/spec/frontend/diffs/store/utils_spec.js b/spec/frontend/diffs/store/utils_spec.js
index 888df06d6b9..117ed56e347 100644
--- a/spec/frontend/diffs/store/utils_spec.js
+++ b/spec/frontend/diffs/store/utils_spec.js
@@ -437,7 +437,7 @@ describe('DiffsStoreUtils', () => {
});
});
- it('sets the renderIt and collapsed attribute on files', () => {
+ it('sets the collapsed attribute on files', () => {
const checkLine = preparedDiff.diff_files[0][INLINE_DIFF_LINES_KEY][0];
expect(checkLine.discussions.length).toBe(0);
@@ -448,7 +448,6 @@ describe('DiffsStoreUtils', () => {
expect(firstChar).not.toBe('+');
expect(firstChar).not.toBe('-');
- expect(preparedDiff.diff_files[0].renderIt).toBe(true);
expect(preparedDiff.diff_files[0].collapsed).toBe(false);
});
@@ -529,8 +528,7 @@ describe('DiffsStoreUtils', () => {
preparedDiffFiles = utils.prepareDiffData({ diff: mock, meta: true });
});
- it('sets the renderIt and collapsed attribute on files', () => {
- expect(preparedDiffFiles[0].renderIt).toBe(true);
+ it('sets the collapsed attribute on files', () => {
expect(preparedDiffFiles[0].collapsed).toBeUndefined();
});
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js
index 703f17cc77a..9fc688fd8a6 100644
--- a/spec/frontend/notes/components/comment_form_spec.js
+++ b/spec/frontend/notes/components/comment_form_spec.js
@@ -121,6 +121,15 @@ describe('issue_comment_form component', () => {
provide: {
glFeatures: features,
},
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
}),
);
};
diff --git a/spec/frontend/notes/components/note_form_spec.js b/spec/frontend/notes/components/note_form_spec.js
index 0a68225d411..fb81d5ce6d1 100644
--- a/spec/frontend/notes/components/note_form_spec.js
+++ b/spec/frontend/notes/components/note_form_spec.js
@@ -26,6 +26,15 @@ describe('issue_note_form component', () => {
provide: {
glFeatures: provide,
},
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
});
};
diff --git a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
index 0b3def2a69e..3b2533b1a27 100644
--- a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
+++ b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
@@ -95,6 +95,15 @@ describe('WikiForm', () => {
GlFormInput,
GlFormGroup,
},
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
}),
);
}
diff --git a/spec/frontend/pipeline_wizard/components/editor_spec.js b/spec/frontend/pipeline_wizard/components/editor_spec.js
index 6d7d4363189..2284c875f58 100644
--- a/spec/frontend/pipeline_wizard/components/editor_spec.js
+++ b/spec/frontend/pipeline_wizard/components/editor_spec.js
@@ -1,42 +1,58 @@
import { mount } from '@vue/test-utils';
import { Document } from 'yaml';
import YamlEditor from '~/pipeline_wizard/components/editor.vue';
+import SourceEditor from '~/editor/source_editor';
describe('Pages Yaml Editor wrapper', () => {
let wrapper;
+ const defaultDoc = new Document({ foo: 'bar' });
+
const defaultOptions = {
- propsData: { doc: new Document({ foo: 'bar' }), filename: 'foo.yml' },
+ propsData: { doc: defaultDoc, filename: 'foo.yml' },
+ };
+
+ const getLatestValue = () => {
+ const latest = wrapper.emitted('update:yaml').pop();
+ return latest[0];
};
describe('mount hook', () => {
beforeEach(() => {
+ jest.spyOn(SourceEditor.prototype, 'createInstance');
+
wrapper = mount(YamlEditor, defaultOptions);
});
- it('editor is mounted', () => {
- expect(wrapper.vm.editor).not.toBeUndefined();
- expect(wrapper.find('.gl-source-editor').exists()).toBe(true);
+ it('creates a source editor instance', () => {
+ expect(SourceEditor.prototype.createInstance).toHaveBeenCalledWith({
+ el: wrapper.element,
+ blobPath: 'foo.yml',
+ language: 'yaml',
+ });
+ });
+
+ it('editor is mounted in the wrapper', () => {
+ expect(wrapper.find('.gl-source-editor.monaco-editor').exists()).toBe(true);
+ });
+
+ it("causes the editor's value to be set to the stringified document", () => {
+ expect(getLatestValue()).toEqual(defaultDoc.toString());
});
});
describe('watchers', () => {
+ beforeEach(() => {
+ wrapper = mount(YamlEditor, defaultOptions);
+ });
+
describe('doc', () => {
const doc = new Document({ baz: ['bar'] });
- beforeEach(() => {
- wrapper = mount(YamlEditor, defaultOptions);
- });
-
- it("causes the editor's value to be set to the stringified document", async () => {
- await wrapper.setProps({ doc });
- expect(wrapper.vm.editor.getValue()).toEqual(doc.toString());
- });
-
it('emits an update:yaml event with the yaml representation of doc', async () => {
await wrapper.setProps({ doc });
- const changeEvents = wrapper.emitted('update:yaml');
- expect(changeEvents[2]).toEqual([doc.toString()]);
+
+ expect(getLatestValue()).toEqual(doc.toString());
});
it('does not cause the touch event to be emitted', () => {
diff --git a/spec/frontend/vue_shared/components/markdown/editor_mode_switcher_spec.js b/spec/frontend/vue_shared/components/markdown/editor_mode_switcher_spec.js
index f8d5faf317c..712e78458c6 100644
--- a/spec/frontend/vue_shared/components/markdown/editor_mode_switcher_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/editor_mode_switcher_spec.js
@@ -1,25 +1,47 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { GlButton, GlLink, GlPopover } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
import EditorModeSwitcher from '~/vue_shared/components/markdown/editor_mode_switcher.vue';
+import { counter } from '~/vue_shared/components/markdown/utils';
+import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
+import { stubComponent } from 'helpers/stub_component';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
+
+jest.mock('~/vue_shared/components/markdown/utils', () => ({
+ counter: jest.fn().mockReturnValue(0),
+}));
describe('vue_shared/component/markdown/editor_mode_switcher', () => {
let wrapper;
+ useLocalStorageSpy();
- const createComponent = ({ value } = {}) => {
- wrapper = shallowMount(EditorModeSwitcher, {
+ const createComponent = ({
+ value,
+ userCalloutDismisserSlotProps = { dismiss: jest.fn() },
+ } = {}) => {
+ wrapper = mount(EditorModeSwitcher, {
propsData: {
value,
},
+ stubs: {
+ UserCalloutDismisser: stubComponent(UserCalloutDismisser, {
+ render() {
+ return this.$scopedSlots.default(userCalloutDismisserSlotProps);
+ },
+ }),
+ },
});
};
const findSwitcherButton = () => wrapper.findComponent(GlButton);
+ const findUserCalloutDismisser = () => wrapper.findComponent(UserCalloutDismisser);
+ const findCalloutPopover = () => wrapper.findComponent(GlPopover);
describe.each`
- modeText | value | buttonText
- ${'Rich text'} | ${'richText'} | ${'Switch to plain text editing'}
- ${'Markdown'} | ${'markdown'} | ${'Switch to rich text editing'}
- `('when $modeText', ({ modeText, value, buttonText }) => {
+ value | buttonText
+ ${'richText'} | ${'Switch to plain text editing'}
+ ${'markdown'} | ${'Switch to rich text editing'}
+ `('when $value', ({ value, buttonText }) => {
beforeEach(() => {
createComponent({ value });
});
@@ -28,10 +50,66 @@ describe('vue_shared/component/markdown/editor_mode_switcher', () => {
expect(findSwitcherButton().text()).toEqual(buttonText);
});
- it('emits event on click', () => {
- findSwitcherButton(modeText).vm.$emit('click');
+ it('emits event on click', async () => {
+ await nextTick();
+ findSwitcherButton().vm.$emit('click');
+
+ expect(wrapper.emitted().switch).toEqual([[false]]);
+ });
+ });
+
+ describe('rich text editor callout', () => {
+ let dismiss;
+
+ beforeEach(() => {
+ dismiss = jest.fn();
+ createComponent({ value: 'markdown', userCalloutDismisserSlotProps: { dismiss } });
+ });
+
+ it('does not skip the user_callout_dismisser query', () => {
+ expect(findUserCalloutDismisser().props()).toMatchObject({
+ skipQuery: false,
+ featureName: 'rich_text_editor',
+ });
+ });
+
+ it('mounts new rich text editor popover', () => {
+ expect(findCalloutPopover().props()).toMatchObject({
+ showCloseButton: '',
+ triggers: 'manual',
+ target: 'switch-to-rich-text-editor',
+ });
+ });
+
+ it('dismisses the callout and emits "switch" event when popover close button is clicked', async () => {
+ await findCalloutPopover().findComponent(GlLink).vm.$emit('click');
+
+ expect(wrapper.emitted().switch).toEqual([[true]]);
+ expect(dismiss).toHaveBeenCalled();
+ });
+
+ it('dismisses the callout when action button is clicked', () => {
+ findSwitcherButton().vm.$emit('click');
+
+ expect(dismiss).toHaveBeenCalled();
+ });
+
+ it('does not show the callout if rich text is already enabled', async () => {
+ await wrapper.setProps({ value: 'richText' });
+
+ expect(findCalloutPopover().props()).toMatchObject({
+ show: false,
+ });
+ });
+
+ it('does not show the callout if already displayed once on the page', () => {
+ counter.mockReturnValue(1);
+
+ createComponent({ value: 'markdown' });
- expect(wrapper.emitted().input).toEqual([[]]);
+ expect(findCalloutPopover().props()).toMatchObject({
+ show: false,
+ });
});
});
});
diff --git a/spec/frontend/vue_shared/components/markdown/field_spec.js b/spec/frontend/vue_shared/components/markdown/field_spec.js
index 37d18455bf2..4ade8f28fd0 100644
--- a/spec/frontend/vue_shared/components/markdown/field_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/field_spec.js
@@ -67,6 +67,15 @@ describe('Markdown field component', () => {
showContentEditorSwitcher,
supportsQuickActions: true,
},
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
},
);
}
diff --git a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
index a9e8e9408f8..8411e22f6c7 100644
--- a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
@@ -64,6 +64,15 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
BubbleMenu: stubComponent(BubbleMenu),
...stubs,
},
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
});
};
@@ -439,8 +448,6 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
describe('when contentEditor is disabled', () => {
it('resets the editingMode to markdownField', () => {
- localStorage.setItem('gl-markdown-editor-mode', 'contentEditor');
-
buildWrapper({ propsData: { autosaveKey: 'issue/1234', enableContentEditor: false } });
expect(wrapper.vm.editingMode).toBe(EDITING_MODE_MARKDOWN_FIELD);
diff --git a/spec/frontend/vue_shared/components/markdown/toolbar_spec.js b/spec/frontend/vue_shared/components/markdown/toolbar_spec.js
index 28dc6fcde74..5bf11ff2b26 100644
--- a/spec/frontend/vue_shared/components/markdown/toolbar_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/toolbar_spec.js
@@ -1,13 +1,27 @@
import { mount } from '@vue/test-utils';
import Toolbar from '~/vue_shared/components/markdown/toolbar.vue';
import EditorModeSwitcher from '~/vue_shared/components/markdown/editor_mode_switcher.vue';
+import { updateText } from '~/lib/utils/text_markdown';
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+
+jest.mock('~/lib/utils/text_markdown');
describe('toolbar', () => {
let wrapper;
- const createWrapper = (props = {}) => {
+ const createWrapper = (props = {}, attachTo = document.body) => {
wrapper = mount(Toolbar, {
+ attachTo,
propsData: { markdownDocsPath: '', ...props },
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
});
};
@@ -47,15 +61,49 @@ describe('toolbar', () => {
describe('with content editor switcher', () => {
beforeEach(() => {
- createWrapper({
- showContentEditorSwitcher: true,
- });
+ setHTMLFixture(
+ '<div class="md-area"><textarea>some value</textarea><div id="root"></div></div>',
+ );
+ createWrapper(
+ {
+ showContentEditorSwitcher: true,
+ },
+ '#root',
+ );
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
});
it('re-emits event from switcher', () => {
- wrapper.findComponent(EditorModeSwitcher).vm.$emit('input', 'richText');
+ wrapper.findComponent(EditorModeSwitcher).vm.$emit('switch');
expect(wrapper.emitted('enableContentEditor')).toEqual([[]]);
+ expect(updateText).not.toHaveBeenCalled();
+ });
+
+ it('does not insert a template text if textarea has some value', () => {
+ wrapper.findComponent(EditorModeSwitcher).vm.$emit('switch', true);
+
+ expect(updateText).not.toHaveBeenCalled();
+ });
+
+ it('inserts a "getting started with rich text" template when switched for the first time', () => {
+ document.querySelector('textarea').value = '';
+
+ wrapper.findComponent(EditorModeSwitcher).vm.$emit('switch', true);
+
+ expect(updateText).toHaveBeenCalledWith(
+ expect.objectContaining({
+ tag: `### Rich text editor
+
+Try out **styling** _your_ content right here or read the [direction](https://about.gitlab.com/direction/plan/knowledge/content_editor/).`,
+ textArea: document.querySelector('textarea'),
+ cursorOffset: 0,
+ wrap: false,
+ }),
+ );
});
});
});
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
index fa38ab8d44d..d2b7b2e89c8 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
@@ -1,13 +1,16 @@
import { GlButton, GlBadge, GlIcon, GlAvatarLabeled, GlAvatarLink } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { TYPE_ISSUE, WORKSPACE_PROJECT } from '~/issues/constants';
+import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
import IssuableHeader from '~/vue_shared/issuable/show/components/issuable_header.vue';
-
import { mockIssuableShowProps, mockIssuable } from '../mock_data';
const issuableHeaderProps = {
...mockIssuable,
...mockIssuableShowProps,
+ issuableType: TYPE_ISSUE,
+ workspaceType: WORKSPACE_PROJECT,
};
describe('IssuableHeader', () => {
@@ -53,6 +56,14 @@ describe('IssuableHeader', () => {
setHTMLFixture('<button class="js-toggle-right-sidebar-button">Collapse sidebar</button>');
});
+ it('emits a "toggle" event', () => {
+ createComponent();
+
+ findButton().vm.$emit('click');
+
+ expect(wrapper.emitted('toggle')).toEqual([[]]);
+ });
+
it('dispatches `click` event on sidebar toggle button', () => {
createComponent();
const toggleSidebarButtonEl = document.querySelector('.js-toggle-right-sidebar-button');
@@ -94,14 +105,12 @@ describe('IssuableHeader', () => {
});
it('renders confidential icon when issuable is confidential', () => {
- createComponent({
- confidential: true,
- });
+ createComponent({ confidential: true });
- const confidentialEl = wrapper.findByTestId('confidential');
-
- expect(confidentialEl.exists()).toBe(true);
- expect(confidentialEl.findComponent(GlIcon).props('name')).toBe('eye-slash');
+ expect(wrapper.findComponent(ConfidentialityBadge).props()).toEqual({
+ issuableType: 'issue',
+ workspaceType: 'project',
+ });
});
it('renders issuable author avatar', () => {
diff --git a/spec/frontend_integration/content_editor/content_editor_integration_spec.js b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
index 6bafe609995..8419c7aae63 100644
--- a/spec/frontend_integration/content_editor/content_editor_integration_spec.js
+++ b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
@@ -22,6 +22,15 @@ describe('content_editor', () => {
listeners: {
...listeners,
},
+ mocks: {
+ $apollo: {
+ queries: {
+ currentUser: {
+ loading: false,
+ },
+ },
+ },
+ },
});
};
diff --git a/spec/helpers/admin/application_settings/settings_helper_spec.rb b/spec/helpers/admin/application_settings/settings_helper_spec.rb
index efffc224eb2..b008f52c0eb 100644
--- a/spec/helpers/admin/application_settings/settings_helper_spec.rb
+++ b/spec/helpers/admin/application_settings/settings_helper_spec.rb
@@ -33,6 +33,12 @@ RSpec.describe Admin::ApplicationSettings::SettingsHelper do
end
describe 'Code Suggestions for Self-Managed instances', feature_category: :code_suggestions do
+ describe '#code_suggestions_description' do
+ subject { helper.code_suggestions_description }
+
+ it { is_expected.to include 'https://docs.gitlab.com/ee/user/project/repository/code_suggestions.html' }
+ end
+
describe '#code_suggestions_token_explanation' do
subject { helper.code_suggestions_token_explanation }
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 11f177b8f02..1c02b4754fa 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -685,14 +685,6 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do
it_behaves_like 'makes recursive queries'
end
-
- context 'when feature flag :use_traversal_ids_for_descendants_scopes is disabled' do
- before do
- stub_feature_flags(use_traversal_ids_for_descendants_scopes: false)
- end
-
- it_behaves_like 'makes recursive queries'
- end
end
describe '.self_and_descendant_ids' do
@@ -709,14 +701,6 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do
it_behaves_like 'makes recursive queries'
end
-
- context 'when feature flag :use_traversal_ids_for_descendants_scopes is disabled' do
- before do
- stub_feature_flags(use_traversal_ids_for_descendants_scopes: false)
- end
-
- it_behaves_like 'makes recursive queries'
- end
end
end
diff --git a/spec/support/helpers/content_editor_helpers.rb b/spec/support/helpers/content_editor_helpers.rb
index a3ecafbbec6..a6cc2560d0b 100644
--- a/spec/support/helpers/content_editor_helpers.rb
+++ b/spec/support/helpers/content_editor_helpers.rb
@@ -1,6 +1,14 @@
# frozen_string_literal: true
module ContentEditorHelpers
+ def close_rich_text_promo_popover_if_present
+ return unless page.has_css?("[data-testid='rich-text-promo-popover']")
+
+ page.within("[data-testid='rich-text-promo-popover']") do
+ click_button "Close"
+ end
+ end
+
def switch_to_content_editor
click_button("Switch to rich text editing")
end
diff --git a/spec/support/shared_contexts/merge_request_create_shared_context.rb b/spec/support/shared_contexts/merge_request_create_shared_context.rb
index fc9a3767365..bf8eeeb7ab6 100644
--- a/spec/support/shared_contexts/merge_request_create_shared_context.rb
+++ b/spec/support/shared_contexts/merge_request_create_shared_context.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_context 'merge request create context' do
+ include ContentEditorHelpers
+
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:target_project) { create(:project, :public, :repository) }
@@ -23,5 +25,7 @@ RSpec.shared_context 'merge request create context' do
source_branch: 'fix',
target_branch: 'master'
})
+
+ close_rich_text_promo_popover_if_present
end
end
diff --git a/spec/support/shared_contexts/merge_request_edit_shared_context.rb b/spec/support/shared_contexts/merge_request_edit_shared_context.rb
index f0e89b0c5f9..8fe0174b13e 100644
--- a/spec/support/shared_contexts/merge_request_edit_shared_context.rb
+++ b/spec/support/shared_contexts/merge_request_edit_shared_context.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_context 'merge request edit context' do
+ include ContentEditorHelpers
+
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:milestone) { create(:milestone, project: target_project) }
@@ -25,5 +27,6 @@ RSpec.shared_context 'merge request edit context' do
sign_in(user)
visit edit_project_merge_request_path(target_project, merge_request)
+ close_rich_text_promo_popover_if_present
end
end
diff --git a/spec/support/shared_examples/features/discussion_comments_shared_example.rb b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
index ba520d0c609..430a8ac39d7 100644
--- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb
+++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
@@ -150,6 +150,8 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
end
RSpec.shared_examples 'thread comments for issue, epic and merge request' do |resource_name|
+ include ContentEditorHelpers
+
let(:form_selector) { '.js-main-target-form' }
let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" }
@@ -159,6 +161,10 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' }
let(:comment) { 'My comment' }
+ before do
+ close_rich_text_promo_popover_if_present
+ end
+
it 'clicking "Comment" will post a comment' do
expect(page).to have_selector toggle_selector
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index 14e53dc8655..f802404518b 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -125,7 +125,11 @@ RSpec.shared_examples 'an editable merge request' do
it 'allows to unselect "Remove source branch"', :js do
expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
- visit edit_project_merge_request_path(target_project, merge_request)
+ begin
+ visit edit_project_merge_request_path(target_project, merge_request)
+ rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError
+ end
+
uncheck 'Delete source branch when merge request is accepted'
click_button 'Save changes'
diff --git a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
index ed885d7a226..c3df89c8002 100644
--- a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
@@ -6,6 +6,7 @@
RSpec.shared_examples 'User creates wiki page' do
include WikiHelpers
+ include ContentEditorHelpers
before do
sign_in(user)
@@ -18,6 +19,7 @@ RSpec.shared_examples 'User creates wiki page' do
wait_for_svg_to_be_loaded(example)
click_link "Create your first page"
+ close_rich_text_promo_popover_if_present
end
it 'shows all available formats in the dropdown' do
@@ -190,6 +192,7 @@ RSpec.shared_examples 'User creates wiki page' do
context "via the `new wiki page` page", :js do
it "creates a page with a single word" do
click_link("New page")
+ close_rich_text_promo_popover_if_present
page.within(".wiki-form") do
fill_in(:wiki_title, with: "foo")
@@ -208,6 +211,7 @@ RSpec.shared_examples 'User creates wiki page' do
it "creates a page with spaces in the name", :js do
click_link("New page")
+ close_rich_text_promo_popover_if_present
page.within(".wiki-form") do
fill_in(:wiki_title, with: "Spaces in the name")
@@ -226,6 +230,7 @@ RSpec.shared_examples 'User creates wiki page' do
it "creates a page with hyphens in the name", :js do
click_link("New page")
+ close_rich_text_promo_popover_if_present
page.within(".wiki-form") do
fill_in(:wiki_title, with: "hyphens-in-the-name")
@@ -249,6 +254,7 @@ RSpec.shared_examples 'User creates wiki page' do
context 'when a server side validation error is returned' do
it "still displays edit form", :js do
click_link("New page")
+ close_rich_text_promo_popover_if_present
page.within(".wiki-form") do
fill_in(:wiki_title, with: "home")
@@ -266,6 +272,7 @@ RSpec.shared_examples 'User creates wiki page' do
it "shows the emoji autocompletion dropdown", :js do
click_link("New page")
+ close_rich_text_promo_popover_if_present
page.within(".wiki-form") do
find("#wiki_content").native.send_keys("")
diff --git a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
index ca68df9a89b..827c875494a 100644
--- a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
@@ -5,6 +5,8 @@
# user
RSpec.shared_examples 'User previews wiki changes' do
+ include ContentEditorHelpers
+
let(:wiki_page) { build(:wiki_page, wiki: wiki) }
before do
@@ -74,6 +76,7 @@ RSpec.shared_examples 'User previews wiki changes' do
before do
wiki_page.create # rubocop:disable Rails/SaveBang
visit wiki_page_path(wiki, wiki_page, action: :edit)
+ close_rich_text_promo_popover_if_present
end
it_behaves_like 'relative links' do
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 91cacaf9209..d06f04db1ce 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
@@ -6,6 +6,8 @@
RSpec.shared_examples 'User updates wiki page' do
include WikiHelpers
+ include ContentEditorHelpers
+
let(:diagramsnet_url) { 'https://embed.diagrams.net' }
before do
@@ -21,6 +23,7 @@ RSpec.shared_examples 'User updates wiki page' do
wait_for_svg_to_be_loaded(example)
click_link "Create your first page"
+ close_rich_text_promo_popover_if_present
end
it 'redirects back to the home edit page' do
@@ -67,6 +70,7 @@ RSpec.shared_examples 'User updates wiki page' do
visit(wiki_path(wiki))
click_link('Edit')
+ close_rich_text_promo_popover_if_present
end
it 'updates a page', :js do
@@ -126,10 +130,6 @@ RSpec.shared_examples 'User updates wiki page' do
expect(page).to have_content('Updated Wiki Content')
end
- it 'focuses on the content field', :js do
- expect(page).to have_selector '.note-textarea:focus'
- end
-
it 'cancels editing of a page' do
page.within(:css, '.wiki-form .form-actions') do
click_on('Cancel')
@@ -164,6 +164,7 @@ RSpec.shared_examples 'User updates wiki page' do
before do
visit wiki_page_path(wiki, wiki_page, action: :edit)
+ close_rich_text_promo_popover_if_present
end
it 'moves the page to the root folder', :js do
@@ -234,6 +235,7 @@ RSpec.shared_examples 'User updates wiki page' do
stub_application_setting(wiki_page_max_content_bytes: 10)
visit wiki_page_path(wiki_page.wiki, wiki_page, action: :edit)
+ close_rich_text_promo_popover_if_present
end
it 'allows changing the title if the content does not change', :js do
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..3ee7725305e 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
@@ -6,6 +6,7 @@
RSpec.shared_examples 'User views a wiki page' do
include WikiHelpers
+ include ContentEditorHelpers
let(:path) { 'image.png' }
let(:wiki_page) do
@@ -269,6 +270,7 @@ RSpec.shared_examples 'User views a wiki page' do
wait_for_svg_to_be_loaded
click_link "Create your first page"
+ close_rich_text_promo_popover_if_present
expect(page).to have_content('Create New Page')
end
diff --git a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb
index 7cbaf40721a..4b27f1f2520 100644
--- a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb
@@ -2,6 +2,7 @@
RSpec.shared_examples 'close quick action' do |issuable_type|
include Features::NotesHelpers
+ include ContentEditorHelpers
before do
project.add_maintainer(maintainer)
@@ -76,6 +77,7 @@ RSpec.shared_examples 'close quick action' do |issuable_type|
context "preview of note on #{issuable_type}", :js do
it 'explains close quick action' do
visit public_send("project_#{issuable_type}_path", project, issuable)
+ close_rich_text_promo_popover_if_present
preview_note("this is done, close\n/close") do
expect(page).not_to have_content '/close'
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 61c443399a8..af5ecf8d4eb 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.4
- gitlab.com/gitlab-org/gitaly/v16 v16.1.1
+ gitlab.com/gitlab-org/gitaly/v16 v16.1.2
gitlab.com/gitlab-org/labkit v1.19.0
gocloud.dev v0.29.0
golang.org/x/image v0.7.0
diff --git a/workhorse/go.sum b/workhorse/go.sum
index f0510497de2..b175bf0f690 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -1931,8 +1931,8 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-gitlab.com/gitlab-org/gitaly/v16 v16.1.1 h1:0WFlmCii66ys0723lYkp8ZXp7nWbA2FcNUMK/pOw+fU=
-gitlab.com/gitlab-org/gitaly/v16 v16.1.1/go.mod h1:gfmwpE66X4lwAO/RdchFhNINSwteGFer4loUOa94nQE=
+gitlab.com/gitlab-org/gitaly/v16 v16.1.2 h1:l7sltwjB/shkqlbuQzfeS3PonrQljCBKOZ2Uozk8ewA=
+gitlab.com/gitlab-org/gitaly/v16 v16.1.2/go.mod h1:gfmwpE66X4lwAO/RdchFhNINSwteGFer4loUOa94nQE=
gitlab.com/gitlab-org/labkit v1.19.0 h1:7j5NOHE42R0Eu3Bj2BgfbGf4aN2HXMTmCN7H2wcqqyA=
gitlab.com/gitlab-org/labkit v1.19.0/go.mod h1:zeATDAaSBelPcPLbTTq8J3ZJEHyPTLVBM1q3nva+/W4=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
diff --git a/yarn.lock b/yarn.lock
index 330ecdfc6b2..d7de1952414 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1132,10 +1132,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.55.0.tgz#c67c5dddf54c400485762aff6a693e1f5a643739"
integrity sha512-1wuXRGhrWKfWXSM9ZI1aRHlZ0wv4X7tJjDil+AQVjPBANB6oBXAPAiga+qkzkBHss7TzyOjY7OytG/9L5weDLg==
-"@gitlab/ui@64.19.0":
- version "64.19.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-64.19.0.tgz#89b77cac7483027c5087e0b5c79f66eed4fa6183"
- integrity sha512-aU1+/kM71YOlXqS9HhQ4ya0yF5UJS8mtB+aP8ThWui6SWymgm59VoER1U22bND7P32IOQ5meRXpgNN3T70vZSg==
+"@gitlab/ui@64.20.0":
+ version "64.20.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-64.20.0.tgz#ee7e0bdee185cfb18c6b540d79d2a88839a76f61"
+ integrity sha512-lQJP6twO6BhAsLj5FO6vCrGCjgSoNCvrZGxJCAx0FZrI8Dn/+DGiML2mqmmI7WG53+s5rV/xgMniUwBN2TQa0Q==
dependencies:
"@floating-ui/dom" "1.2.9"
bootstrap-vue "2.23.1"