Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-23 21:13:22 +00:00
parent 3acaaf7231
commit 0978fc4c40
63 changed files with 468 additions and 320 deletions

View File

@ -3,7 +3,7 @@ import { EditorContent as TiptapEditorContent } from '@tiptap/vue-2';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { VARIANT_DANGER } from '~/flash'; import { VARIANT_DANGER } from '~/flash';
import { createContentEditor } from '../services/create_content_editor'; import { createContentEditor } from '../services/create_content_editor';
import { ALERT_EVENT } from '../constants'; import { ALERT_EVENT, TIPTAP_AUTOFOCUS_OPTIONS } from '../constants';
import ContentEditorAlert from './content_editor_alert.vue'; import ContentEditorAlert from './content_editor_alert.vue';
import ContentEditorProvider from './content_editor_provider.vue'; import ContentEditorProvider from './content_editor_provider.vue';
import EditorStateObserver from './editor_state_observer.vue'; import EditorStateObserver from './editor_state_observer.vue';
@ -51,6 +51,12 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
autofocus: {
type: [String, Boolean],
required: false,
default: false,
validator: (autofocus) => TIPTAP_AUTOFOCUS_OPTIONS.includes(autofocus),
},
}, },
data() { data() {
return { return {
@ -67,7 +73,7 @@ export default {
}, },
}, },
created() { created() {
const { renderMarkdown, uploadsPath, extensions, serializerConfig } = this; const { renderMarkdown, uploadsPath, extensions, serializerConfig, autofocus } = this;
// This is a non-reactive attribute intentionally since this is a complex object. // This is a non-reactive attribute intentionally since this is a complex object.
this.contentEditor = createContentEditor({ this.contentEditor = createContentEditor({
@ -75,6 +81,9 @@ export default {
uploadsPath, uploadsPath,
extensions, extensions,
serializerConfig, serializerConfig,
tiptapOptions: {
autofocus,
},
}); });
}, },
mounted() { mounted() {

View File

@ -66,3 +66,5 @@ export const SAFE_VIDEO_EXT = ['mp4', 'm4v', 'mov', 'webm', 'ogv'];
export const SAFE_AUDIO_EXT = ['mp3', 'oga', 'ogg', 'spx', 'wav']; export const SAFE_AUDIO_EXT = ['mp3', 'oga', 'ogg', 'spx', 'wav'];
export const DIAGRAM_LANGUAGES = ['plantuml', 'mermaid']; export const DIAGRAM_LANGUAGES = ['plantuml', 'mermaid'];
export const TIPTAP_AUTOFOCUS_OPTIONS = [true, false, 'start', 'end', 'all'];

View File

@ -299,7 +299,7 @@ export default {
:uploads-path="pageInfo.uploadsPath" :uploads-path="pageInfo.uploadsPath"
:enable-content-editor="isMarkdownFormat" :enable-content-editor="isMarkdownFormat"
:enable-preview="isMarkdownFormat" :enable-preview="isMarkdownFormat"
:autofocus="pageInfo.persisted" :init-on-autofocus="pageInfo.persisted"
:form-field-placeholder="$options.i18n.content.placeholder" :form-field-placeholder="$options.i18n.content.placeholder"
:form-field-aria-label="$options.i18n.content.label" :form-field-aria-label="$options.i18n.content.label"
form-field-id="wiki_content" form-field-id="wiki_content"

View File

@ -31,7 +31,8 @@ export default {
}, },
uploadsPath: { uploadsPath: {
type: String, type: String,
required: true, required: false,
default: () => window.uploads_path,
}, },
enableContentEditor: { enableContentEditor: {
type: Boolean, type: Boolean,
@ -56,11 +57,6 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
autofocus: {
type: Boolean,
required: false,
default: false,
},
formFieldPlaceholder: { formFieldPlaceholder: {
type: String, type: String,
required: false, required: false,
@ -71,17 +67,30 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
initOnAutofocus: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
editingMode: EDITING_MODE_MARKDOWN_FIELD, editingMode: EDITING_MODE_MARKDOWN_FIELD,
switchEditingControlEnabled: true, switchEditingControlEnabled: true,
autofocus: this.initOnAutofocus,
}; };
}, },
computed: { computed: {
isContentEditorActive() { isContentEditorActive() {
return this.enableContentEditor && this.editingMode === EDITING_MODE_CONTENT_EDITOR; return this.enableContentEditor && this.editingMode === EDITING_MODE_CONTENT_EDITOR;
}, },
contentEditorAutofocus() {
// Match textarea focus behavior
return this.autofocus ? 'end' : false;
},
},
mounted() {
this.autofocusTextarea(this.editingMode);
}, },
methods: { methods: {
updateMarkdownFromContentEditor({ markdown }) { updateMarkdownFromContentEditor({ markdown }) {
@ -99,9 +108,25 @@ export default {
renderMarkdown(markdown) { renderMarkdown(markdown) {
return axios.post(this.renderMarkdownPath, { text: markdown }).then(({ data }) => data.body); return axios.post(this.renderMarkdownPath, { text: markdown }).then(({ data }) => data.body);
}, },
onEditingModeChange(editingMode) {
this.notifyEditingModeChange(editingMode);
this.enableAutofocus(editingMode);
},
onEditingModeRestored(editingMode) {
this.notifyEditingModeChange(editingMode);
},
notifyEditingModeChange(editingMode) { notifyEditingModeChange(editingMode) {
this.$emit(editingMode); this.$emit(editingMode);
}, },
enableAutofocus(editingMode) {
this.autofocus = true;
this.autofocusTextarea(editingMode);
},
autofocusTextarea(editingMode) {
if (this.autofocus && editingMode === EDITING_MODE_MARKDOWN_FIELD) {
this.$refs.textarea.focus();
}
},
}, },
switchEditingControlOptions: [ switchEditingControlOptions: [
{ text: __('Source'), value: EDITING_MODE_MARKDOWN_FIELD }, { text: __('Source'), value: EDITING_MODE_MARKDOWN_FIELD },
@ -119,13 +144,13 @@ export default {
class="gl-display-flex" class="gl-display-flex"
:options="$options.switchEditingControlOptions" :options="$options.switchEditingControlOptions"
:disabled="!enableContentEditor || !switchEditingControlEnabled" :disabled="!enableContentEditor || !switchEditingControlEnabled"
@change="notifyEditingModeChange" @change="onEditingModeChange"
/> />
</div> </div>
<local-storage-sync <local-storage-sync
v-model="editingMode" v-model="editingMode"
storage-key="gl-wiki-content-editor-enabled" storage-key="gl-wiki-content-editor-enabled"
@input="notifyEditingModeChange" @input="onEditingModeRestored"
/> />
<markdown-field <markdown-field
v-if="!isContentEditorActive" v-if="!isContentEditorActive"
@ -148,7 +173,6 @@ export default {
dir="auto" dir="auto"
data-supports-quick-actions="false" data-supports-quick-actions="false"
data-qa-selector="markdown_editor_form_field" data-qa-selector="markdown_editor_form_field"
:autofocus="autofocus"
:aria-label="formFieldAriaLabel" :aria-label="formFieldAriaLabel"
:placeholder="formFieldPlaceholder" :placeholder="formFieldPlaceholder"
@input="updateMarkdownFromMarkdownField" @input="updateMarkdownFromMarkdownField"
@ -161,6 +185,7 @@ export default {
:render-markdown="renderMarkdown" :render-markdown="renderMarkdown"
:uploads-path="uploadsPath" :uploads-path="uploadsPath"
:markdown="value" :markdown="value"
:autofocus="contentEditorAutofocus"
@change="updateMarkdownFromContentEditor" @change="updateMarkdownFromContentEditor"
@loading="disableSwitchEditingControl" @loading="disableSwitchEditingControl"
@loadingSuccess="enableSwitchEditingControl" @loadingSuccess="enableSwitchEditingControl"

View File

@ -856,7 +856,6 @@ module Ci
variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref) variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug) variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug)
variables.append(key: 'CI_COMMIT_BRANCH', value: ref) if branch? variables.append(key: 'CI_COMMIT_BRANCH', value: ref) if branch?
variables.append(key: 'CI_COMMIT_TAG', value: ref) if tag?
variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s) variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s)
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s) variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s) variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
@ -869,7 +868,8 @@ module Ci
variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha) variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref) variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref)
variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug) variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug)
variables.append(key: 'CI_BUILD_TAG', value: ref) if tag?
variables.concat(predefined_commit_tag_variables)
end end
end end
end end
@ -894,6 +894,20 @@ module Ci
end end
end end
def predefined_commit_tag_variables
strong_memoize(:predefined_commit_ref_variables) do
Gitlab::Ci::Variables::Collection.new.tap do |variables|
next variables unless tag?
variables.append(key: 'CI_COMMIT_TAG', value: ref)
variables.append(key: 'CI_COMMIT_TAG_MESSAGE', value: project.repository.find_tag(ref).message)
# legacy variable
variables.append(key: 'CI_BUILD_TAG', value: ref)
end
end
end
def queued_duration def queued_duration
return unless started_at return unless started_at

View File

@ -77,7 +77,7 @@
= _("The maximum file size in megabytes for individual job artifacts.") = _("The maximum file size in megabytes for individual job artifacts.")
= link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size'), target: '_blank', rel: 'noopener noreferrer' = link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size'), target: '_blank', rel: 'noopener noreferrer'
= f.submit _('Save changes'), class: "btn gl-button btn-confirm" = f.submit _('Save changes'), pajamas_button: true
%hr %hr

View File

@ -1,12 +1,13 @@
- return unless custom_attributes.present? - return unless custom_attributes.present?
.card = render Pajamas::CardComponent.new(body_options: { class: 'gl-py-0' }) do |c|
.card-header - c.header do
= link_to(_('Custom Attributes'), help_page_path('api/custom_attributes.md')) = link_to(_('Custom Attributes'), help_page_path('api/custom_attributes.md'))
%ul.content-list - c.body do
- custom_attributes.each do |custom_attribute| %ul.content-list
%li - custom_attributes.each do |custom_attribute|
%span.light %li
= custom_attribute.key %span.light
%strong = custom_attribute.key
= custom_attribute.value %strong
= custom_attribute.value

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class FinalizeTaskSystemNoteRenaming < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
MIGRATION = 'RenameTaskSystemNoteToChecklistItem'
def up
ensure_batched_background_migration_is_finished(
job_class_name: MIGRATION,
table_name: :system_note_metadata,
column_name: :id,
job_arguments: []
)
end
def down
# noop
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class RemoveTaskSystemNoteRenameTempIndex < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'tmp_index_system_note_metadata_on_id_where_task'
def up
remove_concurrent_index_by_name :system_note_metadata, INDEX_NAME
end
def down
add_concurrent_index :system_note_metadata, [:id, :action], where: "action = 'task'", name: INDEX_NAME
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class RemoveTraceColumnFromCiBuilds < Gitlab::Database::Migration[2.0]
enable_lock_retries!
def change
remove_column :ci_builds, :trace, :text
end
end

View File

@ -0,0 +1 @@
e683bd10619f9ceee2a5d330ca4d6b9d396ad8934095b707a0eec5d49cdaf2be

View File

@ -0,0 +1 @@
6a4677165bca249c369214aee9fae331fde4dc716544bac93c06f0ee33222455

View File

@ -0,0 +1 @@
6ce159118651ec93989e8f111528ad2134e46f359c483b60239290d677f9f4f4

View File

@ -12601,7 +12601,6 @@ CREATE TABLE ci_build_trace_metadata (
CREATE TABLE ci_builds ( CREATE TABLE ci_builds (
status character varying, status character varying,
finished_at timestamp without time zone, finished_at timestamp without time zone,
trace text,
created_at timestamp without time zone, created_at timestamp without time zone,
updated_at timestamp without time zone, updated_at timestamp without time zone,
started_at timestamp without time zone, started_at timestamp without time zone,
@ -30910,8 +30909,6 @@ CREATE INDEX tmp_index_project_statistics_cont_registry_size ON project_statisti
CREATE INDEX tmp_index_system_note_metadata_on_attention_request_actions ON system_note_metadata USING btree (id) WHERE ((action)::text = ANY ((ARRAY['attention_requested'::character varying, 'attention_request_removed'::character varying])::text[])); CREATE INDEX tmp_index_system_note_metadata_on_attention_request_actions ON system_note_metadata USING btree (id) WHERE ((action)::text = ANY ((ARRAY['attention_requested'::character varying, 'attention_request_removed'::character varying])::text[]));
CREATE INDEX tmp_index_system_note_metadata_on_id_where_task ON system_note_metadata USING btree (id, action) WHERE ((action)::text = 'task'::text);
CREATE INDEX tmp_index_vulnerability_occurrences_on_id_and_scanner_id ON vulnerability_occurrences USING btree (id, scanner_id) WHERE (report_type = ANY (ARRAY[7, 99])); CREATE INDEX tmp_index_vulnerability_occurrences_on_id_and_scanner_id ON vulnerability_occurrences USING btree (id, scanner_id) WHERE (report_type = ANY (ARRAY[7, 99]));
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name); CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);

View File

@ -27,8 +27,9 @@ Features behind flags can be gradually rolled out, typically:
1. The feature flag is removed. 1. The feature flag is removed.
These features can be enabled and disabled to allow or prevent users from using These features can be enabled and disabled to allow or prevent users from using
them. It can be done by GitLab administrators with access to GitLab Rails them. It can be done by GitLab administrators with access to the
console. [Rails console](#how-to-enable-and-disable-features-behind-flags) or the
[Feature flags API](../api/features.md).
When you disable a feature flag, the feature is hidden from users and all of the functionality is turned off. When you disable a feature flag, the feature is hidden from users and all of the functionality is turned off.
For example, data is not recorded and services do not run. For example, data is not recorded and services do not run.

View File

@ -22,7 +22,8 @@ a database schema.
Adding a `:migration` tag to a test signature enables some custom RSpec Adding a `:migration` tag to a test signature enables some custom RSpec
`before` and `after` hooks in our `before` and `after` hooks in our
[`spec/support/migration.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/f81fa6ab1dd788b70ef44b85aaba1f31ffafae7d/spec/support/migration.rb) [`spec/support/migration.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/f81fa6ab1dd788b70ef44b85aaba1f31ffafae7d/spec/support/migration.rb)
to run. to run. If performing a migration against a database schema other than
`:gitlab_main` (for example `:gitlab_ci`), then you must specify it as well: `migration: :gitlab_ci`. See [spec/migrations/change_public_projects_cost_factor_spec.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/migrations/change_public_projects_cost_factor_spec.rb#L6-6) for an example.
A `before` hook reverts all migrations to the point that a migration A `before` hook reverts all migrations to the point that a migration
under test is not yet migrated. under test is not yet migrated.

View File

@ -19,14 +19,14 @@ Group push rules allow group maintainers to set
In GitLab 15.4 and later, to configure push rules for a group: In GitLab 15.4 and later, to configure push rules for a group:
1. On the left sidebar, select **Push rules**. 1. On the left sidebar, select **Settings > Repository**.
1. Expand the **Pre-defined push rules** section.
1. Select the settings you want. 1. Select the settings you want.
1. Select **Save Push Rules**. 1. Select **Save Push Rules**.
In GitLab 15.3 and earlier, to configure push rules for a group: In GitLab 15.3 and earlier, to configure push rules for a group:
1. On the left sidebar, select **Settings > Repository** page. 1. On the left sidebar, select **Push rules**.
1. Expand the **Pre-defined push rules** section.
1. Select the settings you want. 1. Select the settings you want.
1. Select **Save Push Rules**. 1. Select **Save Push Rules**.

View File

@ -27,7 +27,7 @@ module Gitlab
end end
def id_for_already_imported_cache(note) def id_for_already_imported_cache(note)
note.id note[:id]
end end
end end
end end

View File

@ -27,7 +27,7 @@ module Gitlab
end end
def id_for_already_imported_cache(event) def id_for_already_imported_cache(event)
event.id event[:id]
end end
end end
end end

View File

@ -33,7 +33,7 @@ module Gitlab
end end
def id_for_already_imported_cache(issue) def id_for_already_imported_cache(issue)
issue.number issue[:number]
end end
def collection_options def collection_options

View File

@ -22,7 +22,7 @@ module Gitlab
end end
def already_imported?(label) def already_imported?(label)
existing_labels.include?(label.name) existing_labels.include?(label[:name])
end end
def build_labels_cache def build_labels_cache
@ -33,8 +33,8 @@ module Gitlab
time = Time.zone.now time = Time.zone.now
{ {
title: label.name, title: label[:name],
color: '#' + label.color, color: '#' + label[:color],
project_id: project.id, project_id: project.id,
type: 'ProjectLabel', type: 'ProjectLabel',
created_at: time, created_at: time,

View File

@ -22,7 +22,7 @@ module Gitlab
end end
def already_imported?(milestone) def already_imported?(milestone)
existing_milestones.include?(milestone.number) existing_milestones.include?(milestone[:number])
end end
def build_milestones_cache def build_milestones_cache
@ -31,19 +31,19 @@ module Gitlab
def build(milestone) def build(milestone)
{ {
iid: milestone.number, iid: milestone[:number],
title: milestone.title, title: milestone[:title],
description: milestone.description, description: milestone[:description],
project_id: project.id, project_id: project.id,
state: state_for(milestone), state: state_for(milestone),
due_date: milestone.due_on&.to_date, due_date: milestone[:due_on]&.to_date,
created_at: milestone.created_at, created_at: milestone[:created_at],
updated_at: milestone.updated_at updated_at: milestone[:updated_at]
} }
end end
def state_for(milestone) def state_for(milestone)
milestone.state == 'open' ? :active : :closed milestone[:state] == 'open' ? :active : :closed
end end
def each_milestone def each_milestone

View File

@ -27,7 +27,7 @@ module Gitlab
end end
def id_for_already_imported_cache(note) def id_for_already_imported_cache(note)
note.id note[:id]
end end
end end
end end

View File

@ -19,7 +19,7 @@ module Gitlab
end end
def id_for_already_imported_cache(pr) def id_for_already_imported_cache(pr)
pr.number pr[:number]
end end
def object_type def object_type
@ -55,11 +55,11 @@ module Gitlab
def update_repository?(pr) def update_repository?(pr)
last_update = project.last_repository_updated_at || project.created_at last_update = project.last_repository_updated_at || project.created_at
return false if pr.updated_at < last_update return false if pr[:updated_at] < last_update
# PRs may be updated without there actually being new commits, thus we # PRs may be updated without there actually being new commits, thus we
# check to make sure we only re-fetch if truly necessary. # check to make sure we only re-fetch if truly necessary.
!(commit_exists?(pr.head.sha) && commit_exists?(pr.base.sha)) !(commit_exists?(pr.dig(:head, :sha)) && commit_exists?(pr.dig(:base, :sha)))
end end
def commit_exists?(sha) def commit_exists?(sha)

View File

@ -34,7 +34,7 @@ module Gitlab
end end
def id_for_already_imported_cache(review) def id_for_already_imported_cache(review)
review.id review[:id]
end end
# The worker can be interrupted, by rate limit for instance, # The worker can be interrupted, by rate limit for instance,
@ -52,7 +52,7 @@ module Gitlab
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched) Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
review.merge_request_id = merge_request.id review[:merge_request_id] = merge_request.id
yield(review) yield(review)
mark_as_imported(review) mark_as_imported(review)

View File

@ -21,21 +21,21 @@ module Gitlab
end end
def already_imported?(release) def already_imported?(release)
existing_tags.include?(release.tag_name) || release.tag_name.nil? existing_tags.include?(release[:tag_name]) || release[:tag_name].nil?
end end
def build(release) def build(release)
existing_tags.add(release.tag_name) existing_tags.add(release[:tag_name])
{ {
name: release.name, name: release[:name],
tag: release.tag_name, tag: release[:tag_name],
author_id: fetch_author_id(release), author_id: fetch_author_id(release),
description: description_for(release), description: description_for(release),
created_at: release.created_at, created_at: release[:created_at],
updated_at: release.created_at, updated_at: release[:created_at],
# Draft releases will have a null published_at # Draft releases will have a null published_at
released_at: release.published_at || Time.current, released_at: release[:published_at] || Time.current,
project_id: project.id project_id: project.id
} }
end end
@ -45,7 +45,7 @@ module Gitlab
end end
def description_for(release) def description_for(release)
release.body.presence || "Release for tag #{release.tag_name}" release[:body].presence || "Release for tag #{release[:tag_name]}"
end end
def object_type def object_type

View File

@ -28,7 +28,7 @@ module Gitlab
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched) Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
pull_request = parent_record.is_a? MergeRequest pull_request = parent_record.is_a? MergeRequest
associated.issue = { 'number' => parent_record.iid, 'pull_request' => pull_request } associated[:issue] = { 'number' => parent_record.iid, 'pull_request' => pull_request }
yield(associated) yield(associated)
mark_as_imported(associated) mark_as_imported(associated)
@ -78,7 +78,7 @@ module Gitlab
end end
def id_for_already_imported_cache(event) def id_for_already_imported_cache(event)
event.id event[:id]
end end
def collection_options def collection_options
@ -87,9 +87,9 @@ module Gitlab
# Cross-referenced events on Github doesn't have id. # Cross-referenced events on Github doesn't have id.
def compose_associated_id!(issuable, event) def compose_associated_id!(issuable, event)
return if event.event != 'cross-referenced' return if event[:event] != 'cross-referenced'
event.id = "cross-reference##{issuable.iid}-in-#{event.source.issue.id}" event[:id] = "cross-reference##{issuable.iid}-in-#{event.dig(:source, :issue, :id)}"
end end
end end
end end

View File

@ -21,31 +21,31 @@ module Gitlab
# #
# note - An instance of `Sawyer::Resource` containing the note details. # note - An instance of `Sawyer::Resource` containing the note details.
def self.from_api_response(note, additional_data = {}) def self.from_api_response(note, additional_data = {})
matches = note.html_url.match(NOTEABLE_ID_REGEX) matches = note[:html_url].match(NOTEABLE_ID_REGEX)
unless matches unless matches
raise( raise(
ArgumentError, ArgumentError,
"The note URL #{note.html_url.inspect} is not supported" "The note URL #{note[:html_url].inspect} is not supported"
) )
end end
user = Representation::User.from_api_response(note.user) if note.user user = Representation::User.from_api_response(note[:user]) if note[:user]
hash = { hash = {
noteable_id: matches[:iid].to_i, noteable_id: matches[:iid].to_i,
file_path: note.path, file_path: note[:path],
commit_id: note.commit_id, commit_id: note[:commit_id],
original_commit_id: note.original_commit_id, original_commit_id: note[:original_commit_id],
diff_hunk: note.diff_hunk, diff_hunk: note[:diff_hunk],
author: user, author: user,
note: note.body, note: note[:body],
created_at: note.created_at, created_at: note[:created_at],
updated_at: note.updated_at, updated_at: note[:updated_at],
note_id: note.id, note_id: note[:id],
end_line: note.line, end_line: note[:line],
start_line: note.start_line, start_line: note[:start_line],
side: note.side, side: note[:side],
in_reply_to_id: note.in_reply_to_id in_reply_to_id: note[:in_reply_to_id]
} }
new(hash) new(hash)

View File

@ -19,24 +19,24 @@ module Gitlab
# details. # details.
def self.from_api_response(issue, additional_data = {}) def self.from_api_response(issue, additional_data = {})
user = user =
if issue.user if issue[:user]
Representation::User.from_api_response(issue.user) Representation::User.from_api_response(issue[:user])
end end
hash = { hash = {
iid: issue.number, iid: issue[:number],
title: issue.title, title: issue[:title],
description: issue.body, description: issue[:body],
milestone_number: issue.milestone&.number, milestone_number: issue.dig(:milestone, :number),
state: issue.state == 'open' ? :opened : :closed, state: issue[:state] == 'open' ? :opened : :closed,
assignees: issue.assignees.map do |u| assignees: issue[:assignees].map do |u|
Representation::User.from_api_response(u) Representation::User.from_api_response(u)
end, end,
label_names: issue.labels.map(&:name), label_names: issue[:labels].map { _1[:name] },
author: user, author: user,
created_at: issue.created_at, created_at: issue[:created_at],
updated_at: issue.updated_at, updated_at: issue[:updated_at],
pull_request: issue.pull_request ? true : false, pull_request: issue[:pull_request] ? true : false,
work_item_type_id: additional_data[:work_item_type_id] work_item_type_id: additional_data[:work_item_type_id]
} }

View File

@ -37,20 +37,20 @@ module Gitlab
# event - An instance of `Sawyer::Resource` containing the event details. # event - An instance of `Sawyer::Resource` containing the event details.
def from_api_response(event, additional_data = {}) def from_api_response(event, additional_data = {})
new( new(
id: event.id, id: event[:id],
actor: user_representation(event.actor), actor: user_representation(event[:actor]),
event: event.event, event: event[:event],
commit_id: event.commit_id, commit_id: event[:commit_id],
label_title: event.label && event.label[:name], label_title: event.dig(:label, :name),
old_title: event.rename && event.rename[:from], old_title: event.dig(:rename, :from),
new_title: event.rename && event.rename[:to], new_title: event.dig(:rename, :to),
milestone_title: event.milestone && event.milestone[:title], milestone_title: event.dig(:milestone, :title),
issue: event.issue&.to_h&.symbolize_keys, issue: event[:issue]&.symbolize_keys,
source: event.source, source: event[:source],
assignee: user_representation(event.assignee), assignee: user_representation(event[:assignee]),
requested_reviewer: user_representation(event.requested_reviewer), requested_reviewer: user_representation(event[:requested_reviewer]),
review_requester: user_representation(event.review_requester), review_requester: user_representation(event[:review_requester]),
created_at: event.created_at created_at: event[:created_at]
) )
end end

View File

@ -18,12 +18,12 @@ module Gitlab
# #
# note - An instance of `Sawyer::Resource` containing the note details. # note - An instance of `Sawyer::Resource` containing the note details.
def self.from_api_response(note, additional_data = {}) def self.from_api_response(note, additional_data = {})
matches = note.html_url.match(NOTEABLE_TYPE_REGEX) matches = note[:html_url].match(NOTEABLE_TYPE_REGEX)
if !matches || !matches[:type] if !matches || !matches[:type]
raise( raise(
ArgumentError, ArgumentError,
"The note URL #{note.html_url.inspect} is not supported" "The note URL #{note[:html_url].inspect} is not supported"
) )
end end
@ -34,15 +34,15 @@ module Gitlab
'Issue' 'Issue'
end end
user = Representation::User.from_api_response(note.user) if note.user user = Representation::User.from_api_response(note[:user]) if note[:user]
hash = { hash = {
noteable_type: noteable_type, noteable_type: noteable_type,
noteable_id: matches[:iid].to_i, noteable_id: matches[:iid].to_i,
author: user, author: user,
note: note.body, note: note[:body],
created_at: note.created_at, created_at: note[:created_at],
updated_at: note.updated_at, updated_at: note[:updated_at],
note_id: note.id note_id: note[:id]
} }
new(hash) new(hash)

View File

@ -19,28 +19,28 @@ module Gitlab
# #
# issue - An instance of `Sawyer::Resource` containing the PR details. # issue - An instance of `Sawyer::Resource` containing the PR details.
def self.from_api_response(pr, additional_data = {}) def self.from_api_response(pr, additional_data = {})
assignee = Representation::User.from_api_response(pr.assignee) if pr.assignee assignee = Representation::User.from_api_response(pr[:assignee]) if pr[:assignee]
user = Representation::User.from_api_response(pr.user) if pr.user user = Representation::User.from_api_response(pr[:user]) if pr[:user]
merged_by = Representation::User.from_api_response(pr.merged_by) if pr.merged_by merged_by = Representation::User.from_api_response(pr[:merged_by]) if pr[:merged_by]
hash = { hash = {
iid: pr.number, iid: pr[:number],
title: pr.title, title: pr[:title],
description: pr.body, description: pr[:body],
source_branch: pr.head.ref, source_branch: pr.dig(:head, :ref),
target_branch: pr.base.ref, target_branch: pr.dig(:base, :ref),
source_branch_sha: pr.head.sha, source_branch_sha: pr.dig(:head, :sha),
target_branch_sha: pr.base.sha, target_branch_sha: pr.dig(:base, :sha),
source_repository_id: pr.head&.repo&.id, source_repository_id: pr.dig(:head, :repo, :id),
target_repository_id: pr.base&.repo&.id, target_repository_id: pr.dig(:base, :repo, :id),
source_repository_owner: pr.head&.user&.login, source_repository_owner: pr.dig(:head, :user, :login),
state: pr.state == 'open' ? :opened : :closed, state: pr[:state] == 'open' ? :opened : :closed,
milestone_number: pr.milestone&.number, milestone_number: pr.dig(:milestone, :number),
author: user, author: user,
assignee: assignee, assignee: assignee,
created_at: pr.created_at, created_at: pr[:created_at],
updated_at: pr.updated_at, updated_at: pr[:updated_at],
merged_at: pr.merged_at, merged_at: pr[:merged_at],
merged_by: merged_by merged_by: merged_by
} }

View File

@ -11,16 +11,19 @@ module Gitlab
expose_attribute :author, :note, :review_type, :submitted_at, :merge_request_id, :review_id expose_attribute :author, :note, :review_type, :submitted_at, :merge_request_id, :review_id
# Builds a PullRequestReview from a GitHub API response.
#
# review - An instance of `Sawyer::Resource` containing the note details.
def self.from_api_response(review, additional_data = {}) def self.from_api_response(review, additional_data = {})
user = Representation::User.from_api_response(review.user) if review.user user = Representation::User.from_api_response(review[:user]) if review[:user]
new( new(
merge_request_id: review.merge_request_id, merge_request_id: review[:merge_request_id],
author: user, author: user,
note: review.body, note: review[:body],
review_type: review.state, review_type: review[:state],
submitted_at: review.submitted_at, submitted_at: review[:submitted_at],
review_id: review.id review_id: review[:id]
) )
end end

View File

@ -16,8 +16,8 @@ module Gitlab
# user - An instance of `Sawyer::Resource` containing the user details. # user - An instance of `Sawyer::Resource` containing the user details.
def self.from_api_response(user, additional_data = {}) def self.from_api_response(user, additional_data = {})
new( new(
id: user.id, id: user[:id],
login: user.login login: user[:login]
) )
end end

View File

@ -32,7 +32,7 @@ module Gitlab
end end
def id_for_already_imported_cache(associated) def id_for_already_imported_cache(associated)
associated.id associated[:id]
end end
def parent_collection def parent_collection

View File

@ -39,18 +39,19 @@ module Gitlab
# #
# If the object has no author ID we'll use the ID of the GitLab ghost # If the object has no author ID we'll use the ID of the GitLab ghost
# user. # user.
# object - An instance of `Sawyer::Resource` or a `Github::Representer`
def author_id_for(object, author_key: :author) def author_id_for(object, author_key: :author)
user_info = case author_key user_info = case author_key
when :actor when :actor
object&.actor object[:actor]
when :assignee when :assignee
object&.assignee object[:assignee]
when :requested_reviewer when :requested_reviewer
object&.requested_reviewer object[:requested_reviewer]
when :review_requester when :review_requester
object&.review_requester object[:review_requester]
else else
object&.author object ? object[:author] : nil
end end
id = user_info ? user_id_for(user_info) : GithubImport.ghost_user_id id = user_info ? user_id_for(user_info) : GithubImport.ghost_user_id
@ -64,14 +65,14 @@ module Gitlab
# Returns the GitLab user ID of an issuable's assignee. # Returns the GitLab user ID of an issuable's assignee.
def assignee_id_for(issuable) def assignee_id_for(issuable)
user_id_for(issuable.assignee) if issuable.assignee user_id_for(issuable[:assignee]) if issuable[:assignee]
end end
# Returns the GitLab user ID for a GitHub user. # Returns the GitLab user ID for a GitHub user.
# #
# user - An instance of `Gitlab::GithubImport::Representation::User`. # user - An instance of `Gitlab::GithubImport::Representation::User` or `Sawyer::Resource`.
def user_id_for(user) def user_id_for(user)
find(user.id, user.login) if user.present? find(user[:id], user[:login]) if user.present?
end end
# Returns the GitLab ID for the given GitHub ID or username. # Returns the GitLab ID for the given GitHub ID or username.
@ -114,7 +115,7 @@ module Gitlab
unless email unless email
user = client.user(username) user = client.user(username)
email = Gitlab::Cache::Import::Caching.write(cache_key, user.email, timeout: timeout(user.email)) if user email = Gitlab::Cache::Import::Caching.write(cache_key, user[:email], timeout: timeout(user[:email])) if user
end end
email email

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/sh
# This is based on https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/bin/slack # This is based on https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/bin/slack
# #
# Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set). # Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set).

View File

@ -26,12 +26,13 @@ describe('ContentEditor', () => {
const findEditorStateObserver = () => wrapper.findComponent(EditorStateObserver); const findEditorStateObserver = () => wrapper.findComponent(EditorStateObserver);
const findLoadingIndicator = () => wrapper.findComponent(LoadingIndicator); const findLoadingIndicator = () => wrapper.findComponent(LoadingIndicator);
const findContentEditorAlert = () => wrapper.findComponent(ContentEditorAlert); const findContentEditorAlert = () => wrapper.findComponent(ContentEditorAlert);
const createWrapper = ({ markdown } = {}) => { const createWrapper = ({ markdown, autofocus } = {}) => {
wrapper = shallowMountExtended(ContentEditor, { wrapper = shallowMountExtended(ContentEditor, {
propsData: { propsData: {
renderMarkdown, renderMarkdown,
uploadsPath, uploadsPath,
markdown, markdown,
autofocus,
}, },
stubs: { stubs: {
EditorStateObserver, EditorStateObserver,
@ -70,14 +71,22 @@ describe('ContentEditor', () => {
expect(editorContent.classes()).toContain('md'); expect(editorContent.classes()).toContain('md');
}); });
it('renders ContentEditorProvider component', async () => { it('allows setting the tiptap editor to autofocus', async () => {
await createWrapper(); createWrapper({ autofocus: 'start' });
await nextTick();
expect(findEditorContent().props().editor.options.autofocus).toBe('start');
});
it('renders ContentEditorProvider component', () => {
createWrapper();
expect(wrapper.findComponent(ContentEditorProvider).exists()).toBe(true); expect(wrapper.findComponent(ContentEditorProvider).exists()).toBe(true);
}); });
it('renders top toolbar component', async () => { it('renders top toolbar component', () => {
await createWrapper(); createWrapper();
expect(wrapper.findComponent(TopToolbar).exists()).toBe(true); expect(wrapper.findComponent(TopToolbar).exists()).toBe(true);
}); });

View File

@ -123,7 +123,7 @@ describe('WikiForm', () => {
renderMarkdownPath: pageInfoPersisted.markdownPreviewPath, renderMarkdownPath: pageInfoPersisted.markdownPreviewPath,
markdownDocsPath: pageInfoPersisted.markdownHelpPath, markdownDocsPath: pageInfoPersisted.markdownHelpPath,
uploadsPath: pageInfoPersisted.uploadsPath, uploadsPath: pageInfoPersisted.uploadsPath,
autofocus: pageInfoPersisted.persisted, initOnAutofocus: pageInfoPersisted.persisted,
formFieldId: 'wiki_content', formFieldId: 'wiki_content',
formFieldName: 'wiki[content]', formFieldName: 'wiki[content]',
}), }),

View File

@ -1,12 +1,15 @@
import { GlSegmentedControl } from '@gitlab/ui'; import { GlSegmentedControl } from '@gitlab/ui';
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import { EDITING_MODE_MARKDOWN_FIELD, EDITING_MODE_CONTENT_EDITOR } from '~/vue_shared/constants'; import { EDITING_MODE_MARKDOWN_FIELD, EDITING_MODE_CONTENT_EDITOR } from '~/vue_shared/constants';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue'; import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
import ContentEditor from '~/content_editor/components/content_editor.vue'; import ContentEditor from '~/content_editor/components/content_editor.vue';
import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { stubComponent } from 'helpers/stub_component';
jest.mock('~/emoji'); jest.mock('~/emoji');
@ -15,7 +18,6 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
const value = 'test markdown'; const value = 'test markdown';
const renderMarkdownPath = '/api/markdown'; const renderMarkdownPath = '/api/markdown';
const markdownDocsPath = '/help/markdown'; const markdownDocsPath = '/help/markdown';
const uploadsPath = '/uploads';
const enableAutocomplete = true; const enableAutocomplete = true;
const enablePreview = false; const enablePreview = false;
const formFieldId = 'markdown_field'; const formFieldId = 'markdown_field';
@ -24,13 +26,13 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
const formFieldAriaLabel = 'Edit your content'; const formFieldAriaLabel = 'Edit your content';
let mock; let mock;
const buildWrapper = (propsData = {}) => { const buildWrapper = ({ propsData = {}, attachTo } = {}) => {
wrapper = mountExtended(MarkdownEditor, { wrapper = mountExtended(MarkdownEditor, {
attachTo,
propsData: { propsData: {
value, value,
renderMarkdownPath, renderMarkdownPath,
markdownDocsPath, markdownDocsPath,
uploadsPath,
enableAutocomplete, enableAutocomplete,
enablePreview, enablePreview,
formFieldId, formFieldId,
@ -39,6 +41,9 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
formFieldAriaLabel, formFieldAriaLabel,
...propsData, ...propsData,
}, },
stubs: {
BubbleMenu: stubComponent(BubbleMenu),
},
}); });
}; };
const findSegmentedControl = () => wrapper.findComponent(GlSegmentedControl); const findSegmentedControl = () => wrapper.findComponent(GlSegmentedControl);
@ -48,6 +53,7 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
const findContentEditor = () => wrapper.findComponent(ContentEditor); const findContentEditor = () => wrapper.findComponent(ContentEditor);
beforeEach(() => { beforeEach(() => {
window.uploads_path = 'uploads';
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
}); });
@ -66,7 +72,7 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
enableAutocomplete, enableAutocomplete,
textareaValue: value, textareaValue: value,
markdownDocsPath, markdownDocsPath,
uploadsPath, uploadsPath: window.uploads_path,
enablePreview, enablePreview,
}), }),
); );
@ -129,18 +135,32 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
expect(wrapper.emitted('input')).toEqual([[newValue]]); expect(wrapper.emitted('input')).toEqual([[newValue]]);
}); });
describe('when initOnAutofocus is true', () => {
beforeEach(async () => {
buildWrapper({ attachTo: document.body, propsData: { initOnAutofocus: true } });
await nextTick();
});
it('sets the markdown field as the active element in the document', () => {
expect(document.activeElement).toBe(findTextarea().element);
});
});
describe(`when segmented control triggers input event with ${EDITING_MODE_CONTENT_EDITOR} value`, () => { describe(`when segmented control triggers input event with ${EDITING_MODE_CONTENT_EDITOR} value`, () => {
beforeEach(() => { beforeEach(() => {
buildWrapper(); buildWrapper();
findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR); findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR);
findSegmentedControl().vm.$emit('change', EDITING_MODE_CONTENT_EDITOR);
}); });
it('displays the content editor', () => { it('displays the content editor', () => {
expect(findContentEditor().props()).toEqual( expect(findContentEditor().props()).toEqual(
expect.objectContaining({ expect.objectContaining({
renderMarkdown: expect.any(Function), renderMarkdown: expect.any(Function),
uploadsPath, uploadsPath: window.uploads_path,
markdown: value, markdown: value,
autofocus: 'end',
}), }),
); );
}); });
@ -173,6 +193,17 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR); findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR);
}); });
describe('when initOnAutofocus is true', () => {
beforeEach(() => {
buildWrapper({ propsData: { initOnAutofocus: true } });
findLocalStorageSync().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR);
});
it('sets the content editor autofocus property to end', () => {
expect(findContentEditor().props().autofocus).toBe('end');
});
});
it('emits input event when content editor emits change event', async () => { it('emits input event when content editor emits change event', async () => {
const newValue = 'new value'; const newValue = 'new value';
@ -197,6 +228,19 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
it('updates localStorage value', () => { it('updates localStorage value', () => {
expect(findLocalStorageSync().props().value).toBe(EDITING_MODE_MARKDOWN_FIELD); expect(findLocalStorageSync().props().value).toBe(EDITING_MODE_MARKDOWN_FIELD);
}); });
it('sets the textarea as the activeElement in the document', async () => {
// The component should be rebuilt to attach it to the document body
buildWrapper({ attachTo: document.body });
await findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR);
expect(findContentEditor().exists()).toBe(true);
await findSegmentedControl().vm.$emit('input', EDITING_MODE_MARKDOWN_FIELD);
await findSegmentedControl().vm.$emit('change', EDITING_MODE_MARKDOWN_FIELD);
expect(document.activeElement).toBe(findTextarea().element);
});
}); });
describe('when content editor emits loading event', () => { describe('when content editor emits loading event', () => {

View File

@ -68,8 +68,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
end end
context 'when refs policy is specified' do context 'when refs policy is specified' do
let(:tag_name) { project.repository.tags.first.name }
let(:pipeline) do let(:pipeline) do
build(:ci_pipeline, project: project, ref: 'feature', tag: true) build(:ci_pipeline, project: project, ref: tag_name, tag: true)
end end
let(:config) do let(:config) do

View File

@ -7,14 +7,13 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNotesImporter do
let(:client) { double(:client) } let(:client) { double(:client) }
let(:github_comment) do let(:github_comment) do
double( {
:response,
html_url: 'https://github.com/foo/bar/pull/42', html_url: 'https://github.com/foo/bar/pull/42',
path: 'README.md', path: 'README.md',
commit_id: '123abc', commit_id: '123abc',
original_commit_id: 'original123abc', original_commit_id: 'original123abc',
diff_hunk: "@@ -1 +1 @@\n-Hello\n+Hello world", diff_hunk: "@@ -1 +1 @@\n-Hello\n+Hello world",
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
created_at: Time.zone.now, created_at: Time.zone.now,
updated_at: Time.zone.now, updated_at: Time.zone.now,
line: 23, line: 23,
@ -29,7 +28,7 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNotesImporter do
sug1 sug1
``` ```
BODY BODY
) }
end end
describe '#parallel?' do describe '#parallel?' do

View File

@ -9,20 +9,19 @@ RSpec.describe Gitlab::GithubImport::Importer::IssuesImporter do
let(:updated_at) { Time.new(2017, 1, 1, 12, 15) } let(:updated_at) { Time.new(2017, 1, 1, 12, 15) }
let(:github_issue) do let(:github_issue) do
double( {
:response,
number: 42, number: 42,
title: 'My Issue', title: 'My Issue',
body: 'This is my issue', body: 'This is my issue',
milestone: double(:milestone, number: 4), milestone: { number: 4 },
state: 'open', state: 'open',
assignees: [double(:user, id: 4, login: 'alice')], assignees: [{ id: 4, login: 'alice' }],
labels: [double(:label, name: 'bug')], labels: [{ name: 'bug' }],
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
created_at: created_at, created_at: created_at,
updated_at: updated_at, updated_at: updated_at,
pull_request: false pull_request: false
) }
end end
describe '#parallel?' do describe '#parallel?' do

View File

@ -28,7 +28,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_red
describe '#build_labels' do describe '#build_labels' do
it 'returns an Array containnig label rows' do it 'returns an Array containnig label rows' do
label = double(:label, name: 'bug', color: 'ffffff') label = { name: 'bug', color: 'ffffff' }
expect(importer).to receive(:each_label).and_return([label]) expect(importer).to receive(:each_label).and_return([label])
@ -41,7 +41,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_red
it 'does not create labels that already exist' do it 'does not create labels that already exist' do
create(:label, project: project, title: 'bug') create(:label, project: project, title: 'bug')
label = double(:label, name: 'bug', color: 'ffffff') label = { name: 'bug', color: 'ffffff' }
expect(importer).to receive(:each_label).and_return([label]) expect(importer).to receive(:each_label).and_return([label])
expect(importer.build_labels).to be_empty expect(importer.build_labels).to be_empty
@ -60,7 +60,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_red
describe '#build' do describe '#build' do
let(:label_hash) do let(:label_hash) do
importer.build(double(:label, name: 'bug', color: 'ffffff')) importer.build({ name: 'bug', color: 'ffffff' })
end end
it 'returns the attributes of the label as a Hash' do it 'returns the attributes of the label as a Hash' do

View File

@ -11,8 +11,7 @@ RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab
let(:updated_at) { Time.new(2017, 1, 1, 12, 15) } let(:updated_at) { Time.new(2017, 1, 1, 12, 15) }
let(:milestone) do let(:milestone) do
double( {
:milestone,
number: 1, number: 1,
title: '1.0', title: '1.0',
description: 'The first release', description: 'The first release',
@ -20,12 +19,11 @@ RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab
due_on: due_on, due_on: due_on,
created_at: created_at, created_at: created_at,
updated_at: updated_at updated_at: updated_at
) }
end end
let(:milestone2) do let(:milestone2) do
double( {
:milestone,
number: 1, number: 1,
title: '1.0', title: '1.0',
description: 'The first release', description: 'The first release',
@ -33,7 +31,7 @@ RSpec.describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab
due_on: nil, due_on: nil,
created_at: created_at, created_at: created_at,
updated_at: updated_at updated_at: updated_at
) }
end end
describe '#execute' do describe '#execute' do

View File

@ -7,15 +7,14 @@ RSpec.describe Gitlab::GithubImport::Importer::NotesImporter do
let(:client) { double(:client) } let(:client) { double(:client) }
let(:github_comment) do let(:github_comment) do
double( {
:response,
html_url: 'https://github.com/foo/bar/issues/42', html_url: 'https://github.com/foo/bar/issues/42',
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
body: 'Hello world', body: 'Hello world',
created_at: Time.zone.now, created_at: Time.zone.now,
updated_at: Time.zone.now, updated_at: Time.zone.now,
id: 1 id: 1
) }
end end
describe '#parallel?' do describe '#parallel?' do

View File

@ -7,15 +7,16 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :cle
let(:project) { merge_request.project } let(:project) { merge_request.project }
let(:merged_at) { Time.new(2017, 1, 1, 12, 00).utc } let(:merged_at) { Time.new(2017, 1, 1, 12, 00).utc }
let(:client_double) { double(user: double(id: 999, login: 'merger', email: 'merger@email.com')) } let(:client_double) { double(user: { id: 999, login: 'merger', email: 'merger@email.com' } ) }
let(:merger_user) { double(id: 999, login: 'merger') } let(:merger_user) { { id: 999, login: 'merger' } }
let(:pull_request) do let(:pull_request) do
instance_double( Gitlab::GithubImport::Representation::PullRequest.from_api_response(
Gitlab::GithubImport::Representation::PullRequest, {
iid: merge_request.iid, number: merge_request.iid,
merged_at: merged_at, merged_at: merged_at,
merged_by: merger_user merged_by: merger_user
}
) )
end end

View File

@ -8,7 +8,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
let_it_be(:merge_request) { create(:merge_request) } let_it_be(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project } let(:project) { merge_request.project }
let(:client_double) { double(user: double(id: 999, login: 'author', email: 'author@email.com')) } let(:client_double) { double(user: { id: 999, login: 'author', email: 'author@email.com' }) }
let(:submitted_at) { Time.new(2017, 1, 1, 12, 00).utc } let(:submitted_at) { Time.new(2017, 1, 1, 12, 00).utc }
subject { described_class.new(review, project, client_double) } subject { described_class.new(review, project, client_double) }

View File

@ -8,33 +8,30 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
let(:client) { double(:client) } let(:client) { double(:client) }
let(:pull_request) do let(:pull_request) do
double( {
:response,
number: 42, number: 42,
title: 'My Pull Request', title: 'My Pull Request',
body: 'This is my pull request', body: 'This is my pull request',
state: 'closed', state: 'closed',
head: double( head: {
:head,
sha: '123abc', sha: '123abc',
ref: 'my-feature', ref: 'my-feature',
repo: double(:repo, id: 400), repo: { id: 400 },
user: double(:user, id: 4, login: 'alice') user: { id: 4, login: 'alice' }
), },
base: double( base: {
:base,
sha: '456def', sha: '456def',
ref: 'master', ref: 'master',
repo: double(:repo, id: 200) repo: { id: 200 }
), },
milestone: double(:milestone, number: 4), milestone: { number: 4 },
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
assignee: double(:user, id: 4, login: 'alice'), assignee: { id: 4, login: 'alice' },
merged_by: double(:user, id: 4, login: 'alice'), merged_by: { id: 4, login: 'alice' },
created_at: 1.second.ago, created_at: 1.second.ago,
updated_at: 1.second.ago, updated_at: 1.second.ago,
merged_at: 1.second.ago merged_at: 1.second.ago
) }
end end
describe '#parallel?' do describe '#parallel?' do
@ -184,12 +181,11 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
context 'when the pull request was updated after the last update' do context 'when the pull request was updated after the last update' do
let(:pr) do let(:pr) do
double( {
:pr,
updated_at: Time.zone.now, updated_at: Time.zone.now,
head: double(:head, sha: '123'), head: { sha: '123' },
base: double(:base, sha: '456') base: { sha: '456' }
) }
end end
before do before do
@ -201,7 +197,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns true when the head SHA is not present' do it 'returns true when the head SHA is not present' do
expect(importer) expect(importer)
.to receive(:commit_exists?) .to receive(:commit_exists?)
.with(pr.head.sha) .with('123')
.and_return(false) .and_return(false)
expect(importer.update_repository?(pr)).to eq(true) expect(importer.update_repository?(pr)).to eq(true)
@ -210,12 +206,12 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns true when the base SHA is not present' do it 'returns true when the base SHA is not present' do
expect(importer) expect(importer)
.to receive(:commit_exists?) .to receive(:commit_exists?)
.with(pr.head.sha) .with('123')
.and_return(true) .and_return(true)
expect(importer) expect(importer)
.to receive(:commit_exists?) .to receive(:commit_exists?)
.with(pr.base.sha) .with('456')
.and_return(false) .and_return(false)
expect(importer.update_repository?(pr)).to eq(true) expect(importer.update_repository?(pr)).to eq(true)
@ -224,12 +220,12 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns false if both the head and base SHAs are present' do it 'returns false if both the head and base SHAs are present' do
expect(importer) expect(importer)
.to receive(:commit_exists?) .to receive(:commit_exists?)
.with(pr.head.sha) .with('123')
.and_return(true) .and_return(true)
expect(importer) expect(importer)
.to receive(:commit_exists?) .to receive(:commit_exists?)
.with(pr.base.sha) .with('456')
.and_return(true) .and_return(true)
expect(importer.update_repository?(pr)).to eq(false) expect(importer.update_repository?(pr)).to eq(false)
@ -238,7 +234,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
context 'when the pull request was updated before the last update' do context 'when the pull request was updated before the last update' do
it 'returns false' do it 'returns false' do
pr = double(:pr, updated_at: 1.year.ago) pr = { updated_at: 1.year.ago }
allow(project) allow(project)
.to receive(:last_repository_updated_at) .to receive(:last_repository_updated_at)

View File

@ -23,7 +23,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsReviewsImporter do
end end
describe '#id_for_already_imported_cache' do describe '#id_for_already_imported_cache' do
it { expect(subject.id_for_already_imported_cache(double(id: 1))).to eq(1) } it { expect(subject.id_for_already_imported_cache({ id: 1 })).to eq(1) }
end end
describe '#each_object_to_import', :clean_gitlab_redis_cache do describe '#each_object_to_import', :clean_gitlab_redis_cache do
@ -36,15 +36,11 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsReviewsImporter do
) )
end end
let(:review) { double(id: 1) } let(:review) { { id: 1 } }
it 'fetches the pull requests reviews data' do it 'fetches the pull requests reviews data' do
page = double(objects: [review], number: 1) page = double(objects: [review], number: 1)
expect(review)
.to receive(:merge_request_id=)
.with(merge_request.id)
expect(client) expect(client)
.to receive(:each_page) .to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call .exactly(:once) # ensure to be cached on the second call
@ -55,6 +51,8 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsReviewsImporter do
.to yield_with_args(review) .to yield_with_args(review)
subject.each_object_to_import {} subject.each_object_to_import {}
expect(review[:merge_request_id]).to eq(merge_request.id)
end end
it 'skips cached pages' do it 'skips cached pages' do

View File

@ -10,22 +10,21 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
let(:created_at) { Time.new(2017, 1, 1, 12, 00) } let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
let(:released_at) { Time.new(2017, 1, 1, 12, 00) } let(:released_at) { Time.new(2017, 1, 1, 12, 00) }
let(:author) do let(:author) do
double( {
login: 'User A', login: 'User A',
id: 1 id: 1
) }
end end
let(:github_release) do let(:github_release) do
double( {
:github_release,
tag_name: '1.0', tag_name: '1.0',
name: github_release_name, name: github_release_name,
body: 'This is my release', body: 'This is my release',
created_at: created_at, created_at: created_at,
published_at: released_at, published_at: released_at,
author: author author: author
) }
end end
def stub_email_for_github_username(user_name = 'User A', user_email = 'user@example.com') def stub_email_for_github_username(user_name = 'User A', user_email = 'user@example.com')
@ -56,7 +55,7 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
end end
it 'imports draft releases' do it 'imports draft releases' do
release_double = double( release_double = {
name: 'Test', name: 'Test',
body: 'This is description', body: 'This is description',
tag_name: '1.0', tag_name: '1.0',
@ -65,7 +64,7 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
updated_at: created_at, updated_at: created_at,
published_at: nil, published_at: nil,
author: author author: author
) }
expect(importer).to receive(:each_release).and_return([release_double]) expect(importer).to receive(:each_release).and_return([release_double])
@ -101,7 +100,7 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
end end
it 'uses a default release description if none is provided' do it 'uses a default release description if none is provided' do
expect(github_release).to receive(:body).and_return('') github_release[:body] = nil
expect(importer).to receive(:each_release).and_return([github_release]) expect(importer).to receive(:each_release).and_return([github_release])
release = importer.build_releases.first release = importer.build_releases.first
@ -110,10 +109,10 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
end end
it 'does not create releases that have a NULL tag' do it 'does not create releases that have a NULL tag' do
null_tag_release = double( null_tag_release = {
name: 'NULL Test', name: 'NULL Test',
tag_name: nil tag_name: nil
) }
expect(importer).to receive(:each_release).and_return([null_tag_release]) expect(importer).to receive(:each_release).and_return([null_tag_release])
expect(importer.build_releases).to be_empty expect(importer.build_releases).to be_empty
@ -179,13 +178,13 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
end end
it 'returns ghost user when author is empty in Github release' do it 'returns ghost user when author is empty in Github release' do
allow(github_release).to receive(:author).and_return(nil) github_release[:author] = nil
expect(release_hash[:author_id]).to eq(Gitlab::GithubImport.ghost_user_id) expect(release_hash[:author_id]).to eq(Gitlab::GithubImport.ghost_user_id)
end end
context 'when Github author is not found in Gitlab' do context 'when Github author is not found in Gitlab' do
let(:author) { double(login: 'octocat', id: 1 ) } let(:author) { { login: 'octocat', id: 1 } }
before do before do
# Stub user email which does not match a Gitlab user. # Stub user email which does not match a Gitlab user.
@ -222,11 +221,11 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
describe '#description_for' do describe '#description_for' do
it 'returns the description when present' do it 'returns the description when present' do
expect(importer.description_for(github_release)).to eq(github_release.body) expect(importer.description_for(github_release)).to eq(github_release[:body])
end end
it 'returns a generated description when one is not present' do it 'returns a generated description when one is not present' do
allow(github_release).to receive(:body).and_return('') github_release[:body] = nil
expect(importer.description_for(github_release)).to eq('Release for tag 1.0') expect(importer.description_for(github_release)).to eq('Release for tag 1.0')
end end

View File

@ -14,7 +14,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointDiffNotesImporter d
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::DiffNoteImporter) } it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::DiffNoteImporter) }
it { expect(subject.collection_method).to eq(:pull_request_comments) } it { expect(subject.collection_method).to eq(:pull_request_comments) }
it { expect(subject.object_type).to eq(:diff_note) } it { expect(subject.object_type).to eq(:diff_note) }
it { expect(subject.id_for_already_imported_cache(double(id: 1))).to eq(1) } it { expect(subject.id_for_already_imported_cache({ id: 1 })).to eq(1) }
describe '#each_object_to_import', :clean_gitlab_redis_cache do describe '#each_object_to_import', :clean_gitlab_redis_cache do
let(:merge_request) do let(:merge_request) do
@ -26,7 +26,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointDiffNotesImporter d
) )
end end
let(:note) { double(id: 1) } let(:note) { { id: 1 } }
let(:page) { double(objects: [note], number: 1) } let(:page) { double(objects: [note], number: 1) }
it 'fetches data' do it 'fetches data' do

View File

@ -40,7 +40,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
end end
describe '#id_for_already_imported_cache' do describe '#id_for_already_imported_cache' do
let(:event) { instance_double('Event', id: 1) } let(:event) { { id: 1 } }
it { expect(subject.id_for_already_imported_cache(event)).to eq(1) } it { expect(subject.id_for_already_imported_cache(event)).to eq(1) }
end end
@ -116,8 +116,8 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
counter = 0 counter = 0
subject.each_object_to_import do |object| subject.each_object_to_import do |object|
expect(object).to eq issue_event expect(object).to eq issue_event
expect(issue_event.issue['number']).to eq issuable.iid expect(issue_event[:issue]['number']).to eq issuable.iid
expect(issue_event.issue['pull_request']).to eq false expect(issue_event[:issue]['pull_request']).to eq false
counter += 1 counter += 1
end end
expect(counter).to eq 1 expect(counter).to eq 1
@ -131,8 +131,8 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
counter = 0 counter = 0
subject.each_object_to_import do |object| subject.each_object_to_import do |object|
expect(object).to eq issue_event expect(object).to eq issue_event
expect(issue_event.issue['number']).to eq issuable.iid expect(issue_event[:issue]['number']).to eq issuable.iid
expect(issue_event.issue['pull_request']).to eq true expect(issue_event[:issue]['pull_request']).to eq true
counter += 1 counter += 1
end end
expect(counter).to eq 1 expect(counter).to eq 1

View File

@ -14,7 +14,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueNotesImporter
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::NoteImporter) } it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::NoteImporter) }
it { expect(subject.collection_method).to eq(:issue_comments) } it { expect(subject.collection_method).to eq(:issue_comments) }
it { expect(subject.object_type).to eq(:note) } it { expect(subject.object_type).to eq(:note) }
it { expect(subject.id_for_already_imported_cache(double(id: 1))).to eq(1) } it { expect(subject.id_for_already_imported_cache({ id: 1 })).to eq(1) }
describe '#each_object_to_import', :clean_gitlab_redis_cache do describe '#each_object_to_import', :clean_gitlab_redis_cache do
let(:issue) do let(:issue) do
@ -25,7 +25,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueNotesImporter
) )
end end
let(:note) { double(id: 1) } let(:note) { { id: 1 } }
let(:page) { double(objects: [note], number: 1) } let(:page) { double(objects: [note], number: 1) }
it 'fetches data' do it 'fetches data' do

View File

@ -14,7 +14,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointMergeRequestNotesIm
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::NoteImporter) } it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::NoteImporter) }
it { expect(subject.collection_method).to eq(:issue_comments) } it { expect(subject.collection_method).to eq(:issue_comments) }
it { expect(subject.object_type).to eq(:note) } it { expect(subject.object_type).to eq(:note) }
it { expect(subject.id_for_already_imported_cache(double(id: 1))).to eq(1) } it { expect(subject.id_for_already_imported_cache({ id: 1 })).to eq(1) }
describe '#each_object_to_import', :clean_gitlab_redis_cache do describe '#each_object_to_import', :clean_gitlab_redis_cache do
let(:merge_request) do let(:merge_request) do
@ -26,7 +26,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointMergeRequestNotesIm
) )
end end
let(:note) { double(id: 1) } let(:note) { { id: 1 } }
let(:page) { double(objects: [note], number: 1) } let(:page) { double(objects: [note], number: 1) }
it 'fetches data' do it 'fetches data' do

