diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-15 00:10:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-15 00:10:22 +0300 |
commit | 5b829393a732143e31e2f9a62b6ca2cfb7ebb147 (patch) | |
tree | 85260ce9fd49ea62df0cea5b750f8c0db1b5a082 | |
parent | f69bc1dab50e86440bb4ffdc507ca5efd94bf459 (diff) |
Add latest changes from gitlab-org/gitlab@master
25 files changed, 219 insertions, 31 deletions
diff --git a/app/assets/javascripts/behaviors/shortcuts/keybindings.js b/app/assets/javascripts/behaviors/shortcuts/keybindings.js index c63dba05f10..005ef103ded 100644 --- a/app/assets/javascripts/behaviors/shortcuts/keybindings.js +++ b/app/assets/javascripts/behaviors/shortcuts/keybindings.js @@ -105,6 +105,12 @@ export const TOGGLE_PERFORMANCE_BAR = { defaultKeys: ['p b'], // eslint-disable-line @gitlab/require-i18n-strings }; +export const HIDE_APPEARING_CONTENT = { + id: 'globalShortcuts.hideAppearingContent', + description: __('Hide tooltips or popovers'), + defaultKeys: ['esc'], +}; + export const TOGGLE_CANARY = { id: 'globalShortcuts.toggleCanary', description: __('Toggle GitLab Next'), @@ -492,6 +498,7 @@ export const GLOBAL_SHORTCUTS_GROUP = { GO_TO_YOUR_MERGE_REQUESTS, GO_TO_YOUR_TODO_LIST, TOGGLE_PERFORMANCE_BAR, + HIDE_APPEARING_CONTENT, ], }; diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js index 03cba78cf31..ac2a4184176 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js @@ -12,6 +12,7 @@ import { START_SEARCH, FOCUS_FILTER_BAR, TOGGLE_PERFORMANCE_BAR, + HIDE_APPEARING_CONTENT, TOGGLE_CANARY, TOGGLE_MARKDOWN_PREVIEW, GO_TO_YOUR_TODO_LIST, @@ -78,6 +79,7 @@ export default class Shortcuts { Mousetrap.bind(keysFor(START_SEARCH), Shortcuts.focusSearch); Mousetrap.bind(keysFor(FOCUS_FILTER_BAR), this.focusFilter.bind(this)); Mousetrap.bind(keysFor(TOGGLE_PERFORMANCE_BAR), Shortcuts.onTogglePerfBar); + Mousetrap.bind(keysFor(HIDE_APPEARING_CONTENT), Shortcuts.hideAppearingContent); Mousetrap.bind(keysFor(TOGGLE_CANARY), Shortcuts.onToggleCanary); const findFileURL = document.body.dataset.findFile; @@ -202,6 +204,18 @@ export default class Shortcuts { } } + static hideAppearingContent(e) { + const elements = document.querySelectorAll('.tooltip, .popover'); + + elements.forEach((element) => { + element.style.display = 'none'; + }); + + if (e.preventDefault) { + e.preventDefault(); + } + } + /** * Initializes markdown editor shortcuts on the provided `<textarea>` element * diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue index d900cfcdf2b..c16c29f3222 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue @@ -152,6 +152,9 @@ export default { update(data) { return data.project?.ciTemplate?.content || ''; }, + result({ data }) { + this.updateCiConfig(data.project?.ciTemplate?.content || ''); + }, error() { this.reportFailure(LOAD_FAILURE_UNKNOWN); }, @@ -170,9 +173,6 @@ export default { isEmpty() { return this.currentCiFileContent === ''; }, - templateOrCurrentContent() { - return this.isNewCiConfigFile ? this.starterTemplate : this.currentCiFileContent; - }, }, i18n: { tabEdit: s__('Pipelines|Edit'), @@ -280,7 +280,7 @@ export default { /> <pipeline-editor-home :ci-config-data="ciConfigData" - :ci-file-content="templateOrCurrentContent" + :ci-file-content="currentCiFileContent" :is-new-ci-config-file="isNewCiConfigFile" @commit="updateOnCommit" @resetContent="resetContent" diff --git a/app/finders/pending_todos_finder.rb b/app/finders/pending_todos_finder.rb index d79a2340379..509370b49a8 100644 --- a/app/finders/pending_todos_finder.rb +++ b/app/finders/pending_todos_finder.rb @@ -26,6 +26,7 @@ class PendingTodosFinder todos = by_project(todos) todos = by_target_id(todos) todos = by_target_type(todos) + todos = by_discussion(todos) by_commit_id(todos) end @@ -60,4 +61,12 @@ class PendingTodosFinder todos end end + + def by_discussion(todos) + if (discussion = params[:discussion]) + todos.for_note(discussion.notes) + else + todos + end + end end diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index 3abed7289d5..837d91ef765 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -40,3 +40,5 @@ module Types end end end + +Types::Ci::RunnerType.prepend_mod_with('Types::Ci::RunnerType') diff --git a/app/models/todo.rb b/app/models/todo.rb index 23685fb68e0..94a99603848 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -61,6 +61,7 @@ class Todo < ApplicationRecord scope :for_author, -> (author) { where(author: author) } scope :for_user, -> (user) { where(user: user) } scope :for_project, -> (projects) { where(project: projects) } + scope :for_note, -> (notes) { where(note: notes) } scope :for_undeleted_projects, -> { joins(:project).merge(Project.without_deleted) } scope :for_group, -> (group) { where(group: group) } scope :for_type, -> (type) { where(target_type: type) } diff --git a/app/services/discussions/resolve_service.rb b/app/services/discussions/resolve_service.rb index 3b733023eae..baf14aa8a03 100644 --- a/app/services/discussions/resolve_service.rb +++ b/app/services/discussions/resolve_service.rb @@ -47,9 +47,16 @@ module Discussions MergeRequests::ResolvedDiscussionNotificationService.new(project: project, current_user: current_user).execute(merge_request) end + resolve_user_todos_for(discussion) SystemNoteService.discussion_continued_in_issue(discussion, project, current_user, follow_up_issue) if follow_up_issue end + def resolve_user_todos_for(discussion) + return unless discussion.for_design? + + TodoService.new.resolve_todos_for_target(discussion, current_user) + end + def first_discussion @first_discussion ||= discussions.first end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index fc6543a8efc..71bb813f384 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -316,6 +316,8 @@ class TodoService attributes.merge!(target_id: nil, commit_id: target.id) elsif target.is_a?(Issue) attributes[:issue_type] = target.issue_type + elsif target.is_a?(Discussion) + attributes.merge!(target_type: nil, target_id: nil, discussion: target) end attributes diff --git a/app/views/admin/application_settings/appearances/preview_sign_in.html.haml b/app/views/admin/application_settings/appearances/preview_sign_in.html.haml index a317611862c..77c37abbeef 100644 --- a/app/views/admin/application_settings/appearances/preview_sign_in.html.haml +++ b/app/views/admin/application_settings/appearances/preview_sign_in.html.haml @@ -8,5 +8,5 @@ = label_tag :password = password_field_tag :password, nil, class: "form-control gl-form-input bottom", title: _('This field is required.') .form-group - = button_tag _("Sign in"), class: "btn gl-button btn-confirm" + = button_tag _("Sign in"), class: "btn gl-button btn-confirm", type: "button" diff --git a/doc/administration/consul.md b/doc/administration/consul.md index c7fe076dcce..c88047c4c61 100644 --- a/doc/administration/consul.md +++ b/doc/administration/consul.md @@ -15,11 +15,17 @@ turn communicate with the servers. GitLab Premium includes a bundled version of [Consul](https://www.consul.io/) a service networking solution that you can manage by using `/etc/gitlab/gitlab.rb`. +## Prerequisites + +Before configuring Consul: + +1. Review the [reference architecture](reference_architectures/index.md#available-reference-architectures) + documentation to determine the number of Consul server nodes you should have. +1. If necessary, ensure the [appropriate ports are open](https://docs.gitlab.com/omnibus/package-information/defaults.html#ports) in your firewall. + ## Configure the Consul nodes -After you review the [reference architecture](reference_architectures/index.md#available-reference-architectures) -documentation to determine the number of Consul server nodes you should have, -on _each_ Consul server node: +On _each_ Consul server node: 1. Follow the instructions to [install](https://about.gitlab.com/install/) GitLab by choosing your preferred platform, but do not supply the diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index f47b060e73b..5420dfb0b7b 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -7544,6 +7544,8 @@ Represents the total number of issues and their weights for a particular day. | <a id="cirunneripaddress"></a>`ipAddress` | [`String!`](#string) | IP address of the runner. | | <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. | | <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. | +| <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). | +| <a id="cirunnerpublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). | | <a id="cirunnerrevision"></a>`revision` | [`String!`](#string) | Revision of the runner. | | <a id="cirunnerrununtagged"></a>`runUntagged` | [`Boolean!`](#boolean) | Indicates the runner is able to run untagged jobs. | | <a id="cirunnerrunnertype"></a>`runnerType` | [`CiRunnerType!`](#cirunnertype) | Type of the runner. | diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 8d3b01f3b79..949b7ff4e9d 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -289,8 +289,8 @@ To add a different cluster for each environment: 1. Navigate to your project's **Operations > Kubernetes**. 1. Create the Kubernetes clusters with their respective environment scope, as described from the table above. -1. After creating the clusters, navigate to each cluster and install - Ingress. Wait for the Ingress IP address to be assigned. +1. After creating the clusters, navigate to each cluster and [install + Ingress](quick_start_guide.md#install-ingress). Wait for the Ingress IP address to be assigned. 1. Make sure you've [configured your DNS](#auto-devops-base-domain) with the specified Auto DevOps domains. 1. Navigate to each cluster's page, through **Operations > Kubernetes**, diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md index 7d5ee10f604..6d42a9a4274 100644 --- a/doc/topics/autodevops/quick_start_guide.md +++ b/doc/topics/autodevops/quick_start_guide.md @@ -106,7 +106,8 @@ status on your [GCP dashboard](https://console.cloud.google.com/kubernetes). After your cluster is running, you must install NGINX Ingress Controller as a load balancer, to route traffic from the internet to your application. Because you've created a Google GKE cluster in this guide, you can install NGINX Ingress Controller -with Google Cloud Shell: +through the GitLab [Cluster management project template](../../user/clusters/management_project_template.md), +or manually with Google Cloud Shell: 1. Go to your cluster's details page, and click the **Advanced Settings** tab. 1. Click the link to Google Kubernetes Engine to visit the cluster on Google Cloud Console. @@ -114,21 +115,28 @@ with Google Cloud Shell: 1. After the Cloud Shell starts, run these commands to install NGINX Ingress Controller: ```shell - helm repo add nginx-stable https://helm.nginx.com/stable + kubectl create ns gitlab-managed-apps + helm repo add stable https://charts.helm.sh/stable helm repo update - helm install nginx-ingress nginx-stable/nginx-ingress + helm install ingress stable/nginx-ingress -n gitlab-managed-apps # Check that the ingress controller is installed successfully - kubectl get service nginx-ingress-nginx-ingress + kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps ``` -1. A few minutes after you install NGINX, the load balancer obtains an IP address, and you can - get the external IP address with this command: +## Configure your base domain + +Follow these steps to configure the Base Domain where your apps will be accessible. +1. A few minutes after you install NGINX, the load balancer obtains an IP address, and you can + get the external IP address with the following command: + ```shell - kubectl get service nginx-ingress-nginx-ingress -ojson | jq -r '.status.loadBalancer.ingress[].ip' + kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -ojson | jq -r '.status.loadBalancer.ingress[].ip' ``` + Replace `gitlab-managed-apps` if you have overwritten your namespace. + Copy this IP address, as you need it in the next step. 1. Go back to the cluster page on GitLab, and go to the **Details** tab. diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md index ccffb5913f5..7e59ecb4916 100644 --- a/doc/topics/autodevops/requirements.md +++ b/doc/topics/autodevops/requirements.md @@ -30,8 +30,8 @@ To make full use of Auto DevOps with Kubernetes, you need: deployments, any Ingress controller should work, but as of GitLab 14.0, [canary deployments](../../user/project/canary_deployments.md) require NGINX Ingress. You can deploy the NGINX Ingress controller to your - Kubernetes cluster by installing the - [`ingress-nginx`](https://github.com/kubernetes/ingress-nginx/tree/master/charts/ingress-nginx) + Kubernetes cluster either through the GitLab [Cluster management project template](../../user/clusters/management_project_template.md) + or manually by using the [`ingress-nginx`](https://github.com/kubernetes/ingress-nginx/tree/master/charts/ingress-nginx) Helm chart. NOTE: diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index f712dcace73..5f047de5938 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -1029,7 +1029,13 @@ at least 2 people from the The one-click installation method was deprecated in GitLab 13.9 and removed in [GitLab 14.0](https://gitlab.com/groups/gitlab-org/-/epics/4280). The removal does not break nor uninstall any apps you have installed, it only removes the "Applications" tab from the cluster page. -Follow the process to [take ownership of your GitLab Managed Apps](#take-ownership-of-your-gitlab-managed-apps). +The new recommended way to manage cluster applications is to use the [cluster management project template](management_project_template.md). + +- If you want to migrate your GitLab managed apps management to this template, read + [migrating from GitLab managed apps to project template](migrating_from_gma_to_project_template.md). +- If you don't want to use the template, you can also manually manage your applications. + For that, follow the process to + [take ownership of your GitLab Managed Apps](#take-ownership-of-your-gitlab-managed-apps). If you are not yet on GitLab 14.0 or later, you can refer to [an older version of this document](https://docs.gitlab.com/13.12/ee/user/clusters/applications.html#install-with-one-click-deprecated). diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md index 8b67c7e03ce..8c42a27b471 100644 --- a/doc/user/project/issues/design_management.md +++ b/doc/user/project/issues/design_management.md @@ -219,6 +219,9 @@ There are two ways to resolve/unresolve a Design thread: ![Resolve checkbox](img/resolve_design-discussion_checkbox_v13_1.png) +Resolving a discussion thread will also mark any pending to-do related to notes +inside the thread as done. This is applicable only for to-dos owned by the user triggering the action. + Note that your resolved comment pins disappear from the Design to free up space for new discussions. However, if you need to revisit or find a resolved discussion, all of your resolved threads are available in the **Resolved Comment** area at the bottom of the right sidebar. diff --git a/doc/user/todos.md b/doc/user/todos.md index 695532abf9f..4227f46dfa8 100644 --- a/doc/user/todos.md +++ b/doc/user/todos.md @@ -112,6 +112,7 @@ Actions that dismiss to-do items include: - Changing the milestone - Adding/removing a label - Commenting on the issue +- Resolving a [design discussion thread](project/issues/design_management.md#resolve-design-threads) Your To-Do List is personal, and items are only marked as done if you take action. If you close the issue or merge request, your to-do item is marked as diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb index 2d58bb88816..d05ced00a6b 100644 --- a/lib/gitlab/highlight.rb +++ b/lib/gitlab/highlight.rb @@ -70,6 +70,8 @@ module Gitlab end def highlight_rich(text, continue: true) + add_highlight_attempt_metric + tag = lexer.tag tokens = lexer.lex(text, continue: continue) Timeout.timeout(timeout_time) { @formatter.format(tokens, context.merge(tag: tag)).html_safe } @@ -90,12 +92,25 @@ module Gitlab Gitlab::DependencyLinker.link(blob_name, text, highlighted_text) end + def add_highlight_attempt_metric + return unless Feature.enabled?(:track_highlight_timeouts) + + highlighting_attempt.increment(source: (@language || "undefined")) + end + def add_highlight_timeout_metric return unless Feature.enabled?(:track_highlight_timeouts) highlight_timeout.increment(source: Gitlab::Runtime.sidekiq? ? "background" : "foreground") end + def highlighting_attempt + @highlight_attempt ||= Gitlab::Metrics.counter( + :file_highlighting_attempt, + 'Counts the times highlighting has been attempted on a file' + ) + end + def highlight_timeout @highlight_timeout ||= Gitlab::Metrics.counter( :highlight_timeout, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 63be6e28249..b4c82b69b43 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16281,6 +16281,9 @@ msgstr "" msgid "Hide shared projects" msgstr "" +msgid "Hide tooltips or popovers" +msgstr "" + msgid "Hide value" msgid_plural "Hide values" msgstr[0] "" diff --git a/spec/finders/pending_todos_finder_spec.rb b/spec/finders/pending_todos_finder_spec.rb index b17915f0d59..f317d8b1633 100644 --- a/spec/finders/pending_todos_finder_spec.rb +++ b/spec/finders/pending_todos_finder_spec.rb @@ -3,8 +3,11 @@ require 'spec_helper' RSpec.describe PendingTodosFinder do - let(:user) { create(:user) } - let(:user2) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:user2) { create(:user) } + let_it_be(:issue) { create(:issue) } + let_it_be(:note) { create(:note) } + let(:users) { [user, user2] } describe '#execute' do @@ -30,20 +33,16 @@ RSpec.describe PendingTodosFinder do end it 'supports retrieving of todos for a specific todo target' do - issue = create(:issue) - note = create(:note) todo = create(:todo, :pending, user: user, target: issue) create(:todo, :pending, user: user, target: note) - todos = described_class.new(users, target_id: issue.id).execute + todos = described_class.new(users, target_id: issue.id, target_type: 'Issue').execute expect(todos).to eq([todo]) end it 'supports retrieving of todos for a specific target type' do - issue = create(:issue) - note = create(:note) todo = create(:todo, :pending, user: user, target: issue) create(:todo, :pending, user: user, target: note) @@ -61,5 +60,20 @@ RSpec.describe PendingTodosFinder do expect(todos).to eq([todo]) end + + it 'supports retrieving of todos for specific discussion' do + first_discussion_note = create(:discussion_note_on_issue, noteable: issue, project: issue.project) + note_2 = create(:note, discussion_id: first_discussion_note.discussion_id) + note_3 = create(:note, discussion_id: first_discussion_note.discussion_id) + todo1 = create(:todo, :pending, target: issue, note: note_2, user: note_2.author) + todo2 = create(:todo, :pending, target: issue, note: note_3, user: note_3.author) + create(:todo, :pending, note: note, user: user) + discussion = Discussion.lazy_find(first_discussion_note.discussion_id) + users = [note_2.author, note_3.author, user] + + todos = described_class.new(users, discussion: discussion).execute + + expect(todos).to contain_exactly(todo1, todo2) + end end end diff --git a/spec/frontend/behaviors/shortcuts/keybindings_spec.js b/spec/frontend/behaviors/shortcuts/keybindings_spec.js index 53ce06e78c6..3ad44a16ae1 100644 --- a/spec/frontend/behaviors/shortcuts/keybindings_spec.js +++ b/spec/frontend/behaviors/shortcuts/keybindings_spec.js @@ -5,6 +5,7 @@ import { getCustomizations, keybindingGroups, TOGGLE_PERFORMANCE_BAR, + HIDE_APPEARING_CONTENT, LOCAL_STORAGE_KEY, WEB_IDE_COMMIT, } from '~/behaviors/shortcuts/keybindings'; @@ -95,4 +96,14 @@ describe('~/behaviors/shortcuts/keybindings', () => { expect(keysFor(TOGGLE_PERFORMANCE_BAR)).toEqual(['p b']); }); }); + + describe('when tooltips or popovers are visible', () => { + beforeEach(() => { + setupCustomizations(); + }); + + it('returns the default keybinding for the command', () => { + expect(keysFor(HIDE_APPEARING_CONTENT)).toEqual(['esc']); + }); + }); }); diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb index dfe4a30c5b7..f27216f4d39 100644 --- a/spec/graphql/types/ci/runner_type_spec.rb +++ b/spec/graphql/types/ci/runner_type_spec.rb @@ -2,15 +2,17 @@ require 'spec_helper' -RSpec.describe Types::Ci::RunnerType do +RSpec.describe GitlabSchema.types['CiRunner'] do specify { expect(described_class.graphql_name).to eq('CiRunner') } + specify { expect(described_class).to require_graphql_authorizations(:read_runner) } + it 'contains attributes related to a runner' do expected_fields = %w[ id description contacted_at maximum_timeout access_level active status version short_sha revision locked run_untagged ip_address runner_type tag_list ] - expect(described_class).to have_graphql_fields(*expected_fields) + expect(described_class).to include_graphql_fields(*expected_fields) end end diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index 74388273c8d..1f06019c929 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -143,9 +143,21 @@ RSpec.describe Gitlab::Highlight do end describe 'highlight timeouts' do - context 'when there is a timeout error while highlighting' do - let(:result) { described_class.highlight(file_name, content) } + let(:result) { described_class.highlight(file_name, content, language: "ruby") } + + context 'when there is an attempt' do + it "increments the attempt counter with a defined language" do + expect { result }.to change { highlight_attempt_total("ruby") } + end + + it "increments the attempt counter with an undefined language" do + expect do + described_class.highlight(file_name, content) + end.to change { highlight_attempt_total("undefined") } + end + end + context 'when there is a timeout error while highlighting' do before do allow(Timeout).to receive(:timeout).twice.and_raise(Timeout::Error) # This is done twice because it's rescued first and then @@ -177,6 +189,12 @@ RSpec.describe Gitlab::Highlight do .get(source: source) end + def highlight_attempt_total(source) + Gitlab::Metrics + .counter(:file_highlighting_attempt, 'Counts the times highlighting has been attempted on a file') + .get(source: source) + end + def over_highlight_size_limit(source) Gitlab::Metrics .counter(:over_highlight_size_limit, diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index caa0a886abf..651e2cf273f 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -376,6 +376,18 @@ RSpec.describe Todo do end end + describe '.for_note' do + it 'returns todos that belongs to notes' do + note_1 = create(:note, noteable: issue, project: issue.project) + note_2 = create(:note, noteable: issue, project: issue.project) + todo_1 = create(:todo, note: note_1) + todo_2 = create(:todo, note: note_2) + create(:todo, note: create(:note)) + + expect(described_class.for_note([note_1, note_2])).to contain_exactly(todo_1, todo_2) + end + end + describe '.group_by_user_id_and_state' do let_it_be(:user1) { create(:user) } let_it_be(:user2) { create(:user) } diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb index 2e30455eb0a..24de1d90526 100644 --- a/spec/services/discussions/resolve_service_spec.rb +++ b/spec/services/discussions/resolve_service_spec.rb @@ -121,5 +121,50 @@ RSpec.describe Discussions::ResolveService do service.execute end end + + context 'when resolving a discussion' do + def resolve_discussion(discussion, user) + described_class.new(project, user, one_or_more_discussions: discussion).execute + end + + context 'in a design' do + let_it_be(:design) { create(:design, :with_file, issue: create(:issue, project: project)) } + let_it_be(:user_1) { create(:user) } + let_it_be(:user_2) { create(:user) } + let_it_be(:discussion_1) { create(:diff_note_on_design, noteable: design, project: project, author: user_1).to_discussion } + let_it_be(:discussion_2) { create(:diff_note_on_design, noteable: design, project: project, author: user_2).to_discussion } + + before do + project.add_developer(user_1) + project.add_developer(user_2) + end + + context 'when user resolving discussion has open todos' do + let!(:user_1_todo_for_discussion_1) { create(:todo, :pending, user: user_1, target: design, note: discussion_1.notes.first, project: project) } + let!(:user_1_todo_2_for_discussion_1) { create(:todo, :pending, user: user_1, target: design, note: discussion_1.notes.first, project: project) } + let!(:user_1_todo_for_discussion_2) { create(:todo, :pending, user: user_1, target: design, note: discussion_2.notes.first, project: project) } + let!(:user_2_todo_for_discussion_1) { create(:todo, :pending, user: user_2, target: design, note: discussion_1.notes.first, project: project) } + + it 'marks user todos for given discussion as done' do + resolve_discussion(discussion_1, user_1) + + expect(user_1_todo_for_discussion_1.reload).to be_done + expect(user_1_todo_2_for_discussion_1.reload).to be_done + expect(user_1_todo_for_discussion_2.reload).to be_pending + expect(user_2_todo_for_discussion_1.reload).to be_pending + end + end + end + + context 'in a merge request' do + let!(:user_todo_for_discussion) { create(:todo, :pending, user: user, target: merge_request, note: discussion.notes.first, project: project) } + + it 'does not mark user todo as done' do + resolve_discussion(discussion, user) + + expect(user_todo_for_discussion).to be_pending + end + end + end end end |