From 1b7a8ceadb5f0facee1b2ba9f7e4c8475b7e6f9b Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 8 Dec 2021 00:15:00 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/issue_templates/Deprecations.md | 22 ++++++- .../javascripts/blob/pdf/pdf_viewer.vue | 8 ++- app/assets/javascripts/pdf/index.vue | 2 +- .../components/blob_viewers/index.js | 1 + .../components/blob_viewers/pdf_viewer.vue | 38 +++++++++++- .../javascripts/repository/constants.js | 3 + .../stylesheets/framework/snippets.scss | 5 ++ app/assets/stylesheets/pages/commits.scss | 1 - app/finders/groups_finder.rb | 21 +++++-- app/graphql/types/issue_type_enum.rb | 2 +- app/models/work_item/type.rb | 7 ++- .../use_traversal_ids_groups_finder.yml | 8 +++ ...11126204445_add_task_to_work_item_types.rb | 31 ++++++++++ ...e_subscriptions_to_application_settings.rb | 7 +++ ...x_to_projects_on_marked_for_deletion_at.rb | 15 +++++ db/schema_migrations/20211126204445 | 1 + db/schema_migrations/20211201061733 | 1 + db/schema_migrations/20211203091642 | 1 + db/structure.sql | 3 + .../documentation/styleguide/img/callouts.png | Bin 0 -> 4875 bytes .../documentation/styleguide/index.md | 11 ++++ .../project/repository/forking_workflow.md | 8 +-- lib/api/entities/issue_basic.rb | 2 +- lib/api/issues.rb | 4 +- locale/gitlab.pot | 6 ++ qa/qa/resource/group.rb | 3 +- qa/qa/resource/group_base.rb | 2 +- qa/qa/resource/sandbox.rb | 3 +- .../api/1_manage/bulk_import_group_spec.rb | 22 ++++++- .../projects/issues_controller_spec.rb | 9 +++ spec/finders/groups_finder_spec.rb | 15 ++++- .../blob_viewers/pdf_viewer_spec.js | 47 +++++++++++++-- spec/graphql/types/issue_type_enum_spec.rb | 6 +- ...150320_create_base_work_item_types_spec.rb | 22 +++++-- ...203408_upsert_base_work_item_types_spec.rb | 31 +++++++--- ...204445_add_task_to_work_item_types_spec.rb | 54 ++++++++++++++++++ ...projects_on_marked_for_deletion_at_spec.rb | 18 ++++++ spec/models/work_item/type_spec.rb | 4 +- spec/requests/api/graphql/ci/jobs_spec.rb | 2 +- .../work_item_types_helper.rb | 27 +++++++++ 40 files changed, 423 insertions(+), 50 deletions(-) create mode 100644 config/feature_flags/development/use_traversal_ids_groups_finder.yml create mode 100644 db/migrate/20211126204445_add_task_to_work_item_types.rb create mode 100644 db/migrate/20211201061733_add_future_subscriptions_to_application_settings.rb create mode 100644 db/post_migrate/20211203091642_add_index_to_projects_on_marked_for_deletion_at.rb create mode 100644 db/schema_migrations/20211126204445 create mode 100644 db/schema_migrations/20211201061733 create mode 100644 db/schema_migrations/20211203091642 create mode 100644 doc/development/documentation/styleguide/img/callouts.png create mode 100644 spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb create mode 100644 spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb create mode 100644 spec/support/helpers/migrations_helpers/work_item_types_helper.rb diff --git a/.gitlab/issue_templates/Deprecations.md b/.gitlab/issue_templates/Deprecations.md index caef5c64334..85db4314233 100644 --- a/.gitlab/issue_templates/Deprecations.md +++ b/.gitlab/issue_templates/Deprecations.md @@ -12,7 +12,10 @@ The description of the deprecation should state what actions the user should tak ### Breaking Change - + + ### Affected Topology + + + + +/label ~devops:: ~group: ~Category: + + +/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate" + + +/label ~"type::deprecation" + + \ No newline at end of file diff --git a/app/assets/javascripts/blob/pdf/pdf_viewer.vue b/app/assets/javascripts/blob/pdf/pdf_viewer.vue index 96d6f500960..a1a62abeb6f 100644 --- a/app/assets/javascripts/blob/pdf/pdf_viewer.vue +++ b/app/assets/javascripts/blob/pdf/pdf_viewer.vue @@ -38,7 +38,13 @@ export default {
- +