View File

@ -28,7 +28,7 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
let(:start_line) { nil } let(:start_line) { nil }
let(:end_line) { 23 } let(:end_line) { 23 }
let(:note_body) { 'Hello world' } let(:note_body) { 'Hello world' }
let(:user_data) { { 'id' => 4, 'login' => 'alice' } } let(:user_data) { { id: 4, login: 'alice' } }
let(:side) { 'RIGHT' } let(:side) { 'RIGHT' }
let(:created_at) { Time.new(2017, 1, 1, 12, 00) } let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
let(:updated_at) { Time.new(2017, 1, 1, 12, 15) } let(:updated_at) { Time.new(2017, 1, 1, 12, 15) }
@ -275,15 +275,14 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
describe '.from_api_response' do describe '.from_api_response' do
it_behaves_like 'a DiffNote representation' do it_behaves_like 'a DiffNote representation' do
let(:response) do let(:response) do
double( {
:response,
id: note_id, id: note_id,
html_url: 'https://github.com/foo/bar/pull/42', html_url: 'https://github.com/foo/bar/pull/42',
path: 'README.md', path: 'README.md',
commit_id: '123abc', commit_id: '123abc',
original_commit_id: 'original123abc', original_commit_id: 'original123abc',
side: side, side: side,
user: user_data && double(:user, user_data), user: user_data,
diff_hunk: hunk, diff_hunk: hunk,
body: note_body, body: note_body,
created_at: created_at, created_at: created_at,
@ -291,7 +290,7 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
line: end_line, line: end_line,
start_line: start_line, start_line: start_line,
in_reply_to_id: in_reply_to_id in_reply_to_id: in_reply_to_id
) }
end end
subject(:note) { described_class.from_api_response(response) } subject(:note) { described_class.from_api_response(response) }

