Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
eab843a2f5
commit
1b7a8ceadb
40 changed files with 423 additions and 50 deletions
|
@ -12,7 +12,10 @@ The description of the deprecation should state what actions the user should tak
|
|||
|
||||
### Breaking Change
|
||||
|
||||
<!-- Is this a breaking change or not? If so, please add instructions for how users can update their workflow. -->
|
||||
<!-- Does this MR contain a breaking change? If yes:
|
||||
- Add the ~"breaking change" label to this issue.
|
||||
- Add instructions for how users can update their workflow. -->
|
||||
|
||||
### Affected Topology
|
||||
|
||||
<!--
|
||||
|
@ -52,3 +55,20 @@ Which tier is this feature available in?
|
|||
<!--
|
||||
Add links to any relevant documentation or code that will provide additional details or clarity regarding the planned change. Also, include a link to the removal issue if relevant.
|
||||
-->
|
||||
|
||||
<!-- Label reminders - you should have one of each of the following labels.
|
||||
Use the following resources to find the appropriate labels:
|
||||
- https://gitlab.com/gitlab-org/gitlab/-/labels
|
||||
- https://about.gitlab.com/handbook/product/categories/features/
|
||||
-->
|
||||
|
||||
<!-- Populate the Section, Group, and Category -->
|
||||
/label ~devops:: ~group: ~Category:
|
||||
|
||||
<!-- Choose the Pricing Tier(s) -->
|
||||
/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate"
|
||||
|
||||
<!-- Identifies that this Issue is related to deprecating a feature -->
|
||||
/label ~"type::deprecation"
|
||||
|
||||
<!-- Add the ~"breaking change" label to this issue if necessary -->
|
|
@ -38,7 +38,13 @@ export default {
|
|||
<div v-if="loading && !error" class="text-center loading">
|
||||
<gl-loading-icon class="mt-5" size="lg" />
|
||||
</div>
|
||||
<pdf-lab v-if="!loadError" :pdf="pdf" @pdflabload="onLoad" @pdflaberror="onError" />
|
||||
<pdf-lab
|
||||
v-if="!loadError"
|
||||
:pdf="pdf"
|
||||
@pdflabload="onLoad"
|
||||
@pdflaberror="onError"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<p v-if="error" class="text-center">
|
||||
<span v-if="loadError" ref="loadError">
|
||||
{{ __('An error occurred while loading the file. Please try again later.') }}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -40,6 +40,7 @@ export const viewerProps = (type, blob) => {
|
|||
},
|
||||
pdf: {
|
||||
url: blob.rawPath,
|
||||
fileSize: blob.rawSize,
|
||||
},
|
||||
}[type];
|
||||
};
|
||||
|
|
|
@ -1,16 +1,50 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import PdfViewer from '~/blob/pdf/pdf_viewer.vue';
|
||||
import { __ } from '~/locale';
|
||||
import { PDF_MAX_FILE_SIZE, PDF_MAX_PAGE_LIMIT } from '../../constants';
|
||||
|
||||
export default {
|
||||
components: { PdfViewer },
|
||||
components: { GlButton, PdfViewer },
|
||||
i18n: {
|
||||
tooLargeDescription: __('This PDF is too large to display. Please download to view.'),
|
||||
tooLargeButtonText: __('Download PDF'),
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
fileSize: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { totalPages: 0 };
|
||||
},
|
||||
computed: {
|
||||
tooLargeToDisplay() {
|
||||
return this.fileSize > PDF_MAX_FILE_SIZE || this.totalPages > PDF_MAX_PAGE_LIMIT;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleOnLoad(totalPages) {
|
||||
this.totalPages = totalPages;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<pdf-viewer :pdf="url" />
|
||||
<div>
|
||||
<pdf-viewer v-if="!tooLargeToDisplay" :pdf="url" @pdflabload="handleOnLoad" />
|
||||
|
||||
<div v-else class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-p-5">
|
||||
<p>{{ $options.i18n.tooLargeDescription }}</p>
|
||||
|
||||
<gl-button icon="download" category="secondary" variant="confirm" :href="url" download>{{
|
||||
$options.i18n.tooLargeButtonText
|
||||
}}</gl-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -149,7 +149,6 @@
|
|||
.commit-content {
|
||||
padding-right: 10px;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
|
||||
.commit-title {
|
||||
display: flex;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
31
db/migrate/20211126204445_add_task_to_work_item_types.rb
Normal file
31
db/migrate/20211126204445_add_task_to_work_item_types.rb
Normal file
|
@ -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
|
|
@ -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
|
|
@ -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
|
1
db/schema_migrations/20211126204445
Normal file
1
db/schema_migrations/20211126204445
Normal file
|
@ -0,0 +1 @@
|
|||
e31592bbeb6ba6175f19cfceaafb37672633028dd021052542909999b46eac38
|
1
db/schema_migrations/20211201061733
Normal file
1
db/schema_migrations/20211201061733
Normal file
|
@ -0,0 +1 @@
|
|||
c5282e48f31c0896a3ce21fe238eb602dc006b0bfe62aa4f12ee39bbd620c76c
|
1
db/schema_migrations/20211203091642
Normal file
1
db/schema_migrations/20211203091642
Normal file
|
@ -0,0 +1 @@
|
|||
9954fb041a3f284f53cc9c5c68b1a9dff36513a1851e663c221eccd40736fb16
|
|
@ -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);
|
||||
|
|
BIN
doc/development/documentation/styleguide/img/callouts.png
Normal file
BIN
doc/development/documentation/styleguide/img/callouts.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ module QA
|
|||
class GroupBase < Base
|
||||
include Members
|
||||
|
||||
attr_accessor :path
|
||||
attr_accessor :path, :avatar
|
||||
|
||||
attributes :id,
|
||||
:runners_token,
|
||||
|
|
|
@ -69,7 +69,8 @@ module QA
|
|||
{
|
||||
path: path,
|
||||
name: path,
|
||||
visibility: 'public'
|
||||
visibility: 'public',
|
||||
avatar: avatar
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue