- To preserve performance only {{ visible }} of {{ total }} files are - displayed. -
-@@ -748,20 +767,22 @@ export default {
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql
index d85794f7245..99e6f4e9beb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.fragment.graphql
@@ -1,9 +1,11 @@
fragment ReadyToMerge on Project {
+ __typename
id
onlyAllowMergeIfPipelineSucceeds
mergeRequestsFfOnlyEnabled
squashReadOnly
mergeRequest(iid: $iid) {
+ __typename
id
autoMergeEnabled
shouldRemoveSourceBranch
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index 22243b606ed..7a4cc61af79 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -181,7 +181,8 @@ module MarkupHelper
wiki: wiki,
repository: wiki.repository,
page_slug: wiki_page.slug,
- issuable_reference_expansion_enabled: true
+ issuable_reference_expansion_enabled: true,
+ requested_path: wiki_page.path
).merge(render_wiki_content_context_container(wiki))
end
@@ -263,7 +264,7 @@ module MarkupHelper
end
def asciidoc_unsafe(text, context = {})
- context.merge!(
+ context.reverse_merge!(
commit: @commit,
ref: @ref,
requested_path: @path
diff --git a/app/models/user.rb b/app/models/user.rb
index 4c375fe5230..2905db7ce6c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -277,7 +277,19 @@ class User < ApplicationRecord
after_update :username_changed_hook, if: :saved_change_to_username?
after_destroy :post_destroy_hook
after_destroy :remove_key_cache
- after_create :add_primary_email_to_emails!, if: :confirmed?
+ after_save if: -> { saved_change_to_email? && confirmed? } do
+ email_to_confirm = self.emails.find_by(email: self.email)
+
+ if email_to_confirm.present?
+ if skip_confirmation_period_expiry_check
+ email_to_confirm.force_confirm
+ else
+ email_to_confirm.confirm
+ end
+ else
+ add_primary_email_to_emails!
+ end
+ end
after_commit(on: :update) do
if previous_changes.key?('email')
# Add the old primary email to Emails if not added already - this should be removed
@@ -2013,29 +2025,6 @@ class User < ApplicationRecord
ci_job_token_scope.present?
end
- # override from Devise::Models::Confirmable
- #
- # Add the primary email to user.emails (or confirm it if it was already
- # present) when the primary email is confirmed.
- def confirm(args = {})
- saved = super(args)
- return false unless saved
-
- email_to_confirm = self.emails.find_by(email: self.email)
-
- if email_to_confirm.present?
- if skip_confirmation_period_expiry_check
- email_to_confirm.force_confirm(args)
- else
- email_to_confirm.confirm(args)
- end
- else
- add_primary_email_to_emails!
- end
-
- saved
- end
-
def user_project
strong_memoize(:user_project) do
personal_projects.find_by(path: username, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml
index f6d05959d2e..6060d697f52 100644
--- a/app/views/groups/registry/repositories/index.html.haml
+++ b/app/views/groups/registry/repositories/index.html.haml
@@ -12,6 +12,7 @@
"registry_host_url_with_port" => escape_once(registry_config.host_port),
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
+ "container_registry_importing_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'),
"is_admin": current_user&.admin.to_s,
is_group_page: "true",
"group_path": @group.full_path,
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 67f24383b17..5f8b21b2646 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -40,12 +40,10 @@
= _('Time based: Yes')
= form_tag profile_two_factor_auth_path, method: :post do |f|
- if @error
- .gl-alert.gl-alert-danger.gl-mb-5
- .gl-alert-container
- .gl-alert-content
- %p.gl-alert-body.gl-md-0
- = @error[:message]
- = link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer'
+ = render 'shared/global_alert', title: @error[:message], variant: :danger, dismissible: false do
+ .gl-alert-body
+ = link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer'
+
.form-group
= label_tag :pin_code, _('Pin code'), class: "label-bold"
= text_field_tag :pin_code, nil, class: "form-control gl-form-input", required: true, data: { qa_selector: 'pin_code_field' }
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index 3efe1fd2e82..aab5e9fca98 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -15,6 +15,7 @@
"expiration_policy_help_page_path" => help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
+ "container_registry_importing_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'),
"project_path": @project.full_path,
"gid_prefix": container_repository_gid_prefix,
"is_admin": current_user&.admin.to_s,
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 08e37308936..43dd2c3e759 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -570,6 +570,15 @@
:weight: 1
:idempotent:
:tags: []
+- :name: cronjob:quality_test_data_cleanup
+ :worker_name: Quality::TestDataCleanupWorker
+ :feature_category: :quality_management
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:releases_manage_evidence
:worker_name: Releases::ManageEvidenceWorker
:feature_category: :release_evidence
diff --git a/app/workers/quality/test_data_cleanup_worker.rb b/app/workers/quality/test_data_cleanup_worker.rb
new file mode 100644
index 00000000000..68b36cacbbf
--- /dev/null
+++ b/app/workers/quality/test_data_cleanup_worker.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Quality
+ class TestDataCleanupWorker
+ include ApplicationWorker
+
+ data_consistency :always
+ feature_category :quality_management
+ urgency :low
+
+ include CronjobQueue
+ idempotent!
+
+ KEEP_RECENT_DATA_DAY = 3
+ GROUP_PATH_PATTERN = 'test-group-fulfillment'
+ GROUP_OWNER_EMAIL_PATTERN = %w(test-user- gitlab-qa-user qa-user-).freeze
+
+ # Remove test groups generated in E2E tests on gstg
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform
+ return unless Gitlab.staging?
+
+ Group.where('path like ?', "#{GROUP_PATH_PATTERN}%").where('created_at < ?', KEEP_RECENT_DATA_DAY.days.ago).each do |group|
+ next unless GROUP_OWNER_EMAIL_PATTERN.any? { |pattern| group.owners.first.email.include?(pattern) }
+
+ with_context(namespace: group, user: group.owners.first) do
+ Groups::DestroyService.new(group, group.owners.first).execute
+ end
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 53ab8061ffa..22e13270179 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -1049,7 +1049,10 @@ For example:
## Mattermost Logs
-For Omnibus GitLab installations, Mattermost logs are in `/var/log/gitlab/mattermost/mattermost.log`.
+For Omnibus GitLab installations, Mattermost logs are in these locations:
+
+- `/var/log/gitlab/mattermost/mattermost.log`
+- `/var/log/gitlab/mattermost/current`
## Workhorse Logs
diff --git a/doc/api/group_wikis.md b/doc/api/group_wikis.md
index a4de537004d..a78f989a755 100644
--- a/doc/api/group_wikis.md
+++ b/doc/api/group_wikis.md
@@ -10,6 +10,7 @@ type: reference, api
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212199) in GitLab 13.5.
> - The `encoding` field was [added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81150) in GitLab 14.9.
> - The `render_html` attribute was [added](https://gitlab.com/gitlab-org/gitlab/-/issues/336792) in GitLab 14.9.
+> - The `version` attribute was [added](https://gitlab.com/gitlab-org/gitlab/-/issues/336792) in GitLab 14.9.
The [group wikis](../user/project/wiki/group.md) API is available only in APIv4.
An API for [project wikis](wikis.md) is also available.
@@ -71,6 +72,7 @@ GET /groups/:id/wikis/:slug
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `slug` | string | yes | URL-encoded slug (a unique string) of the wiki page, such as `dir%2Fpage_name` |
| `render_html` | boolean | no | Return the rendered HTML of the wiki page |
+| `version` | string | no | Wiki page version sha |
```shell
curl --header "PRIVATE-TOKEN: #{wiki_page.content} Test content
`Improved project description in readme file.` |
| `%{url}` | Full URL to the merge request. | `https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1` |
-| `%{approved_by}` | Line-separated list of the merge request approvers. This value is not updated until the first page refresh after an approval. | `Approved-by: Sidney Jones
`Approved-by: Zhang Wei
`Approved-by: Zhang Wei
`Co-authored-by: Blake Smith
`This commit implements feature`
`Changelog:added`
`* Bug fixed`
`* Documentation improved`
`This commit introduced better docs.`|
@@ -92,6 +92,10 @@ Commit message templates support these variables:
Any line containing only an empty variable is removed. If the line to be removed is both
preceded and followed by an empty line, the preceding empty line is also removed.
+After you edit a commit message on an open merge request, GitLab will
+not automatically update the commit message again.
+To restore the commit message to the project template, reload the page.
+
## Related topics
- [Squash and merge](squash_and_merge.md).
diff --git a/lib/api/entities/wiki_page.rb b/lib/api/entities/wiki_page.rb
index c6dba10e441..43af6a336d2 100644
--- a/lib/api/entities/wiki_page.rb
+++ b/lib/api/entities/wiki_page.rb
@@ -6,7 +6,7 @@ module API
include ::MarkupHelper
expose :content do |wiki_page, options|
- options[:render_html] ? render_wiki_content(wiki_page) : wiki_page.content
+ options[:render_html] ? render_wiki_content(wiki_page, ref: wiki_page.version.id) : wiki_page.content
end
expose :encoding do |wiki_page|
diff --git a/lib/api/helpers/wikis_helpers.rb b/lib/api/helpers/wikis_helpers.rb
index 4a14dc1f40a..a9cd0e2919d 100644
--- a/lib/api/helpers/wikis_helpers.rb
+++ b/lib/api/helpers/wikis_helpers.rb
@@ -13,8 +13,8 @@ module API
raise "Unknown wiki container #{kind}"
end
- def wiki_page
- Wiki.for_container(container, current_user).find_page(params[:slug]) || not_found!('Wiki Page')
+ def wiki_page(version = nil)
+ Wiki.for_container(container, current_user).find_page(params[:slug], version.presence) || not_found!('Wiki Page')
end
def commit_params(attrs)
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index fca690df916..e90d88940a5 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -45,12 +45,13 @@ module API
end
params do
requires :slug, type: String, desc: 'The slug of a wiki page'
+ optional :version, type: String, desc: 'The version hash of a wiki page'
optional :render_html, type: Boolean, default: false, desc: 'Render content to HTML'
end
get ':id/wikis/:slug' do
authorize! :read_wiki, container
- present wiki_page, with: Entities::WikiPage, render_html: params[:render_html]
+ present wiki_page(params[:version]), with: Entities::WikiPage, render_html: params[:render_html]
end
desc 'Create a wiki page' do
diff --git a/lib/gitlab/database/background_migration/batched_job.rb b/lib/gitlab/database/background_migration/batched_job.rb
index fba0eb2fb13..f3160679d64 100644
--- a/lib/gitlab/database/background_migration/batched_job.rb
+++ b/lib/gitlab/database/background_migration/batched_job.rb
@@ -3,6 +3,8 @@
module Gitlab
module Database
module BackgroundMigration
+ SplitAndRetryError = Class.new(StandardError)
+
class BatchedJob < SharedModel
include EachBatch
include FromUnion
@@ -11,6 +13,8 @@ module Gitlab
MAX_ATTEMPTS = 3
STUCK_JOBS_TIMEOUT = 1.hour.freeze
+ TIMEOUT_EXCEPTIONS = [ActiveRecord::StatementTimeout, ActiveRecord::ConnectionTimeoutError,
+ ActiveRecord::AdapterTimeout, ActiveRecord::LockWaitTimeout].freeze
belongs_to :batched_migration, foreign_key: :batched_background_migration_id
has_many :batched_job_transition_logs, foreign_key: :batched_background_migration_job_id
@@ -51,6 +55,16 @@ module Gitlab
job.metrics = {}
end
+ after_transition any => :failed do |job, transition|
+ error_hash = transition.args.find { |arg| arg[:error].present? }
+
+ exception = error_hash&.fetch(:error)
+
+ job.split_and_retry! if job.can_split?(exception)
+ rescue SplitAndRetryError => error
+ Gitlab::AppLogger.error(message: error.message, batched_job_id: job.id)
+ end
+
after_transition do |job, transition|
error_hash = transition.args.find { |arg| arg[:error].present? }
@@ -79,13 +93,17 @@ module Gitlab
duration.to_f / batched_migration.interval
end
+ def can_split?(exception)
+ attempts >= MAX_ATTEMPTS && TIMEOUT_EXCEPTIONS.include?(exception&.class) && batch_size > sub_batch_size
+ end
+
def split_and_retry!
with_lock do
- raise 'Only failed jobs can be split' unless failed?
+ raise SplitAndRetryError, 'Only failed jobs can be split' unless failed?
new_batch_size = batch_size / 2
- raise 'Job cannot be split further' if new_batch_size < 1
+ raise SplitAndRetryError, 'Job cannot be split further' if new_batch_size < 1
batching_strategy = batched_migration.batch_class.new(connection: self.class.connection)
next_batch_bounds = batching_strategy.next_batch(
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ea9a5c64a05..50f015c9038 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -9723,6 +9723,9 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
+msgstr ""
+
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr ""
@@ -38768,6 +38771,9 @@ msgstr ""
msgid "To personalize your GitLab experience, we'd like to know a bit more about you"
msgstr ""
+msgid "To preserve performance only %{strongStart}%{visible} of %{total}%{strongEnd} files are displayed."
+msgstr ""
+
msgid "To preserve performance only %{strong_open}%{display_size} of %{real_size}%{strong_close} files are displayed."
msgstr ""
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index 117d47b96c8..740a8920cf2 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -385,6 +385,18 @@ module QA
end
end
+ def remove_via_api!
+ super
+
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1, message: "Waiting for #{self.class.name} to be removed") do
+ !exists?
+ rescue InternalServerError
+ # Retry on transient errors that are likely to be due to race conditions between concurrent delete operations
+ # when parts of a resource are stored in multiple tables
+ false
+ end
+ end
+
protected
# Return subset of fields for comparing projects
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 152ae061605..aa264ad3377 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -118,5 +118,14 @@ FactoryBot.define do
create(:crm_settings, group: group, enabled: true)
end
end
+
+ trait :test_group do
+ path { "test-group-fulfillment#{SecureRandom.hex(4)}" }
+ created_at { 4.days.ago }
+
+ after(:create) do |group|
+ group.add_owner(create(:user, email: "test-user-#{SecureRandom.hex(4)}@test.com"))
+ end
+ end
end
end
diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb
index f5af9ba8b7b..7bef2dc9416 100644
--- a/spec/features/groups/container_registry_spec.rb
+++ b/spec/features/groups/container_registry_spec.rb
@@ -97,6 +97,8 @@ RSpec.describe 'Container Registry', :js do
expect(find('.modal .modal-title')).to have_content _('Remove tag')
find('.modal .modal-footer .btn-danger').click
end
+
+ it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['latest']
end
end
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 03f67669061..50f4cce5c23 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -45,8 +45,8 @@ RSpec.describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request)
- page.within('.alert') do
- expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve performance only 3 of 3+ files are displayed.")
+ page.within('.gl-alert') do
+ expect(page).to have_text("Too many changes to show. To preserve performance only 3 of 3+ files are displayed. Plain diff Email patch")
end
end
end
diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb
index 4ebcb69592b..17eb421191f 100644
--- a/spec/features/projects/container_registry_spec.rb
+++ b/spec/features/projects/container_registry_spec.rb
@@ -103,6 +103,8 @@ RSpec.describe 'Container Registry', :js do
find('.modal .modal-footer .btn-danger').click
end
+ it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['1']
+
it('pagination navigate to the second page') do
visit_next_page
diff --git a/spec/frontend/diffs/components/hidden_files_warning_spec.js b/spec/frontend/diffs/components/hidden_files_warning_spec.js
index 3f1f23a40f5..bbd4f5faeec 100644
--- a/spec/frontend/diffs/components/hidden_files_warning_spec.js
+++ b/spec/frontend/diffs/components/hidden_files_warning_spec.js
@@ -1,4 +1,6 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
+import { GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
const propsData = {
@@ -12,7 +14,7 @@ describe('HiddenFilesWarning', () => {
let wrapper;
const createComponent = () => {
- wrapper = shallowMount(HiddenFilesWarning, {
+ wrapper = mount(HiddenFilesWarning, {
propsData,
});
};
@@ -26,22 +28,20 @@ describe('HiddenFilesWarning', () => {
});
it('has a correct plain diff URL', () => {
- const plainDiffLink = wrapper.findAll('a').wrappers.filter((x) => x.text() === 'Plain diff')[0];
+ const plainDiffLink = wrapper.findAllComponents(GlButton).at(0);
expect(plainDiffLink.attributes('href')).toBe(propsData.plainDiffPath);
});
it('has a correct email patch URL', () => {
- const emailPatchLink = wrapper
- .findAll('a')
- .wrappers.filter((x) => x.text() === 'Email patch')[0];
+ const emailPatchLink = wrapper.findAllComponents(GlButton).at(1);
expect(emailPatchLink.attributes('href')).toBe(propsData.emailPatchPath);
});
it('has a correct visible/total files text', () => {
- const filesText = wrapper.find('strong');
-
- expect(filesText.text()).toBe('5 of 10');
+ expect(wrapper.text()).toContain(
+ __('To preserve performance only 5 of 10 files are displayed.'),
+ );
});
});
diff --git a/spec/frontend/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb
index 1eb48c0ce2c..1eae854eca3 100644
--- a/spec/frontend/fixtures/merge_requests.rb
+++ b/spec/frontend/fixtures/merge_requests.rb
@@ -130,6 +130,25 @@ RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type:
expect(response).to be_successful
end
+ describe GraphQL::Query, type: :request do
+ include ApiHelpers
+ include GraphqlHelpers
+
+ context 'merge request in state readyToMerge query' do
+ base_input_path = 'vue_merge_request_widget/queries/states/'
+ base_output_path = 'graphql/merge_requests/states/'
+ query_name = 'ready_to_merge.query.graphql'
+
+ it "#{base_output_path}#{query_name}.json" do
+ query = get_graphql_query_as_string("#{base_input_path}#{query_name}", ee: true)
+
+ post_graphql(query, current_user: user, variables: { projectPath: project.full_path, iid: merge_request.iid.to_s })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+ end
+
private
def render_discussions_json(merge_request)
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 96c9baeb328..9a30fd5f5c3 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -169,12 +169,11 @@ describe('RepoEditor', () => {
expect(findEditor().isVisible()).toBe(true);
});
- it('renders only an edit tab', async () => {
+ it('renders no tabs', async () => {
await createComponent();
const tabs = findTabs();
- expect(tabs).toHaveLength(1);
- expect(tabs.at(0).text()).toBe('Edit');
+ expect(tabs).toHaveLength(0);
});
});
@@ -196,25 +195,48 @@ describe('RepoEditor', () => {
mock.restore();
});
- it('renders an Edit and a Preview Tab', async () => {
- await createComponent({ activeFile });
- const tabs = findTabs();
+ describe('when files is markdown', () => {
+ let layoutSpy;
- expect(tabs).toHaveLength(2);
- expect(tabs.at(0).text()).toBe('Edit');
- expect(tabs.at(1).text()).toBe('Preview Markdown');
+ beforeEach(async () => {
+ await createComponent({ activeFile });
+ layoutSpy = jest.spyOn(wrapper.vm.editor, 'layout');
+ });
+
+ it('renders an Edit and a Preview Tab', () => {
+ const tabs = findTabs();
+
+ expect(tabs).toHaveLength(2);
+ expect(tabs.at(0).text()).toBe('Edit');
+ expect(tabs.at(1).text()).toBe('Preview Markdown');
+ });
+
+ it('renders markdown for tempFile', async () => {
+ findPreviewTab().trigger('click');
+ await waitForPromises();
+ expect(wrapper.find(ContentViewer).html()).toContain(defaultFileProps.content);
+ });
+
+ it('should not trigger layout', async () => {
+ expect(layoutSpy).not.toHaveBeenCalled();
+ });
+
+ describe('when file changes to non-markdown file', () => {
+ beforeEach(async () => {
+ wrapper.setProps({ file: dummyFile.empty });
+ });
+
+ it('should hide tabs', () => {
+ expect(findTabs()).toHaveLength(0);
+ });
+
+ it('should trigger refresh dimensions', async () => {
+ expect(layoutSpy).toHaveBeenCalledTimes(1);
+ });
+ });
});
- it('renders markdown for tempFile', async () => {
- // by default files created in the spec are temp: no need for explicitly sending the param
- await createComponent({ activeFile });
-
- findPreviewTab().trigger('click');
- await waitForPromises();
- expect(wrapper.find(ContentViewer).html()).toContain(defaultFileProps.content);
- });
-
- it('shows no tabs when not in Edit mode', async () => {
+ it('when not in edit mode, shows no tabs', async () => {
await createComponent({
state: {
currentActivityView: leftSidebarViews.review.name,
@@ -405,7 +427,7 @@ describe('RepoEditor', () => {
it.each`
mode | isVisible
- ${'edit'} | ${true}
+ ${'edit'} | ${false}
${'review'} | ${false}
${'commit'} | ${false}
`('tabs in $mode are $isVisible', async ({ mode, isVisible } = {}) => {
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index 3fea08d5512..0be0bf89210 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -51,31 +51,6 @@ describe('common_utils', () => {
});
});
- describe('parseUrl', () => {
- it('returns an anchor tag with url', () => {
- expect(commonUtils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url');
- });
-
- it('url is escaped', () => {
- // IE11 will return a relative pathname while other browsers will return a full pathname.
- // parseUrl uses an anchor element for parsing an url. With relative urls, the anchor
- // element will create an absolute url relative to the current execution context.
- // The JavaScript test suite is executed at '/' which will lead to an absolute url
- // starting with '/'.
- expect(commonUtils.parseUrl('" test="asf"').pathname).toContain('/%22%20test=%22asf%22');
- });
- });
-
- describe('parseUrlPathname', () => {
- it('returns an absolute url when given an absolute url', () => {
- expect(commonUtils.parseUrlPathname('/some/absolute/url')).toEqual('/some/absolute/url');
- });
-
- it('returns an absolute url when given a relative url', () => {
- expect(commonUtils.parseUrlPathname('some/relative/url')).toEqual('/some/relative/url');
- });
- });
-
describe('handleLocationHash', () => {
beforeEach(() => {
jest.spyOn(window.document, 'getElementById');
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index c6edba19c56..7608cff4c9e 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -22,6 +22,27 @@ beforeEach(() => {
});
describe('URL utility', () => {
+ describe('parseUrlPathname', () => {
+ it('returns an absolute url when given an absolute url', () => {
+ expect(urlUtils.parseUrlPathname('/some/absolute/url')).toBe('/some/absolute/url');
+ });
+
+ it('returns an absolute url when given a relative url', () => {
+ expect(urlUtils.parseUrlPathname('some/relative/url')).toBe('/some/relative/url');
+ });
+
+ it('returns an absolute url that includes the document.location path when given a relative url', () => {
+ // Change the location to see the `/mypath/` included in the result
+ setWindowLocation(`${TEST_HOST}/mypath/`);
+
+ expect(urlUtils.parseUrlPathname('some/relative/url')).toBe('/mypath/some/relative/url');
+ });
+
+ it('encodes certain character in the url', () => {
+ expect(urlUtils.parseUrlPathname('test="a b"')).toBe('/test=%22a%20b%22');
+ });
+ });
+
describe('webIDEUrl', () => {
afterEach(() => {
gon.relative_url_root = '';
@@ -636,7 +657,7 @@ describe('URL utility', () => {
`('returns "$expectation" with "$protocol" protocol', ({ protocol, expectation }) => {
setWindowLocation(`${protocol}//example.com`);
- expect(urlUtils.getWebSocketProtocol()).toEqual(expectation);
+ expect(urlUtils.getWebSocketProtocol()).toBe(expectation);
});
});
@@ -646,7 +667,7 @@ describe('URL utility', () => {
const path = '/lorem/ipsum?a=bc';
- expect(urlUtils.getWebSocketUrl(path)).toEqual('ws://example.com/lorem/ipsum?a=bc');
+ expect(urlUtils.getWebSocketUrl(path)).toBe('ws://example.com/lorem/ipsum?a=bc');
});
});
@@ -696,7 +717,7 @@ describe('URL utility', () => {
it('should return valid parameter', () => {
setWindowLocation('?scope=all&p=2');
- expect(getParameterByName('p')).toEqual('2');
+ expect(getParameterByName('p')).toBe('2');
expect(getParameterByName('scope')).toBe('all');
});
@@ -737,7 +758,7 @@ describe('URL utility', () => {
it('converts search query object back into a search query', () => {
const searchQueryObject = { one: '1', two: '2' };
- expect(urlUtils.objectToQuery(searchQueryObject)).toEqual('one=1&two=2');
+ expect(urlUtils.objectToQuery(searchQueryObject)).toBe('one=1&two=2');
});
it('returns empty string when `params` is undefined, null or empty string', () => {
@@ -833,15 +854,15 @@ describe('URL utility', () => {
it('adds new params as query string', () => {
const url = 'https://gitlab.com/test';
- expect(
- urlUtils.setUrlParams({ group_id: 'gitlab-org', project_id: 'my-project' }, url),
- ).toEqual('https://gitlab.com/test?group_id=gitlab-org&project_id=my-project');
+ expect(urlUtils.setUrlParams({ group_id: 'gitlab-org', project_id: 'my-project' }, url)).toBe(
+ 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project',
+ );
});
it('updates an existing parameter', () => {
const url = 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project';
- expect(urlUtils.setUrlParams({ project_id: 'gitlab-test' }, url)).toEqual(
+ expect(urlUtils.setUrlParams({ project_id: 'gitlab-test' }, url)).toBe(
'https://gitlab.com/test?group_id=gitlab-org&project_id=gitlab-test',
);
});
@@ -849,7 +870,7 @@ describe('URL utility', () => {
it("removes the project_id param when it's value is null", () => {
const url = 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project';
- expect(urlUtils.setUrlParams({ project_id: null }, url)).toEqual(
+ expect(urlUtils.setUrlParams({ project_id: null }, url)).toBe(
'https://gitlab.com/test?group_id=gitlab-org',
);
});
@@ -857,7 +878,7 @@ describe('URL utility', () => {
it('adds parameters from arrays', () => {
const url = 'https://gitlab.com/test';
- expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url)).toEqual(
+ expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url)).toBe(
'https://gitlab.com/test?labels=foo&labels=bar',
);
});
@@ -865,13 +886,13 @@ describe('URL utility', () => {
it('removes parameters from empty arrays', () => {
const url = 'https://gitlab.com/test?labels=foo&labels=bar';
- expect(urlUtils.setUrlParams({ labels: [] }, url)).toEqual('https://gitlab.com/test');
+ expect(urlUtils.setUrlParams({ labels: [] }, url)).toBe('https://gitlab.com/test');
});
it('removes parameters from empty arrays while keeping other parameters', () => {
const url = 'https://gitlab.com/test?labels=foo&labels=bar&unrelated=unrelated';
- expect(urlUtils.setUrlParams({ labels: [] }, url)).toEqual(
+ expect(urlUtils.setUrlParams({ labels: [] }, url)).toBe(
'https://gitlab.com/test?unrelated=unrelated',
);
});
@@ -879,7 +900,7 @@ describe('URL utility', () => {
it('adds parameters from arrays when railsArraySyntax=true', () => {
const url = 'https://gitlab.com/test';
- expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true)).toEqual(
+ expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true)).toBe(
'https://gitlab.com/test?labels%5B%5D=foo&labels%5B%5D=bar',
);
});
@@ -887,7 +908,7 @@ describe('URL utility', () => {
it('removes parameters from empty arrays when railsArraySyntax=true', () => {
const url = 'https://gitlab.com/test?labels%5B%5D=foo&labels%5B%5D=bar';
- expect(urlUtils.setUrlParams({ labels: [] }, url, false, true)).toEqual(
+ expect(urlUtils.setUrlParams({ labels: [] }, url, false, true)).toBe(
'https://gitlab.com/test',
);
});
@@ -895,7 +916,7 @@ describe('URL utility', () => {
it('decodes URI when decodeURI=true', () => {
const url = 'https://gitlab.com/test';
- expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true, true)).toEqual(
+ expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true, true)).toBe(
'https://gitlab.com/test?labels[]=foo&labels[]=bar',
);
});
@@ -903,7 +924,7 @@ describe('URL utility', () => {
it('removes all existing URL params and sets a new param when cleanParams=true', () => {
const url = 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project';
- expect(urlUtils.setUrlParams({ foo: 'bar' }, url, true)).toEqual(
+ expect(urlUtils.setUrlParams({ foo: 'bar' }, url, true)).toBe(
'https://gitlab.com/test?foo=bar',
);
});
diff --git a/spec/frontend/merge_request_tabs_spec.js b/spec/frontend/merge_request_tabs_spec.js
index ced9b71125b..5c24a070342 100644
--- a/spec/frontend/merge_request_tabs_spec.js
+++ b/spec/frontend/merge_request_tabs_spec.js
@@ -4,6 +4,7 @@ import initMrPage from 'helpers/init_vue_mr_page_helper';
import axios from '~/lib/utils/axios_utils';
import MergeRequestTabs from '~/merge_request_tabs';
import '~/lib/utils/common_utils';
+import '~/lib/utils/url_utility';
jest.mock('~/lib/utils/webpack', () => ({
resetServiceWorkersPublicPath: jest.fn(),
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js
index e25162f4da5..9680e273add 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js
@@ -6,6 +6,7 @@ import {
DELETE_TAG_ERROR_MESSAGE,
DELETE_TAGS_SUCCESS_MESSAGE,
DELETE_TAGS_ERROR_MESSAGE,
+ DETAILS_IMPORTING_ERROR_MESSAGE,
ADMIN_GARBAGE_COLLECTION_TIP,
} from '~/packages_and_registries/container_registry/explorer/constants';
@@ -76,6 +77,7 @@ describe('Delete alert', () => {
});
});
});
+
describe('error states', () => {
describe.each`
deleteAlertType | message
@@ -105,6 +107,25 @@ describe('Delete alert', () => {
});
});
+ describe('importing repository error state', () => {
+ beforeEach(() => {
+ mountComponent({
+ deleteAlertType: 'danger_importing',
+ containerRegistryImportingHelpPagePath: 'https://foobar',
+ });
+ });
+
+ it('alert exist and text is appropriate', () => {
+ expect(findAlert().text()).toMatchInterpolatedText(DETAILS_IMPORTING_ERROR_MESSAGE);
+ });
+
+ it('alert body contains link', () => {
+ const alertLink = findLink();
+ expect(alertLink.exists()).toBe(true);
+ expect(alertLink.attributes('href')).toBe('https://foobar');
+ });
+ });
+
describe('dismissing alert', () => {
it('GlAlert dismiss event triggers a change event', () => {
mountComponent({ deleteAlertType: 'success_tags' });
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js
index f0c586fe7f4..fda1db4b7e1 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js
@@ -239,6 +239,15 @@ export const graphQLDeleteImageRepositoryTagsMock = {
},
};
+export const graphQLDeleteImageRepositoryTagImportingErrorMock = {
+ data: {
+ destroyContainerRepositoryTags: {
+ errors: ['repository importing'],
+ __typename: 'DestroyContainerRepositoryTagsPayload',
+ },
+ },
+};
+
export const dockerCommands = {
dockerBuildCommand: 'foofoo',
dockerPushCommand: 'barbar',
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
index c602b37c3b5..59ca47bee50 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
@@ -18,6 +18,7 @@ import {
UNFINISHED_STATUS,
DELETE_SCHEDULED,
ALERT_DANGER_IMAGE,
+ ALERT_DANGER_IMPORTING,
MISSING_OR_DELETED_IMAGE_BREADCRUMB,
ROOT_IMAGE_TEXT,
MISSING_OR_DELETED_IMAGE_TITLE,
@@ -33,6 +34,7 @@ import Tracking from '~/tracking';
import {
graphQLImageDetailsMock,
graphQLDeleteImageRepositoryTagsMock,
+ graphQLDeleteImageRepositoryTagImportingErrorMock,
containerRepositoryMock,
graphQLEmptyImageDetailsMock,
tagsMock,
@@ -329,6 +331,7 @@ describe('Details Page', () => {
const config = {
isAdmin: true,
garbageCollectionHelpPagePath: 'baz',
+ containerRegistryImportingHelpPagePath: 'https://foobar',
};
const deleteAlertType = 'success_tag';
@@ -353,6 +356,35 @@ describe('Details Page', () => {
expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType });
});
+
+ describe('importing repository error', () => {
+ let mutationResolver;
+ let tagsResolver;
+
+ beforeEach(async () => {
+ mutationResolver = jest
+ .fn()
+ .mockResolvedValue(graphQLDeleteImageRepositoryTagImportingErrorMock);
+ tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock));
+
+ mountComponent({ mutationResolver, tagsResolver });
+ await waitForApolloRequestRender();
+ });
+
+ it('displays the proper alert', async () => {
+ findTagsList().vm.$emit('delete', [cleanTags[0]]);
+ await nextTick();
+
+ findDeleteModal().vm.$emit('confirmDelete');
+ await waitForPromises();
+
+ expect(tagsResolver).toHaveBeenCalled();
+
+ const deleteAlert = findDeleteAlert();
+ expect(deleteAlert.exists()).toBe(true);
+ expect(deleteAlert.props('deleteAlertType')).toBe(ALERT_DANGER_IMPORTING);
+ });
+ });
});
describe('Partial Cleanup Alert', () => {
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 78585ed75bc..0e364eb6800 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -1,7 +1,12 @@
-import { shallowMount } from '@vue/test-utils';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { GlSprintf } from '@gitlab/ui';
+import VueApollo from 'vue-apollo';
+import produce from 'immer';
+import readyToMergeResponse from 'test_fixtures/graphql/merge_requests/states/ready_to_merge.query.graphql.json';
import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql';
import simplePoll from '~/lib/utils/simple_poll';
import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue';
import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
@@ -19,9 +24,11 @@ jest.mock('~/commons/nav/user_merge_requests', () => ({
refreshUserMergeRequestCounts: jest.fn(),
}));
-const commitMessage = 'This is the commit message';
-const squashCommitMessage = 'This is the squash commit message';
-const commitMessageWithDescription = 'This is the commit message description';
+const commitMessage = readyToMergeResponse.data.project.mergeRequest.defaultMergeCommitMessage;
+const squashCommitMessage =
+ readyToMergeResponse.data.project.mergeRequest.defaultSquashCommitMessage;
+const commitMessageWithDescription =
+ readyToMergeResponse.data.project.mergeRequest.defaultMergeCommitMessageWithDescription;
const createTestMr = (customConfig) => {
const mr = {
isPipelineActive: false,
@@ -42,6 +49,8 @@ const createTestMr = (customConfig) => {
commitMessage,
squashCommitMessage,
commitMessageWithDescription,
+ defaultMergeCommitMessage: commitMessage,
+ defaultSquashCommitMessage: squashCommitMessage,
shouldRemoveSourceBranch: true,
canRemoveSourceBranch: false,
targetBranch: 'main',
@@ -61,15 +70,25 @@ const createTestService = () => ({
merge: jest.fn(),
poll: jest.fn().mockResolvedValue(),
});
+const localVue = createLocalVue();
+localVue.use(VueApollo);
let wrapper;
+let readyToMergeResponseSpy;
const findMergeButton = () => wrapper.find('[data-testid="merge-button"]');
const findPipelineFailedConfirmModal = () =>
wrapper.findComponent(MergeFailedPipelineConfirmationDialog);
+const createReadyToMergeResponse = (customMr) => {
+ return produce(readyToMergeResponse, (draft) => {
+ Object.assign(draft.data.project.mergeRequest, customMr);
+ });
+};
+
const createComponent = (customConfig = {}, mergeRequestWidgetGraphql = false) => {
wrapper = shallowMount(ReadyToMerge, {
+ localVue,
propsData: {
mr: createTestMr(customConfig),
service: createTestService(),
@@ -82,10 +101,29 @@ const createComponent = (customConfig = {}, mergeRequestWidgetGraphql = false) =
stubs: {
CommitEdit,
},
+ apolloProvider: createMockApollo([[readyToMergeQuery, readyToMergeResponseSpy]]),
});
};
+const findCheckboxElement = () => wrapper.find(SquashBeforeMerge);
+const findCommitsHeaderElement = () => wrapper.find(CommitsHeader);
+const findCommitEditElements = () => wrapper.findAll(CommitEdit);
+const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
+const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label');
+const findTipLink = () => wrapper.find(GlSprintf);
+const findCommitEditWithInputId = (inputId) =>
+ findCommitEditElements().wrappers.find((x) => x.props('inputId') === inputId);
+const findMergeCommitMessage = () => findCommitEditWithInputId('merge-message-edit').props('value');
+const findSquashCommitMessage = () =>
+ findCommitEditWithInputId('squash-message-edit').props('value');
+
+const triggerApprovalUpdated = () => eventHub.$emit('ApprovalUpdated');
+
describe('ReadyToMerge', () => {
+ beforeEach(() => {
+ readyToMergeResponseSpy = jest.fn().mockResolvedValueOnce(readyToMergeResponse);
+ });
+
afterEach(() => {
wrapper.destroy();
});
@@ -447,13 +485,6 @@ describe('ReadyToMerge', () => {
});
describe('render children components', () => {
- const findCheckboxElement = () => wrapper.find(SquashBeforeMerge);
- const findCommitsHeaderElement = () => wrapper.find(CommitsHeader);
- const findCommitEditElements = () => wrapper.findAll(CommitEdit);
- const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
- const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label');
- const findTipLink = () => wrapper.find(GlSprintf);
-
describe('squash checkbox', () => {
it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => {
createComponent({
@@ -772,4 +803,65 @@ describe('ReadyToMerge', () => {
expect(findPipelineFailedConfirmModal().props()).toEqual({ visible: true });
});
});
+
+ describe('updating graphql data triggers commit message update when default changed', () => {
+ const UPDATED_MERGE_COMMIT_MESSAGE = 'New merge message from BE';
+ const UPDATED_SQUASH_COMMIT_MESSAGE = 'New squash message from BE';
+ const USER_COMMIT_MESSAGE = 'Merge message provided manually by user';
+
+ const createDefaultGqlComponent = () =>
+ createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: true } }, true);
+
+ beforeEach(() => {
+ readyToMergeResponseSpy = jest
+ .fn()
+ .mockResolvedValueOnce(createReadyToMergeResponse({ squash: true, squashOnMerge: true }))
+ .mockResolvedValue(
+ createReadyToMergeResponse({
+ squash: true,
+ squashOnMerge: true,
+ defaultMergeCommitMessage: UPDATED_MERGE_COMMIT_MESSAGE,
+ defaultSquashCommitMessage: UPDATED_SQUASH_COMMIT_MESSAGE,
+ }),
+ );
+ });
+
+ describe.each`
+ desc | finderFn | initialValue | updatedValue | inputId
+ ${'merge commit message'} | ${findMergeCommitMessage} | ${commitMessage} | ${UPDATED_MERGE_COMMIT_MESSAGE} | ${'#merge-message-edit'}
+ ${'squash commit message'} | ${findSquashCommitMessage} | ${squashCommitMessage} | ${UPDATED_SQUASH_COMMIT_MESSAGE} | ${'#squash-message-edit'}
+ `('with $desc', ({ finderFn, initialValue, updatedValue, inputId }) => {
+ it('should have initial value', async () => {
+ createDefaultGqlComponent();
+
+ await waitForPromises();
+
+ expect(finderFn()).toBe(initialValue);
+ });
+
+ it('should have updated value after graphql refetch', async () => {
+ createDefaultGqlComponent();
+ await waitForPromises();
+
+ triggerApprovalUpdated();
+ await waitForPromises();
+
+ expect(finderFn()).toBe(updatedValue);
+ });
+
+ it('should not update if user has touched', async () => {
+ createDefaultGqlComponent();
+ await waitForPromises();
+
+ const input = wrapper.find(inputId);
+ input.element.value = USER_COMMIT_MESSAGE;
+ input.trigger('input');
+
+ triggerApprovalUpdated();
+ await waitForPromises();
+
+ expect(finderFn()).toBe(USER_COMMIT_MESSAGE);
+ });
+ });
+ });
});
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index e5c48c0c9c4..a7e657f2636 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -318,13 +318,14 @@ RSpec.describe MarkupHelper do
let(:wiki) { build(:wiki, container: project) }
let(:content) { 'wiki content' }
let(:slug) { 'nested/page' }
- let(:wiki_page) { double('WikiPage', path: "file.#{extension}", content: content, slug: slug, wiki: wiki) }
+ let(:path) { "file.#{extension}" }
+ let(:wiki_page) { double('WikiPage', path: path, content: content, slug: slug, wiki: wiki) }
let(:context) do
{
pipeline: :wiki, project: project, wiki: wiki,
page_slug: slug, issuable_reference_expansion_enabled: true,
- repository: wiki.repository
+ repository: wiki.repository, requested_path: path
}
end
diff --git a/spec/lib/api/entities/wiki_page_spec.rb b/spec/lib/api/entities/wiki_page_spec.rb
index de36ebc3a76..238c8233a14 100644
--- a/spec/lib/api/entities/wiki_page_spec.rb
+++ b/spec/lib/api/entities/wiki_page_spec.rb
@@ -29,6 +29,20 @@ RSpec.describe API::Entities::WikiPage do
it 'returns the wiki page content rendered' do
expect(subject[:content]).to eq "