View File

@ -74,20 +74,19 @@ RSpec.describe Gitlab::GithubImport::Representation::Issue do
describe '.from_api_response' do describe '.from_api_response' do
let(:response) do let(:response) do
double( {
:response,
number: 42, number: 42,
title: 'My Issue', title: 'My Issue',
body: 'This is my issue', body: 'This is my issue',
milestone: double(:milestone, number: 4), milestone: { number: 4 },
state: 'open', state: 'open',
assignees: [double(:user, id: 4, login: 'alice')], assignees: [{ id: 4, login: 'alice' }],
labels: [double(:label, name: 'bug')], labels: [{ name: 'bug' }],
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
created_at: created_at, created_at: created_at,
updated_at: updated_at, updated_at: updated_at,
pull_request: false pull_request: false
) }
end end
let(:additional_data) { { work_item_type_id: work_item_type_id } } let(:additional_data) { { work_item_type_id: work_item_type_id } }
@ -97,9 +96,7 @@ RSpec.describe Gitlab::GithubImport::Representation::Issue do
end end
it 'does not set the user if the response did not include a user' do it 'does not set the user if the response did not include a user' do
allow(response) response[:user] = nil
.to receive(:user)
.and_return(nil)
issue = described_class.from_api_response(response, additional_data) issue = described_class.from_api_response(response, additional_data)

View File

@ -48,15 +48,14 @@ RSpec.describe Gitlab::GithubImport::Representation::Note do
describe '.from_api_response' do describe '.from_api_response' do
let(:response) do let(:response) do
double( {
:response,
html_url: 'https://github.com/foo/bar/issues/42', html_url: 'https://github.com/foo/bar/issues/42',
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
body: 'Hello world', body: 'Hello world',
created_at: created_at, created_at: created_at,
updated_at: updated_at, updated_at: updated_at,
id: 1 id: 1
) }
end end
it_behaves_like 'a Note' do it_behaves_like 'a Note' do
@ -64,9 +63,7 @@ RSpec.describe Gitlab::GithubImport::Representation::Note do
end end
it 'does not set the user if the response did not include a user' do it 'does not set the user if the response did not include a user' do
allow(response) response[:user] = nil
.to receive(:user)
.and_return(nil)
note = described_class.from_api_response(response) note = described_class.from_api_response(response)

View File

