`;
document.body.append(div);
+
+if (!LIVE_RELOAD) {
+ setTimeout(() => {
+ window.location.reload();
+ }, 5000);
+}
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 684808e748d..81a934babfe 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -993,19 +993,6 @@ input {
.top-nav-toggle .dropdown-icon {
margin-right: 0.5rem;
}
-.tanuki-logo .tanuki-left-ear,
-.tanuki-logo .tanuki-right-ear,
-.tanuki-logo .tanuki-nose {
- fill: #e24329;
-}
-.tanuki-logo .tanuki-left-eye,
-.tanuki-logo .tanuki-right-eye {
- fill: #fc6d26;
-}
-.tanuki-logo .tanuki-left-cheek,
-.tanuki-logo .tanuki-right-cheek {
- fill: #fca326;
-}
.context-header {
position: relative;
margin-right: 2px;
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 0d35c400676..afeaf16e4b6 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -978,19 +978,6 @@ input {
.top-nav-toggle .dropdown-icon {
margin-right: 0.5rem;
}
-.tanuki-logo .tanuki-left-ear,
-.tanuki-logo .tanuki-right-ear,
-.tanuki-logo .tanuki-nose {
- fill: #e24329;
-}
-.tanuki-logo .tanuki-left-eye,
-.tanuki-logo .tanuki-right-eye {
- fill: #fc6d26;
-}
-.tanuki-logo .tanuki-left-cheek,
-.tanuki-logo .tanuki-right-cheek {
- fill: #fca326;
-}
.context-header {
position: relative;
margin-right: 2px;
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index c5cbe58ec27..213d1c013a0 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -514,19 +514,6 @@ label.label-bold {
.navbar-empty .brand-header-logo {
max-height: 100%;
}
-.tanuki-logo .tanuki-left-ear,
-.tanuki-logo .tanuki-right-ear,
-.tanuki-logo .tanuki-nose {
- fill: #e24329;
-}
-.tanuki-logo .tanuki-left-eye,
-.tanuki-logo .tanuki-right-eye {
- fill: #fc6d26;
-}
-.tanuki-logo .tanuki-left-cheek,
-.tanuki-logo .tanuki-right-cheek {
- fill: #fca326;
-}
input::-moz-placeholder {
color: #868686;
opacity: 1;
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index c8b1ed04e4a..cd0311d0bac 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -106,6 +106,8 @@ class Projects::IssuesController < Projects::ApplicationController
@issue = @noteable = service.execute
+ @add_related_issue = add_related_issue
+
@merge_request_to_resolve_discussions_of = service.merge_request_to_resolve_discussions_of
if params[:discussion_to_resolve]
@@ -122,6 +124,7 @@ class Projects::IssuesController < Projects::ApplicationController
def create
create_params = issue_params.merge(
+ add_related_issue: add_related_issue,
merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of],
discussion_to_resolve: params[:discussion_to_resolve]
)
@@ -377,6 +380,11 @@ class Projects::IssuesController < Projects::ApplicationController
action_name == 'service_desk'
end
+ def add_related_issue
+ add_related_issue = project.issues.find_by_iid(params[:add_related_issue])
+ add_related_issue if Ability.allowed?(current_user, :read_issue, add_related_issue)
+ end
+
# Overridden in EE
def create_vulnerability_issue_feedback(issue); end
end
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 5ca360f38da..cb43d911a2f 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -38,6 +38,8 @@ module AppearancesHelper
def brand_header_logo
if current_appearance&.header_logo?
image_tag current_appearance.header_logo_path, class: 'brand-header-logo'
+ elsif Feature.enabled?(:ukraine_support_tanuki)
+ render partial: 'shared/logo_ukraine', formats: :svg
else
render partial: 'shared/logo', formats: :svg
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 8e7f5060412..9187a3eda0a 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -169,7 +169,7 @@ module IssuesHelper
end
def issue_header_actions_data(project, issuable, current_user)
- new_issuable_params = { issue: { description: _('Related to #%{issue_id}.') % { issue_id: issuable.iid } + "\n\n" } }
+ new_issuable_params = { issue: {}, add_related_issue: issuable.iid }
if issuable.incident?
new_issuable_params[:issuable_template] = 'incident'
new_issuable_params[:issue][:issue_type] = 'incident'
diff --git a/app/models/user.rb b/app/models/user.rb
index 89a6bd0f008..e49a39079ef 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -135,6 +135,7 @@ class User < ApplicationRecord
has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :webauthn_registrations
has_many :chat_names, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :saved_replies, class_name: '::Users::SavedReply'
has_one :user_synced_attributes_metadata, autosave: true
has_one :aws_role, class_name: 'Aws::Role'
diff --git a/app/models/users/saved_reply.rb b/app/models/users/saved_reply.rb
new file mode 100644
index 00000000000..7737d826b05
--- /dev/null
+++ b/app/models/users/saved_reply.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Users
+ class SavedReply < ApplicationRecord
+ self.table_name = 'saved_replies'
+
+ belongs_to :user
+
+ validates :user_id, :name, :content, presence: true
+ validates :name,
+ length: { maximum: 255 },
+ uniqueness: { scope: [:user_id] },
+ format: {
+ with: Gitlab::Regex.saved_reply_name_regex,
+ message: Gitlab::Regex.saved_reply_name_regex_message
+ }
+ validates :content, length: { maximum: 10000 }
+ end
+end
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 7fbf7c6af58..7ab663718db 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -23,6 +23,7 @@ module Issues
handle_move_between_ids(@issue)
+ @add_related_issue ||= params.delete(:add_related_issue)
filter_resolve_discussion_params
create(@issue, skip_system_notes: skip_system_notes)
@@ -52,6 +53,7 @@ module Issues
# Add new items to Issues::AfterCreateService if they can be performed in Sidekiq
def after_create(issue)
user_agent_detail_service.create
+ handle_add_related_issue(issue)
resolve_discussions_with_issue(issue)
create_escalation_status(issue)
@@ -91,6 +93,12 @@ module Issues
def user_agent_detail_service
UserAgentDetailService.new(spammable: @issue, spam_params: spam_params)
end
+
+ def handle_add_related_issue(issue)
+ return unless @add_related_issue
+
+ IssueLinks::CreateService.new(issue, issue.author, { target_issuable: @add_related_issue }).execute
+ end
end
end
diff --git a/app/views/shared/_logo_ukraine.svg b/app/views/shared/_logo_ukraine.svg
new file mode 100644
index 00000000000..e2c2bb3855d
--- /dev/null
+++ b/app/views/shared/_logo_ukraine.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index 9e42c528a11..34720576526 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -4,6 +4,16 @@
- has_due_date = issuable.has_attribute?(:due_date)
- form = local_assigns.fetch(:form)
+- if @add_related_issue
+ .form-group.row
+ .offset-sm-2.col-sm-10
+ .form-check
+ = check_box_tag :add_related_issue, @add_related_issue.iid, true, class: 'form-check-input'
+ = label_tag :add_related_issue, class: 'form-check-label' do
+ - add_related_issue_link = link_to "\##{@add_related_issue.iid}", issue_path(@add_related_issue), class: ['has-tooltip'], title: @add_related_issue.title
+ #{_('Relate to %{issuable_type} %{add_related_issue_link}').html_safe % { issuable_type: @add_related_issue.issue_type, add_related_issue_link: add_related_issue_link }}
+ %p.text-muted= _('Adds this %{issuable_type} as related to the %{issuable_type} it was created from') % { issuable_type: @add_related_issue.issue_type }
+
- if issuable.respond_to?(:confidential) && can?(current_user, :set_confidentiality, issuable)
.form-group.row
.offset-sm-2.col-sm-10
diff --git a/config/feature_flags/development/ukraine_support_tanuki.yml b/config/feature_flags/development/ukraine_support_tanuki.yml
new file mode 100644
index 00000000000..3a2c64a5aa4
--- /dev/null
+++ b/config/feature_flags/development/ukraine_support_tanuki.yml
@@ -0,0 +1,8 @@
+---
+name: ukraine_support_tanuki
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82050
+rollout_issue_url:
+milestone: '14.9'
+type: development
+group: group::editor
+default_enabled: false
diff --git a/config/helpers/incremental_webpack_compiler/compiler.js b/config/helpers/incremental_webpack_compiler/compiler.js
index 0ef090bce24..2614501faa4 100644
--- a/config/helpers/incremental_webpack_compiler/compiler.js
+++ b/config/helpers/incremental_webpack_compiler/compiler.js
@@ -102,7 +102,7 @@ class IncrementalWebpackCompiler {
setTimeout(() => {
devServer.invalidate(() => {
- if (devServer.sockets) {
+ if (Array.isArray(devServer.webSocketServer && devServer.webSocketServer.clients)) {
devServer.sendMessage(devServer.webSocketServer.clients, 'static-changed');
}
});
diff --git a/config/webpack.config.js b/config/webpack.config.js
index b8bc33f5d07..66c02dcc87d 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -677,6 +677,7 @@ module.exports = {
IS_JH: IS_JH ? 'window.gon && window.gon.jh' : JSON.stringify(false),
// This is used by Sourcegraph because these assets are loaded dnamically
'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
+ ...(IS_PRODUCTION ? {} : { LIVE_RELOAD: DEV_SERVER_LIVERELOAD }),
}),
/* Pikaday has a optional dependency to moment.
diff --git a/db/migrate/20220216110023_create_saved_replies.rb b/db/migrate/20220216110023_create_saved_replies.rb
new file mode 100644
index 00000000000..e4b6c039dee
--- /dev/null
+++ b/db/migrate/20220216110023_create_saved_replies.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class CreateSavedReplies < Gitlab::Database::Migration[1.0]
+ enable_lock_retries!
+
+ def up
+ create_table :saved_replies do |t|
+ t.references :user, index: false, null: false, foreign_key: { on_delete: :cascade }
+ t.timestamps_with_timezone null: false
+ t.text :name, null: false, limit: 255
+ t.text :content, null: false, limit: 10000
+
+ t.index [:user_id, :name], name: 'index_saved_replies_on_name_text_pattern_ops', unique: true, opclass: { name: :text_pattern_ops }
+ end
+ end
+
+ def down
+ drop_table :saved_replies, if_exists: true
+ end
+end
diff --git a/db/schema_migrations/20220216110023 b/db/schema_migrations/20220216110023
new file mode 100644
index 00000000000..30acd6fdaf2
--- /dev/null
+++ b/db/schema_migrations/20220216110023
@@ -0,0 +1 @@
+5931c4981c89d65c5aaca05dc8375c2c21bb595e28354d6623986d906ece165d
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 29a6d140d1f..c66d1ccbbf8 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -20171,6 +20171,26 @@ CREATE SEQUENCE saml_providers_id_seq
ALTER SEQUENCE saml_providers_id_seq OWNED BY saml_providers.id;
+CREATE TABLE saved_replies (
+ id bigint NOT NULL,
+ user_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ name text NOT NULL,
+ content text NOT NULL,
+ CONSTRAINT check_0cb57dc22a CHECK ((char_length(content) <= 10000)),
+ CONSTRAINT check_2eb3366d7f CHECK ((char_length(name) <= 255))
+);
+
+CREATE SEQUENCE saved_replies_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE saved_replies_id_seq OWNED BY saved_replies.id;
+
CREATE TABLE schema_migrations (
version character varying NOT NULL,
finished_at timestamp with time zone DEFAULT now()
@@ -22966,6 +22986,8 @@ ALTER TABLE ONLY saml_group_links ALTER COLUMN id SET DEFAULT nextval('saml_grou
ALTER TABLE ONLY saml_providers ALTER COLUMN id SET DEFAULT nextval('saml_providers_id_seq'::regclass);
+ALTER TABLE ONLY saved_replies ALTER COLUMN id SET DEFAULT nextval('saved_replies_id_seq'::regclass);
+
ALTER TABLE ONLY scim_identities ALTER COLUMN id SET DEFAULT nextval('scim_identities_id_seq'::regclass);
ALTER TABLE ONLY scim_oauth_access_tokens ALTER COLUMN id SET DEFAULT nextval('scim_oauth_access_tokens_id_seq'::regclass);
@@ -25112,6 +25134,9 @@ ALTER TABLE ONLY saml_group_links
ALTER TABLE ONLY saml_providers
ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY saved_replies
+ ADD CONSTRAINT saved_replies_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY schema_migrations
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
@@ -28894,6 +28919,8 @@ CREATE UNIQUE INDEX index_saml_group_links_on_group_id_and_saml_group_name ON sa
CREATE INDEX index_saml_providers_on_group_id ON saml_providers USING btree (group_id);
+CREATE UNIQUE INDEX index_saved_replies_on_name_text_pattern_ops ON saved_replies USING btree (user_id, name text_pattern_ops);
+
CREATE INDEX index_scim_identities_on_group_id ON scim_identities USING btree (group_id);
CREATE UNIQUE INDEX index_scim_identities_on_lower_extern_uid_and_group_id ON scim_identities USING btree (lower((extern_uid)::text), group_id);
@@ -32789,6 +32816,9 @@ ALTER TABLE ONLY resource_milestone_events
ALTER TABLE ONLY term_agreements
ADD CONSTRAINT fk_rails_a88721bcdf FOREIGN KEY (term_id) REFERENCES application_setting_terms(id);
+ALTER TABLE ONLY saved_replies
+ ADD CONSTRAINT fk_rails_a8bf5bf111 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_pipeline_artifacts
ADD CONSTRAINT fk_rails_a9e811a466 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 1e474775b58..2dc2d4960b7 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -19,7 +19,7 @@ You can create an issue in many ways in GitLab:
- [From a project](#from-a-project)
- [From a group](#from-a-group)
-- [From another issue](#from-another-issue)
+- [From another issue or incident](#from-another-issue-or-incident)
- [From an issue board](#from-an-issue-board)
- [By sending an email](#by-sending-an-email)
- [Using a URL with prefilled values](#using-a-url-with-prefilled-values)
@@ -70,9 +70,10 @@ The newly created issue opens.
The project you selected most recently becomes the default for your next visit.
This can save you a lot of time and clicks, if you mostly create issues for the same project.
-### From another issue
+### From another issue or incident
-> New issue becoming linked to the issue of origin [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68226) in GitLab 14.3.
+> - New issue becoming linked to the issue of origin [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68226) in GitLab 14.3.
+> - **Relate to…** checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198494) in GitLab 14.9.
You can create a new issue from an existing one. The two issues can then be marked as related.
@@ -83,10 +84,10 @@ Prerequisites:
To create an issue from another issue:
1. In an existing issue, select the vertical ellipsis (**{ellipsis_v}**).
-1. Select **New issue**.
+1. Select **New related issue**.
1. Complete the [fields](#fields-in-the-new-issue-form).
- The new issue's description is prefilled with `Related to #123`, where `123` is the ID of the
- issue of origin. If you keep this mention in the description, the two issues become
+ The new issue form has a **Relate to issue #123** checkbox, where `123` is the ID of the
+ issue of origin. If you keep this checkbox checked, the two issues become
[linked](related_issues.md).
1. Select **Create issue**.
@@ -160,7 +161,8 @@ To regenerate the email address:
### Using a URL with prefilled values
-> Ability to use both `issuable_template` and `issue[description]` in the same URL [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80554) in GitLab 14.9.
+> - Ability to use both `issuable_template` and `issue[description]` in the same URL [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80554) in GitLab 14.9.
+> - Ability to specify `add_related_issue` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198494) in GitLab 14.9.
To link directly to the new issue page with prefilled fields, use query
string parameters in a URL. You can embed a URL in an external
@@ -173,6 +175,7 @@ HTML page to create issues with certain fields prefilled.
| Description template | `issuable_template` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
| Description | `issue[description]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). If used in combination with `issuable_template` or a [default issue template](../description_templates.md#set-a-default-template-for-merge-requests-and-issues), the `issue[description]` value is appended to the template. |
| Confidential | `issue[confidential]` | If `true`, the issue is marked as confidential. |
+| Relate to… | `add_related_issue` | A numeric issue ID. If present, the issue form shows a [**Relate to…** checkbox](#from-another-issue-or-incident) to optionally link the new issue to the specified existing issue. |
Adapt these examples to form your new issue URL with prefilled fields.
To create an issue in the GitLab project:
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
index 95005871194..4c7e0a4e613 100644
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ b/lib/gitlab/database/gitlab_schemas.yml
@@ -460,6 +460,7 @@ reviews: :gitlab_main
routes: :gitlab_main
saml_group_links: :gitlab_main
saml_providers: :gitlab_main
+saved_replies: :gitlab_main
schema_migrations: :gitlab_shared
scim_identities: :gitlab_main
scim_oauth_access_tokens: :gitlab_main
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index a6491d23bf5..ddb6bc37bba 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -459,6 +459,15 @@ module Gitlab
"can contain only lowercase letters, digits, '_' and '-'. " \
"Must start with a letter, and cannot end with '-' or '_'"
end
+
+ def saved_reply_name_regex
+ @saved_reply_name_regex ||= /\A[a-z]([a-z0-9\-_]*[a-z0-9])?\z/.freeze
+ end
+
+ def saved_reply_name_regex_message
+ "can contain only lowercase letters, digits, '_' and '-'. " \
+ "Must start with a letter, and cannot end with '-' or '_'"
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 337cf832ee7..c6cf57d5005 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2394,6 +2394,9 @@ msgstr ""
msgid "Adds email participant(s)."
msgstr ""
+msgid "Adds this %{issuable_type} as related to the %{issuable_type} it was created from"
+msgstr ""
+
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
@@ -24337,9 +24340,6 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{issueType}"
-msgstr ""
-
msgid "New %{type} in %{project}"
msgstr ""
@@ -24507,6 +24507,9 @@ msgstr ""
msgid "New public deploy key"
msgstr ""
+msgid "New related %{issueType}"
+msgstr ""
+
msgid "New release"
msgstr ""
@@ -30313,6 +30316,9 @@ msgstr ""
msgid "Rejected (closed)"
msgstr ""
+msgid "Relate to %{issuable_type} %{add_related_issue_link}"
+msgstr ""
+
msgid "Related feature flags"
msgstr ""
@@ -30322,9 +30328,6 @@ msgstr ""
msgid "Related merge requests"
msgstr ""
-msgid "Related to #%{issue_id}."
-msgstr ""
-
msgid "Relates to"
msgstr ""
diff --git a/spec/factories/users/saved_replies.rb b/spec/factories/users/saved_replies.rb
new file mode 100644
index 00000000000..a3c450fb1f1
--- /dev/null
+++ b/spec/factories/users/saved_replies.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :saved_reply, class: 'Users::SavedReply' do
+ sequence(:name) { |n| "saved_reply_#{n}" }
+ content { 'Saved Reply Content' }
+
+ user
+ end
+end
diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb
index e0261ad4d0b..a669966502e 100644
--- a/spec/features/incidents/user_views_incident_spec.rb
+++ b/spec/features/incidents/user_views_incident_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe "User views incident" do
it 'shows the merge request and incident actions', :js, :aggregate_failures do
click_button 'Incident actions'
- expect(page).to have_link('New incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident', description: "Related to \##{incident.iid}.\n\n" } }))
+ expect(page).to have_link('New related incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' }, add_related_issue: incident.iid }))
expect(page).to have_button('Create merge request')
expect(page).to have_button('Close incident')
end
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index d67562e32fc..18942e48400 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -8,16 +8,19 @@ RSpec.describe 'New/edit issue', :js do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
+ let_it_be(:guest) { create(:user) }
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:label) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
let_it_be(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
+ let_it_be(:confidential_issue) { create(:issue, project: project, assignees: [user], milestone: milestone, confidential: true) }
let(:current_user) { user }
before_all do
project.add_maintainer(user)
project.add_maintainer(user2)
+ project.add_guest(guest)
end
before do
@@ -357,6 +360,61 @@ RSpec.describe 'New/edit issue', :js do
end
end
+ describe 'new issue from related issue' do
+ it 'does not offer to link the new issue to any other issues if the URL parameter is absent' do
+ visit new_project_issue_path(project)
+ expect(page).not_to have_selector '#add_related_issue'
+ expect(page).not_to have_text "Relate to"
+ end
+
+ context 'guest' do
+ let(:current_user) { guest }
+
+ it 'does not offer to link the new issue to an issue that the user does not have access to' do
+ visit new_project_issue_path(project, { add_related_issue: confidential_issue.iid })
+ expect(page).not_to have_selector '#add_related_issue'
+ expect(page).not_to have_text "Relate to"
+ end
+ end
+
+ it 'links the new issue and the issue of origin' do
+ visit new_project_issue_path(project, { add_related_issue: issue.iid })
+ expect(page).to have_selector '#add_related_issue'
+ expect(page).to have_text "Relate to issue \##{issue.iid}"
+ expect(page).to have_text 'Adds this issue as related to the issue it was created from'
+ fill_in 'issue_title', with: 'title'
+ click_button 'Create issue'
+ page.within '#related-issues' do
+ expect(page).to have_text "\##{issue.iid}"
+ end
+ end
+
+ it 'links the new incident and the incident of origin' do
+ incident = create(:incident, project: project)
+ visit new_project_issue_path(project, { add_related_issue: incident.iid })
+ expect(page).to have_selector '#add_related_issue'
+ expect(page).to have_text "Relate to incident \##{incident.iid}"
+ expect(page).to have_text 'Adds this incident as related to the incident it was created from'
+ fill_in 'issue_title', with: 'title'
+ click_button 'Create issue'
+ page.within '#related-issues' do
+ expect(page).to have_text "\##{incident.iid}"
+ end
+ end
+
+ it 'does not link the new issue to any other issues if the checkbox is not checked' do
+ visit new_project_issue_path(project, { add_related_issue: issue.iid })
+ expect(page).to have_selector '#add_related_issue'
+ expect(page).to have_text "Relate to issue \##{issue.iid}"
+ uncheck "Relate to issue \##{issue.iid}"
+ fill_in 'issue_title', with: 'title'
+ click_button 'Create issue'
+ page.within '#related-issues' do
+ expect(page).not_to have_text "\##{issue.iid}"
+ end
+ end
+ end
+
describe 'edit issue' do
before do
visit edit_project_issue_path(project, issue)
diff --git a/spec/features/issues/issue_header_spec.rb b/spec/features/issues/issue_header_spec.rb
index 3e27ce81860..165015013dd 100644
--- a/spec/features/issues/issue_header_spec.rb
+++ b/spec/features/issues/issue_header_spec.rb
@@ -25,8 +25,8 @@ RSpec.describe 'issue header', :js do
click_button 'Issue actions'
end
- it 'shows the "New issue", "Report abuse", and "Delete issue" items', :aggregate_failures do
- expect(page).to have_link 'New issue'
+ it 'shows the "New related issue", "Report abuse", and "Delete issue" items', :aggregate_failures do
+ expect(page).to have_link 'New related issue'
expect(page).to have_link 'Report abuse'
expect(page).to have_button 'Delete issue'
expect(page).not_to have_link 'Submit as spam'
@@ -114,8 +114,8 @@ RSpec.describe 'issue header', :js do
click_button 'Issue actions'
end
- it 'only shows the "New issue" and "Report abuse" items', :aggregate_failures do
- expect(page).to have_link 'New issue'
+ it 'only shows the "New related issue" and "Report abuse" items', :aggregate_failures do
+ expect(page).to have_link 'New related issue'
expect(page).to have_link 'Report abuse'
expect(page).not_to have_link 'Submit as spam'
expect(page).not_to have_button 'Delete issue'
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
index 31bf7649470..eca698bb2f4 100644
--- a/spec/features/issues/user_views_issue_spec.rb
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe "User views issue" do
it 'shows the merge request and issue actions', :js, :aggregate_failures do
click_button 'Issue actions'
- expect(page).to have_link('New issue', href: new_project_issue_path(project, { issue: { description: "Related to \##{issue.iid}.\n\n" } }))
+ expect(page).to have_link('New related issue', href: new_project_issue_path(project, { add_related_issue: issue.iid }))
expect(page).to have_button('Create merge request')
expect(page).to have_button('Close issue')
end
diff --git a/spec/features/markdown/sandboxed_mermaid_spec.rb b/spec/features/markdown/sandboxed_mermaid_spec.rb
index f118fb3db66..05fe83b3107 100644
--- a/spec/features/markdown/sandboxed_mermaid_spec.rb
+++ b/spec/features/markdown/sandboxed_mermaid_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'Sandboxed Mermaid rendering', :js do
wait_for_requests
- expected = %(