{{ __('An error occurred while loading the file. Please try again later.') }} diff --git a/app/assets/javascripts/pdf/index.vue b/app/assets/javascripts/pdf/index.vue index 6a64538abfe..644eccc0232 100644 --- a/app/assets/javascripts/pdf/index.vue +++ b/app/assets/javascripts/pdf/index.vue @@ -45,7 +45,7 @@ export default { .promise.then(this.renderPages) .then((pages) => { this.pages = pages; - this.$emit('pdflabload'); + this.$emit('pdflabload', pages.length); }) .catch((error) => { this.$emit('pdflaberror', error); diff --git a/app/assets/javascripts/repository/components/blob_viewers/index.js b/app/assets/javascripts/repository/components/blob_viewers/index.js index b5c4c81b9d8..62d687e848b 100644 --- a/app/assets/javascripts/repository/components/blob_viewers/index.js +++ b/app/assets/javascripts/repository/components/blob_viewers/index.js @@ -40,6 +40,7 @@ export const viewerProps = (type, blob) => { }, pdf: { url: blob.rawPath, + fileSize: blob.rawSize, }, }[type]; }; diff --git a/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue index 3eefcd64b13..803a357df52 100644 --- a/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue @@ -1,16 +1,50 @@ diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js index b4363c51165..cdc4818e493 100644 --- a/app/assets/javascripts/repository/constants.js +++ b/app/assets/javascripts/repository/constants.js @@ -20,3 +20,6 @@ export const COMMIT_MESSAGE_BODY_MAX_LENGTH = 72; export const LIMITED_CONTAINER_WIDTH_CLASS = 'limit-container-width'; export const I18N_COMMIT_DATA_FETCH_ERROR = __('An error occurred while fetching commit data.'); + +export const PDF_MAX_FILE_SIZE = 10000000; // 10 MB +export const PDF_MAX_PAGE_LIMIT = 50; diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index c59e70c80df..39786aa0138 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -16,6 +16,11 @@ .snippet-file-content { border-radius: 3px; + .file-content { + max-height: 500px; + overflow-y: auto; + } + + .snippet-file-content { @include gl-mt-5; } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 7f35b8fab43..cc8ea1493fc 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -149,7 +149,6 @@ .commit-content { padding-right: 10px; white-space: normal; - overflow: hidden; .commit-title { display: flex; diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index 7ea3362fba1..7e3cdd79a4c 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -52,7 +52,16 @@ class GroupsFinder < UnionFinder return [Group.all] if current_user&.can_read_all_resources? && all_available? groups = [] - groups << Gitlab::ObjectHierarchy.new(groups_for_ancestors, groups_for_descendants).all_objects if current_user + + if current_user + if Feature.enabled?(:use_traversal_ids_groups_finder, default_enabled: :yaml) + groups << current_user.authorized_groups.self_and_ancestors + groups << current_user.groups.self_and_descendants + else + groups << Gitlab::ObjectHierarchy.new(groups_for_ancestors, groups_for_descendants).all_objects + end + end + groups << Group.unscoped.public_to_user(current_user) if include_public_groups? groups << Group.none if groups.empty? groups @@ -72,9 +81,13 @@ class GroupsFinder < UnionFinder .groups .where('members.access_level >= ?', params[:min_access_level]) - Gitlab::ObjectHierarchy - .new(groups) - .base_and_descendants + if Feature.enabled?(:use_traversal_ids_groups_finder, default_enabled: :yaml) + groups.self_and_descendants + else + Gitlab::ObjectHierarchy + .new(groups) + .base_and_descendants + end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb index 6999ea270a2..0cfba6bbbd0 100644 --- a/app/graphql/types/issue_type_enum.rb +++ b/app/graphql/types/issue_type_enum.rb @@ -5,7 +5,7 @@ module Types graphql_name 'IssueType' description 'Issue type' - ::WorkItem::Type.base_types.keys.each do |issue_type| + ::WorkItem::Type.allowed_types_for_issues.each do |issue_type| value issue_type.upcase, value: issue_type, description: "#{issue_type.titleize} issue type" end end diff --git a/app/models/work_item/type.rb b/app/models/work_item/type.rb index 7038beadd62..3acb9c0011c 100644 --- a/app/models/work_item/type.rb +++ b/app/models/work_item/type.rb @@ -15,7 +15,8 @@ class WorkItem::Type < ApplicationRecord issue: { name: 'Issue', icon_name: 'issue-type-issue', enum_value: 0 }, incident: { name: 'Incident', icon_name: 'issue-type-incident', enum_value: 1 }, test_case: { name: 'Test Case', icon_name: 'issue-type-test-case', enum_value: 2 }, ## EE-only - requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 } ## EE-only + requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 }, ## EE-only + task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 } }.freeze cache_markdown_field :description, pipeline: :single_line @@ -42,6 +43,10 @@ class WorkItem::Type < ApplicationRecord default_by_type(:issue) end + def self.allowed_types_for_issues + base_types.keys.excluding('task') + end + private def strip_whitespace diff --git a/config/feature_flags/development/use_traversal_ids_groups_finder.yml b/config/feature_flags/development/use_traversal_ids_groups_finder.yml new file mode 100644 index 00000000000..b0550fe62d9 --- /dev/null +++ b/config/feature_flags/development/use_traversal_ids_groups_finder.yml @@ -0,0 +1,8 @@ +--- +name: use_traversal_ids_groups_finder +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67650 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345666 +milestone: '14.6' +type: development +group: group::access +default_enabled: false diff --git a/db/migrate/20211126204445_add_task_to_work_item_types.rb b/db/migrate/20211126204445_add_task_to_work_item_types.rb new file mode 100644 index 00000000000..875c2272c6d --- /dev/null +++ b/db/migrate/20211126204445_add_task_to_work_item_types.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class AddTaskToWorkItemTypes < Gitlab::Database::Migration[1.0] + TASK_ENUM_VALUE = 4 + + class WorkItemType < ActiveRecord::Base + self.inheritance_column = :_type_disabled + self.table_name = 'work_item_types' + + validates :name, uniqueness: { case_sensitive: false, scope: [:namespace_id] } + end + + def up + # New instances will not run this migration and add this type via fixtures + # checking if record exists mostly because migration specs will run all migrations + # and that will conflict with the preloaded base work item types + task_work_item = WorkItemType.find_by(name: 'Task', namespace_id: nil) + + if task_work_item + say('Task item record exist, skipping creation') + else + WorkItemType.create(name: 'Task', namespace_id: nil, base_type: TASK_ENUM_VALUE, icon_name: 'issue-type-task') + end + end + + def down + # There's the remote possibility that issues could already be + # using this issue type, with a tight foreign constraint. + # Therefore we will not attempt to remove any data. + end +end diff --git a/db/migrate/20211201061733_add_future_subscriptions_to_application_settings.rb b/db/migrate/20211201061733_add_future_subscriptions_to_application_settings.rb new file mode 100644 index 00000000000..9cbbef42327 --- /dev/null +++ b/db/migrate/20211201061733_add_future_subscriptions_to_application_settings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddFutureSubscriptionsToApplicationSettings < Gitlab::Database::Migration[1.0] + def change + add_column :application_settings, :future_subscriptions, :jsonb, null: false, default: [] + end +end diff --git a/db/post_migrate/20211203091642_add_index_to_projects_on_marked_for_deletion_at.rb b/db/post_migrate/20211203091642_add_index_to_projects_on_marked_for_deletion_at.rb new file mode 100644 index 00000000000..56b0df1f393 --- /dev/null +++ b/db/post_migrate/20211203091642_add_index_to_projects_on_marked_for_deletion_at.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddIndexToProjectsOnMarkedForDeletionAt < Gitlab::Database::Migration[1.0] + disable_ddl_transaction! + + INDEX_NAME = 'index_projects_not_aimed_for_deletion' + + def up + add_concurrent_index :projects, :id, where: 'marked_for_deletion_at IS NULL', name: INDEX_NAME + end + + def down + remove_concurrent_index :projects, :id, name: INDEX_NAME + end +end diff --git a/db/schema_migrations/20211126204445 b/db/schema_migrations/20211126204445 new file mode 100644 index 00000000000..b130d90b8ec --- /dev/null +++ b/db/schema_migrations/20211126204445 @@ -0,0 +1 @@ +e31592bbeb6ba6175f19cfceaafb37672633028dd021052542909999b46eac38 \ No newline at end of file diff --git a/db/schema_migrations/20211201061733 b/db/schema_migrations/20211201061733 new file mode 100644 index 00000000000..722e7dd828c --- /dev/null +++ b/db/schema_migrations/20211201061733 @@ -0,0 +1 @@ +c5282e48f31c0896a3ce21fe238eb602dc006b0bfe62aa4f12ee39bbd620c76c \ No newline at end of file diff --git a/db/schema_migrations/20211203091642 b/db/schema_migrations/20211203091642 new file mode 100644 index 00000000000..ef53b1d2ed9 --- /dev/null +++ b/db/schema_migrations/20211203091642 @@ -0,0 +1 @@ +9954fb041a3f284f53cc9c5c68b1a9dff36513a1851e663c221eccd40736fb16 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 435ef8d4b0c..7307577f820 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -10479,6 +10479,7 @@ CREATE TABLE application_settings ( sentry_environment text, max_ssh_key_lifetime integer, static_objects_external_storage_auth_token_encrypted text, + future_subscriptions jsonb DEFAULT '[]'::jsonb NOT NULL, CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)), CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)), @@ -27106,6 +27107,8 @@ CREATE INDEX index_projects_api_vis20_path ON projects USING btree (path, id) WH CREATE INDEX index_projects_api_vis20_updated_at ON projects USING btree (updated_at, id) WHERE (visibility_level = 20); +CREATE INDEX index_projects_not_aimed_for_deletion ON projects USING btree (id) WHERE (marked_for_deletion_at IS NULL); + CREATE INDEX index_projects_on_created_at_and_id ON projects USING btree (created_at, id); CREATE INDEX index_projects_on_creator_id_and_created_at_and_id ON projects USING btree (creator_id, created_at, id); diff --git a/doc/development/documentation/styleguide/img/callouts.png b/doc/development/documentation/styleguide/img/callouts.png new file mode 100644 index 0000000000000000000000000000000000000000..b84e9e269dc41ce20dcb0c7580a5d01d48824a35 GIT binary patch literal 4875 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 literal 0 HcmV?d00001 diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index 255fb3dc786..d56f604bf5a 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -1162,6 +1162,17 @@ review app in the merge request. Make sure the image isn't blurry or overwhelmin - **Be consistent.** Coordinate screenshots with the other screenshots already on a documentation page for a consistent reading experience. +### Add callouts + +If you need to emphasize an area in a screenshot, use an arrow. + +- For color, use `#EE2604`. If you use the Preview application on macOS, this is the default red. +- For the line width, use 3 pt. If you use the Preview application on macOS, this is the third line in the list. +- Use the arrow style shown in the following image. +- If you have multiple arrows, make them parallel when possible. + +![callout example](img/callouts.png) + ### Save the image - Resize any wide or tall screenshots if needed, but make sure the screenshot is diff --git a/doc/user/project/repository/forking_workflow.md b/doc/user/project/repository/forking_workflow.md index 13fd9572dc5..1ab21286a8e 100644 --- a/doc/user/project/repository/forking_workflow.md +++ b/doc/user/project/repository/forking_workflow.md @@ -26,17 +26,17 @@ To fork an existing project in GitLab: 1. Select the project to fork to: - - *(Recommended method)* Below **Select a namespace to fork the project**, identify + - Recommended method. Below **Select a namespace to fork the project**, identify the project you want to fork to, and click **Select**. Only namespaces you have Developer and higher [permissions](../../permissions.md) for are shown. ![Choose namespace](img/forking_workflow_choose_namespace_v13_10.png) - - *(Experimental method)* If your GitLab administrator has + - Experimental method. If your GitLab administrator has [enabled the experimental fork project form](#enable-or-disable-the-fork-project-form), read [Create a fork with the fork project form](#create-a-fork-with-the-fork-project-form). - Only namespaces you have Developer and higher - [permissions](../../permissions.md) for are shown. + Only namespaces you have at least the Developer + [role](../../permissions.md) for are shown. NOTE: The project path must be unique in the namespace. diff --git a/lib/api/entities/issue_basic.rb b/lib/api/entities/issue_basic.rb index ab248523028..6125dc05a6e 100644 --- a/lib/api/entities/issue_basic.rb +++ b/lib/api/entities/issue_basic.rb @@ -23,7 +23,7 @@ module API expose :issue_type, as: :type, format_with: :upcase, - documentation: { type: "String", desc: "One of #{::WorkItem::Type.base_types.keys.map(&:upcase)}" } + documentation: { type: "String", desc: "One of #{::WorkItem::Type.allowed_types_for_issues.map(&:upcase)}" } expose :assignee, using: ::API::Entities::UserBasic do |issue| issue.assignees.first diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 9958526fa7f..06d5e4cb504 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -82,7 +82,7 @@ module API desc: 'Return issues sorted in `asc` or `desc` order.' optional :due_date, type: String, values: %w[0 overdue week month next_month_and_previous_two_weeks] << '', desc: 'Return issues that have no due date (`0`), or whose due date is this week, this month, between two weeks ago and next month, or which are overdue. Accepts: `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`, `0`' - optional :issue_type, type: String, values: WorkItem::Type.base_types.keys, desc: "The type of the issue. Accepts: #{WorkItem::Type.base_types.keys.join(', ')}" + optional :issue_type, type: String, values: WorkItem::Type.allowed_types_for_issues, desc: "The type of the issue. Accepts: #{WorkItem::Type.allowed_types_for_issues.join(', ')}" use :issues_stats_params use :pagination @@ -99,7 +99,7 @@ module API optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY' optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential' optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked" - optional :issue_type, type: String, values: WorkItem::Type.base_types.keys, desc: "The type of the issue. Accepts: #{WorkItem::Type.base_types.keys.join(', ')}" + optional :issue_type, type: String, values: WorkItem::Type.allowed_types_for_issues, desc: "The type of the issue. Accepts: #{WorkItem::Type.allowed_types_for_issues.join(', ')}" use :optional_issue_params_ee end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cd1f5967ce6..b6fac186500 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12524,6 +12524,9 @@ msgstr "" msgid "Download CSV" msgstr "" +msgid "Download PDF" +msgstr "" + msgid "Download artifacts" msgstr "" @@ -35558,6 +35561,9 @@ msgstr "" msgid "This GitLab instance is undergoing maintenance and is operating in read-only mode." msgstr "" +msgid "This PDF is too large to display. Please download to view." +msgstr "" + msgid "This Project is currently archived and read-only. Please unarchive the project first if you want to resume Pull mirroring" msgstr "" diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb index ce85273c3b2..a325d96ccc2 100644 --- a/qa/qa/resource/group.rb +++ b/qa/qa/resource/group.rb @@ -68,7 +68,8 @@ module QA path: path, name: path, visibility: 'public', - require_two_factor_authentication: @require_two_factor_authentication + require_two_factor_authentication: @require_two_factor_authentication, + avatar: avatar } end diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb index 932d39675a3..19bb5ea00d7 100644 --- a/qa/qa/resource/group_base.rb +++ b/qa/qa/resource/group_base.rb @@ -7,7 +7,7 @@ module QA class GroupBase < Base include Members - attr_accessor :path + attr_accessor :path, :avatar attributes :id, :runners_token, diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb index 385e0fa4f7e..555bfb1abc9 100644 --- a/qa/qa/resource/sandbox.rb +++ b/qa/qa/resource/sandbox.rb @@ -69,7 +69,8 @@ module QA { path: path, name: path, - visibility: 'public' + visibility: 'public', + avatar: avatar } end diff --git a/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb b/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb index 158881ed94c..fe88d7c949a 100644 --- a/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb +++ b/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb @@ -26,6 +26,7 @@ module QA Resource::Sandbox.fabricate_via_api! do |group| group.api_client = api_client group.path = "source-group-for-import-#{SecureRandom.hex(4)}" + group.avatar = File.new('qa/fixtures/designs/tanuki.jpg', 'r') end end @@ -37,6 +38,10 @@ module QA end end + let(:import_failures) do + imported_group.import_details.sum([]) { |details| details[:failures] } + end + before do sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER) end @@ -73,6 +78,8 @@ module QA label.group = subgroup label.title = "subgroup-#{SecureRandom.hex(4)}" end + + imported_group # trigger import end it( @@ -87,6 +94,8 @@ module QA expect(imported_subgroup.reload!).to eq(subgroup) expect(imported_subgroup.labels).to include(*subgroup.labels) + + expect(import_failures).to be_empty, "Expected no errors, received: #{import_failures}" end end end @@ -108,6 +117,8 @@ module QA badge.link_url = "http://example.com/badge" badge.image_url = "http://shields.io/badge" end + + imported_group # trigger import end it( @@ -124,6 +135,8 @@ module QA expect(imported_milestone.updated_at).to eq(source_milestone.updated_at) expect(imported_group.badges).to eq(source_group.badges) + + expect(import_failures).to be_empty, "Expected no errors, received: #{import_failures}" end end end @@ -139,6 +152,8 @@ module QA before do member.set_public_email source_group.add_member(member, Resource::Members::AccessLevel::DEVELOPER) + + imported_group # trigger import end after do @@ -153,8 +168,11 @@ module QA imported_member = imported_group.reload!.members.find { |usr| usr.username == member.username } - expect(imported_member).not_to be_nil - expect(imported_member.access_level).to eq(Resource::Members::AccessLevel::DEVELOPER) + aggregate_failures do + expect(imported_member).not_to be_nil + expect(imported_member.access_level).to eq(Resource::Members::AccessLevel::DEVELOPER) + expect(import_failures).to be_empty, "Expected no errors, received: #{import_failures}" + end end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 68cccfa8bde..ce8bbf92cdf 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1197,6 +1197,15 @@ RSpec.describe Projects::IssuesController do end end + context 'when trying to create a task' do + it 'defaults to issue type' do + issue = post_new_issue(issue_type: 'task') + + expect(issue.issue_type).to eq('issue') + expect(issue.work_item_type.base_type).to eq('issue') + end + end + it 'creates the issue successfully', :aggregate_failures do issue = post_new_issue diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index 10a08d7326e..a4cbee6a124 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe GroupsFinder do include AdminModeHelper - describe '#execute' do + shared_examples '#execute' do let(:user) { create(:user) } describe 'root level groups' do @@ -20,6 +20,7 @@ RSpec.describe GroupsFinder do user_private_group) :regular | { all_available: false } | %i(user_public_group user_internal_group user_private_group) :regular | {} | %i(public_group internal_group user_public_group user_internal_group user_private_group) + :regular | { min_access_level: Gitlab::Access::DEVELOPER } | %i(user_public_group user_internal_group user_private_group) :external | { all_available: true } | %i(public_group user_public_group user_internal_group user_private_group) :external | { all_available: false } | %i(user_public_group user_internal_group user_private_group) @@ -261,4 +262,16 @@ RSpec.describe GroupsFinder do end end end + + describe '#execute' do + include_examples '#execute' + + context 'when use_traversal_ids_groups_finder feature flags is disabled' do + before do + stub_feature_flags(use_traversal_ids_groups_finder: false) + end + + include_examples '#execute' + end + end end diff --git a/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js index adee5b90863..fd910002529 100644 --- a/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js +++ b/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js @@ -1,22 +1,59 @@ -import { shallowMount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; import Component from '~/repository/components/blob_viewers/pdf_viewer.vue'; import PdfViewer from '~/blob/pdf/pdf_viewer.vue'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; describe('PDF Viewer', () => { let wrapper; - const propsData = { url: 'some/pdf_blob.pdf' }; + const defaultPropsData = { url: 'some/pdf_blob.pdf' }; - const createComponent = () => { - wrapper = shallowMount(Component, { propsData }); + const createComponent = (fileSize = 999) => { + wrapper = shallowMountExtended(Component, { propsData: { ...defaultPropsData, fileSize } }); }; const findPDFViewer = () => wrapper.findComponent(PdfViewer); + const findHelpText = () => wrapper.find('p'); + const findDownLoadButton = () => wrapper.findComponent(GlButton); it('renders a PDF Viewer component', () => { createComponent(); expect(findPDFViewer().exists()).toBe(true); - expect(findPDFViewer().props('pdf')).toBe(propsData.url); + expect(findPDFViewer().props('pdf')).toBe(defaultPropsData.url); + }); + + describe('Too large', () => { + beforeEach(() => createComponent(20000000)); + + it('does not a PDF Viewer component', () => { + expect(findPDFViewer().exists()).toBe(false); + }); + + it('renders help text', () => { + expect(findHelpText().text()).toBe( + 'This PDF is too large to display. Please download to view.', + ); + }); + + it('renders a download button', () => { + expect(findDownLoadButton().text()).toBe('Download PDF'); + expect(findDownLoadButton().props('icon')).toBe('download'); + }); + }); + + describe('Too many pages', () => { + beforeEach(() => { + createComponent(); + findPDFViewer().vm.$emit('pdflabload', 100); + }); + + it('does not a PDF Viewer component', () => { + expect(findPDFViewer().exists()).toBe(false); + }); + + it('renders a download button', () => { + expect(findDownLoadButton().exists()).toBe(true); + }); }); }); diff --git a/spec/graphql/types/issue_type_enum_spec.rb b/spec/graphql/types/issue_type_enum_spec.rb index 7ae5eb76f28..131e92aa5ed 100644 --- a/spec/graphql/types/issue_type_enum_spec.rb +++ b/spec/graphql/types/issue_type_enum_spec.rb @@ -5,9 +5,9 @@ require 'spec_helper' RSpec.describe Types::IssueTypeEnum do specify { expect(described_class.graphql_name).to eq('IssueType') } - it 'exposes all the existing issue type values' do - expect(described_class.values.keys).to include( - *%w[ISSUE INCIDENT] + it 'exposes all the existing issue type values except for task' do + expect(described_class.values.keys).to match_array( + %w[ISSUE INCIDENT TEST_CASE REQUIREMENT] ) end end diff --git a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb index 34ea7f53f51..6df8e1b2ebf 100644 --- a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb +++ b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb @@ -4,18 +4,28 @@ require 'spec_helper' require_migration! RSpec.describe CreateBaseWorkItemTypes, :migration do - let!(:work_item_types) { table(:work_item_types) } + include MigrationHelpers::WorkItemTypesHelper + + let_it_be(:work_item_types) { table(:work_item_types) } + + let(:base_types) do + { + issue: 0, + incident: 1, + test_case: 2, + requirement: 3 + } + end after(:all) do # Make sure base types are recreated after running the migration # because migration specs are not run in a transaction - WorkItem::Type.delete_all - Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import + reset_work_item_types end it 'creates default data' do # Need to delete all as base types are seeded before entire test suite - WorkItem::Type.delete_all + work_item_types.delete_all reversible_migration do |migration| migration.before -> { @@ -24,8 +34,8 @@ RSpec.describe CreateBaseWorkItemTypes, :migration do } migration.after -> { - expect(work_item_types.count).to eq 4 - expect(work_item_types.all.pluck(:base_type)).to match_array WorkItem::Type.base_types.values + expect(work_item_types.count).to eq(4) + expect(work_item_types.all.pluck(:base_type)).to match_array(base_types.values) } end end diff --git a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb index 3c8c55ccb80..1957a973ee1 100644 --- a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb +++ b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb @@ -4,19 +4,29 @@ require 'spec_helper' require_migration! RSpec.describe UpsertBaseWorkItemTypes, :migration do - let!(:work_item_types) { table(:work_item_types) } + include MigrationHelpers::WorkItemTypesHelper + + let_it_be(:work_item_types) { table(:work_item_types) } + + let(:base_types) do + { + issue: 0, + incident: 1, + test_case: 2, + requirement: 3 + } + end after(:all) do # Make sure base types are recreated after running the migration # because migration specs are not run in a transaction - WorkItem::Type.delete_all - Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import + reset_work_item_types end context 'when no default types exist' do it 'creates default data' do # Need to delete all as base types are seeded before entire test suite - WorkItem::Type.delete_all + work_item_types.delete_all expect(work_item_types.count).to eq(0) @@ -29,7 +39,7 @@ RSpec.describe UpsertBaseWorkItemTypes, :migration do migration.after -> { expect(work_item_types.count).to eq(4) - expect(work_item_types.all.pluck(:base_type)).to match_array(WorkItem::Type.base_types.values) + expect(work_item_types.all.pluck(:base_type)).to match_array(base_types.values) } end end @@ -37,16 +47,21 @@ RSpec.describe UpsertBaseWorkItemTypes, :migration do context 'when default types already exist' do it 'does not create default types again' do - expect(work_item_types.all.pluck(:base_type)).to match_array(WorkItem::Type.base_types.values) + # Database needs to be in a similar state as when this migration was created + work_item_types.delete_all + work_item_types.find_or_create_by!(name: 'Issue', namespace_id: nil, base_type: base_types[:issue], icon_name: 'issue-type-issue') + work_item_types.find_or_create_by!(name: 'Incident', namespace_id: nil, base_type: base_types[:incident], icon_name: 'issue-type-incident') + work_item_types.find_or_create_by!(name: 'Test Case', namespace_id: nil, base_type: base_types[:test_case], icon_name: 'issue-type-test-case') + work_item_types.find_or_create_by!(name: 'Requirement', namespace_id: nil, base_type: base_types[:requirement], icon_name: 'issue-type-requirements') reversible_migration do |migration| migration.before -> { - expect(work_item_types.all.pluck(:base_type)).to match_array(WorkItem::Type.base_types.values) + expect(work_item_types.all.pluck(:base_type)).to match_array(base_types.values) } migration.after -> { expect(work_item_types.count).to eq(4) - expect(work_item_types.all.pluck(:base_type)).to match_array(WorkItem::Type.base_types.values) + expect(work_item_types.all.pluck(:base_type)).to match_array(base_types.values) } end end diff --git a/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb b/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb new file mode 100644 index 00000000000..b80e4703f07 --- /dev/null +++ b/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe AddTaskToWorkItemTypes, :migration do + include MigrationHelpers::WorkItemTypesHelper + + let_it_be(:work_item_types) { table(:work_item_types) } + + let(:base_types) do + { + issue: 0, + incident: 1, + test_case: 2, + requirement: 3, + task: 4 + } + end + + after(:all) do + # Make sure base types are recreated after running the migration + # because migration specs are not run in a transaction + reset_work_item_types + end + + it 'skips creating the record if it already exists' do + reset_db_state_prior_to_migration + work_item_types.find_or_create_by!(name: 'Task', namespace_id: nil, base_type: base_types[:task], icon_name: 'issue-type-task') + + expect do + migrate! + end.to not_change(work_item_types, :count) + end + + it 'adds task to base work item types' do + reset_db_state_prior_to_migration + + expect do + migrate! + end.to change(work_item_types, :count).from(4).to(5) + + expect(work_item_types.all.pluck(:base_type)).to include(base_types[:task]) + end + + def reset_db_state_prior_to_migration + # Database needs to be in a similar state as when this migration was created + work_item_types.delete_all + work_item_types.find_or_create_by!(name: 'Issue', namespace_id: nil, base_type: base_types[:issue], icon_name: 'issue-type-issue') + work_item_types.find_or_create_by!(name: 'Incident', namespace_id: nil, base_type: base_types[:incident], icon_name: 'issue-type-incident') + work_item_types.find_or_create_by!(name: 'Test Case', namespace_id: nil, base_type: base_types[:test_case], icon_name: 'issue-type-test-case') + work_item_types.find_or_create_by!(name: 'Requirement', namespace_id: nil, base_type: base_types[:requirement], icon_name: 'issue-type-requirements') + end +end diff --git a/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb b/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb new file mode 100644 index 00000000000..2e1289c58f7 --- /dev/null +++ b/spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe AddIndexToProjectsOnMarkedForDeletionAt do + it 'correctly migrates up and down' do + reversible_migration do |migration| + migration.before -> { + expect(ActiveRecord::Base.connection.indexes('projects').map(&:name)).not_to include('index_projects_not_aimed_for_deletion') + } + + migration.after -> { + expect(ActiveRecord::Base.connection.indexes('projects').map(&:name)).to include('index_projects_not_aimed_for_deletion') + } + end + end +end diff --git a/spec/models/work_item/type_spec.rb b/spec/models/work_item/type_spec.rb index dd5324d63a0..cc18558975b 100644 --- a/spec/models/work_item/type_spec.rb +++ b/spec/models/work_item/type_spec.rb @@ -19,10 +19,10 @@ RSpec.describe WorkItem::Type do it 'deletes type but not unrelated issues' do type = create(:work_item_type) - expect(WorkItem::Type.count).to eq(5) + expect(WorkItem::Type.count).to eq(6) expect { type.destroy! }.not_to change(Issue, :count) - expect(WorkItem::Type.count).to eq(4) + expect(WorkItem::Type.count).to eq(5) end end diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb index 692dc8bfd93..fab4c78ce7a 100644 --- a/spec/requests/api/graphql/ci/jobs_spec.rb +++ b/spec/requests/api/graphql/ci/jobs_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'Query.project.pipeline' do create(:ci_build_need, build: test_job, name: 'my test job') end - it 'reports the build needs and execution requirements' do + it 'reports the build needs and execution requirements', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/347290' do post_graphql(query, current_user: user) expect(jobs_graphql_data).to contain_exactly( diff --git a/spec/support/helpers/migrations_helpers/work_item_types_helper.rb b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb new file mode 100644 index 00000000000..59b1f1b1305 --- /dev/null +++ b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module MigrationHelpers + module WorkItemTypesHelper + DEFAULT_WORK_ITEM_TYPES = { + issue: { name: 'Issue', icon_name: 'issue-type-issue', enum_value: 0 }, + incident: { name: 'Incident', icon_name: 'issue-type-incident', enum_value: 1 }, + test_case: { name: 'Test Case', icon_name: 'issue-type-test-case', enum_value: 2 }, + requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 }, + task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 } + }.freeze + + def reset_work_item_types + work_item_types_table.delete_all + + DEFAULT_WORK_ITEM_TYPES.each do |type, attributes| + work_item_types_table.create!(base_type: attributes[:enum_value], **attributes.slice(:name, :icon_name)) + end + end + + private + + def work_item_types_table + table(:work_item_types) + end + end +end