@ -21,15 +21,14 @@ RSpec.describe Gitlab::GithubImport::Representation::PullRequestReview do
describe '.from_api_response' do describe '.from_api_response' do
let(:response) do let(:response) do
double( {
:response,
id: 999, id: 999,
merge_request_id: 42, merge_request_id: 42,
body: 'note', body: 'note',
state: 'APPROVED', state: 'APPROVED',
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
submitted_at: submitted_at submitted_at: submitted_at
) }
end end
it_behaves_like 'a PullRequest review' do it_behaves_like 'a PullRequest review' do
@ -37,9 +36,7 @@ RSpec.describe Gitlab::GithubImport::Representation::PullRequestReview do
end end
it 'does not set the user if the response did not include a user' do it 'does not set the user if the response did not include a user' do
allow(response) response[:user] = nil
.to receive(:user)
.and_return(nil)
review = described_class.from_api_response(response) review = described_class.from_api_response(response)

View File

@ -93,33 +93,30 @@ RSpec.describe Gitlab::GithubImport::Representation::PullRequest do
describe '.from_api_response' do describe '.from_api_response' do
let(:response) do let(:response) do
double( {
:response,
number: 42, number: 42,
title: 'My Pull Request', title: 'My Pull Request',
body: 'This is my pull request', body: 'This is my pull request',
state: 'closed', state: 'closed',
head: double( head: {
:head,
sha: '123abc', sha: '123abc',
ref: 'my-feature', ref: 'my-feature',
repo: double(:repo, id: 400), repo: { id: 400 },
user: double(:user, id: 4, login: 'alice') user: { id: 4, login: 'alice' }
), },
base: double( base: {
:base,
sha: '456def', sha: '456def',
ref: 'master', ref: 'master',
repo: double(:repo, id: 200) repo: { id: 200 }
), },
milestone: double(:milestone, number: 4), milestone: { number: 4 },
user: double(:user, id: 4, login: 'alice'), user: { id: 4, login: 'alice' },
assignee: double(:user, id: 4, login: 'alice'), assignee: { id: 4, login: 'alice' },
merged_by: double(:user, id: 4, login: 'alice'), merged_by: { id: 4, login: 'alice' },
created_at: created_at, created_at: created_at,
updated_at: updated_at, updated_at: updated_at,
merged_at: merged_at merged_at: merged_at
) }
end end
it_behaves_like 'a PullRequest' do it_behaves_like 'a PullRequest' do
@ -127,9 +124,7 @@ RSpec.describe Gitlab::GithubImport::Representation::PullRequest do
end end
it 'does not set the user if the response did not include a user' do it 'does not set the user if the response did not include a user' do
allow(response) response[:user] = nil
.to receive(:user)
.and_return(nil)
pr = described_class.from_api_response(response) pr = described_class.from_api_response(response)

View File

@ -21,7 +21,7 @@ RSpec.describe Gitlab::GithubImport::Representation::User do
describe '.from_api_response' do describe '.from_api_response' do
it_behaves_like 'a User' do it_behaves_like 'a User' do
let(:response) { double(:response, id: 42, login: 'alice') } let(:response) { { id: 42, login: 'alice' } }
let(:user) { described_class.from_api_response(response) } let(:user) { described_class.from_api_response(response) }
end end
end end

View File

@ -17,8 +17,8 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
describe '#author_id_for' do describe '#author_id_for' do
context 'with default author_key' do context 'with default author_key' do
it 'returns the user ID for the author of an object' do it 'returns the user ID for the author of an object' do
user = double(:user, id: 4, login: 'kittens') user = { id: 4, login: 'kittens' }
note = double(:note, author: user) note = { author: user }
expect(finder).to receive(:user_id_for).with(user).and_return(42) expect(finder).to receive(:user_id_for).with(user).and_return(42)
@ -26,8 +26,8 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
end end
it 'returns the ID of the project creator if no user ID could be found' do it 'returns the ID of the project creator if no user ID could be found' do
user = double(:user, id: 4, login: 'kittens') user = { id: 4, login: 'kittens' }
note = double(:note, author: user) note = { author: user }
expect(finder).to receive(:user_id_for).with(user).and_return(nil) expect(finder).to receive(:user_id_for).with(user).and_return(nil)
@ -35,7 +35,7 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
end end
it 'returns the ID of the ghost user when the object has no user' do it 'returns the ID of the ghost user when the object has no user' do
note = double(:note, author: nil) note = { author: nil }
expect(finder.author_id_for(note)).to eq([User.ghost.id, true]) expect(finder.author_id_for(note)).to eq([User.ghost.id, true])
end end
@ -46,7 +46,7 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
end end
context 'with a non-default author_key' do context 'with a non-default author_key' do
let(:user) { double(:user, id: 4, login: 'kittens') } let(:user) { { id: 4, login: 'kittens' } }
shared_examples 'user ID finder' do |author_key| shared_examples 'user ID finder' do |author_key|
it 'returns the user ID for an object' do it 'returns the user ID for an object' do
@ -57,25 +57,25 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
end end
context 'when the author_key parameter is :actor' do context 'when the author_key parameter is :actor' do
let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', actor: user) } let(:issue_event) { { actor: user } }
it_behaves_like 'user ID finder', :actor it_behaves_like 'user ID finder', :actor
end end
context 'when the author_key parameter is :assignee' do context 'when the author_key parameter is :assignee' do
let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', assignee: user) } let(:issue_event) { { assignee: user } }
it_behaves_like 'user ID finder', :assignee it_behaves_like 'user ID finder', :assignee
end end
context 'when the author_key parameter is :requested_reviewer' do context 'when the author_key parameter is :requested_reviewer' do
let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', requested_reviewer: user) } let(:issue_event) { { requested_reviewer: user } }
it_behaves_like 'user ID finder', :requested_reviewer it_behaves_like 'user ID finder', :requested_reviewer
end end
context 'when the author_key parameter is :review_requester' do context 'when the author_key parameter is :review_requester' do
let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', review_requester: user) } let(:issue_event) { { review_requester: user } }
it_behaves_like 'user ID finder', :review_requester it_behaves_like 'user ID finder', :review_requester
end end
@ -84,15 +84,15 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
describe '#assignee_id_for' do describe '#assignee_id_for' do
it 'returns the user ID for the assignee of an issuable' do it 'returns the user ID for the assignee of an issuable' do
user = double(:user, id: 4, login: 'kittens') user = { id: 4, login: 'kittens' }
issue = double(:issue, assignee: user) issue = { assignee: user }
expect(finder).to receive(:user_id_for).with(user).and_return(42) expect(finder).to receive(:user_id_for).with(user).and_return(42)
expect(finder.assignee_id_for(issue)).to eq(42) expect(finder.assignee_id_for(issue)).to eq(42)
end end
it 'returns nil if the issuable does not have an assignee' do it 'returns nil if the issuable does not have an assignee' do
issue = double(:issue, assignee: nil) issue = { assignee: nil }
expect(finder).not_to receive(:user_id_for) expect(finder).not_to receive(:user_id_for)
expect(finder.assignee_id_for(issue)).to be_nil expect(finder.assignee_id_for(issue)).to be_nil
@ -101,9 +101,9 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
describe '#user_id_for' do describe '#user_id_for' do
it 'returns the user ID for the given user' do it 'returns the user ID for the given user' do
user = double(:user, id: 4, login: 'kittens') user = { id: 4, login: 'kittens' }
expect(finder).to receive(:find).with(user.id, user.login).and_return(42) expect(finder).to receive(:find).with(user[:id], user[:login]).and_return(42)
expect(finder.user_id_for(user)).to eq(42) expect(finder.user_id_for(user)).to eq(42)
end end
@ -221,7 +221,7 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
end end
context 'when an Email address is not cached' do context 'when an Email address is not cached' do
let(:user) { double(:user, email: email) } let(:user) { { email: email } }
it 'retrieves the Email address from the GitHub API' do it 'retrieves the Email address from the GitHub API' do
expect(client).to receive(:user).with('kittens').and_return(user) expect(client).to receive(:user).with('kittens').and_return(user)
@ -251,7 +251,7 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
end end
it 'shortens the timeout for Email address in cache when an Email address is private/nil from GitHub' do it 'shortens the timeout for Email address in cache when an Email address is private/nil from GitHub' do
user = double(:user, email: nil) user = { email: nil }
expect(client).to receive(:user).with('kittens').and_return(user) expect(client).to receive(:user).with('kittens').and_return(user)
expect(Gitlab::Cache::Import::Caching) expect(Gitlab::Cache::Import::Caching)

View File

@ -3069,8 +3069,24 @@ RSpec.describe Ci::Build do
end end
context 'when build is for tag' do context 'when build is for tag' do
let(:tag_name) { project.repository.tags.first.name }
let(:tag_message) { project.repository.tags.first.message }
let!(:pipeline) do
create(:ci_pipeline, project: project,
sha: project.commit.id,
ref: tag_name,
status: 'success')
end
let!(:build) { create(:ci_build, pipeline: pipeline, ref: tag_name) }
let(:tag_variable) do let(:tag_variable) do
{ key: 'CI_COMMIT_TAG', value: 'master', public: true, masked: false } { key: 'CI_COMMIT_TAG', value: tag_name, public: true, masked: false }
end
let(:tag_message_variable) do
{ key: 'CI_COMMIT_TAG_MESSAGE', value: tag_message, public: true, masked: false }
end end
before do before do
@ -3081,7 +3097,7 @@ RSpec.describe Ci::Build do
it do it do
build.reload build.reload
expect(subject).to include(tag_variable) expect(subject).to include(tag_variable, tag_message_variable)
end end
end end