Merge branch 'master' into 44149-issue-comment-buttons
* master: (29 commits) Fix provider server URL used when listing repos to import Fix inconsistent punctuation on MR form Update dependency for svgs Fix timestamp to include %M instead of %I for post-deploy migrations. Use Gitaly 0.89.0 Resolve "Hover style for sidebar dropdowns is wrong" fixed spec Respect the protocol in `expose_url` Fix removes source branch text being rendered in merged state Fix code and wiki search results when filename is non-ASCII Include the ee/ directory in backtraces Use GitLab fork of zaproxy Updates file extensions on Vue docs fixed note polling not sending updated last fetched at date added spec for polling Add changelog entry Use `list.id` for `:key` added mutation spec Bump parser and unparser gems to remove warnings fix polling not working correctly Fixed issue notes being duplicated ...
This commit is contained in:
commit
939c87233e
|
@ -1 +1 @@
|
|||
0.88.0
|
||||
0.89.0
|
||||
|
|
10
Gemfile.lock
10
Gemfile.lock
|
@ -63,7 +63,7 @@ GEM
|
|||
fog-core
|
||||
mime-types (>= 2.99)
|
||||
unf
|
||||
ast (2.3.0)
|
||||
ast (2.4.0)
|
||||
atomic (1.1.99)
|
||||
attr_encrypted (3.0.3)
|
||||
encryptor (~> 3.0.0)
|
||||
|
@ -586,8 +586,8 @@ GEM
|
|||
orm_adapter (0.5.0)
|
||||
os (0.9.6)
|
||||
parallel (1.12.1)
|
||||
parser (2.4.0.2)
|
||||
ast (~> 2.3)
|
||||
parser (2.5.0.3)
|
||||
ast (~> 2.4.0)
|
||||
parslet (1.5.0)
|
||||
blankslate (~> 2.0)
|
||||
path_expander (1.0.2)
|
||||
|
@ -951,13 +951,13 @@ GEM
|
|||
get_process_mem (~> 0)
|
||||
unicorn (>= 4, < 6)
|
||||
uniform_notifier (1.10.0)
|
||||
unparser (0.2.6)
|
||||
unparser (0.2.7)
|
||||
abstract_type (~> 0.0.7)
|
||||
adamantium (~> 0.2.0)
|
||||
concord (~> 0.1.5)
|
||||
diff-lcs (~> 1.3)
|
||||
equalizer (~> 0.0.9)
|
||||
parser (>= 2.3.1.2, < 2.5)
|
||||
parser (>= 2.3.1.2, < 2.6)
|
||||
procto (~> 0.0.2)
|
||||
url_safe_base64 (0.2.2)
|
||||
validates_hostname (1.0.6)
|
||||
|
|
|
@ -27,10 +27,11 @@ export default {
|
|||
return Vue.http[method](endpoint);
|
||||
},
|
||||
poll(data = {}) {
|
||||
const { endpoint, lastFetchedAt } = data;
|
||||
const endpoint = data.notesData.notesPath;
|
||||
const lastFetchedAt = data.lastFetchedAt;
|
||||
const options = {
|
||||
headers: {
|
||||
'X-Last-Fetched-At': lastFetchedAt,
|
||||
'X-Last-Fetched-At': lastFetchedAt ? `${lastFetchedAt}` : undefined,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -209,18 +209,16 @@ const pollSuccessCallBack = (resp, commit, state, getters) => {
|
|||
});
|
||||
}
|
||||
|
||||
commit(types.SET_LAST_FETCHED_AT, resp.lastFetchedAt);
|
||||
commit(types.SET_LAST_FETCHED_AT, resp.last_fetched_at);
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
export const poll = ({ commit, state, getters }) => {
|
||||
const requestData = { endpoint: state.notesData.notesPath, lastFetchedAt: state.lastFetchedAt };
|
||||
|
||||
eTagPoll = new Poll({
|
||||
resource: service,
|
||||
method: 'poll',
|
||||
data: requestData,
|
||||
data: state,
|
||||
successCallback: resp => resp.json()
|
||||
.then(data => pollSuccessCallBack(data, commit, state, getters)),
|
||||
errorCallback: () => Flash('Something went wrong while fetching latest comments.'),
|
||||
|
@ -229,7 +227,7 @@ export const poll = ({ commit, state, getters }) => {
|
|||
if (!Visibility.hidden()) {
|
||||
eTagPoll.makeRequest();
|
||||
} else {
|
||||
service.poll(requestData);
|
||||
service.poll(state);
|
||||
}
|
||||
|
||||
Visibility.change(() => {
|
||||
|
|
|
@ -90,19 +90,21 @@ export default {
|
|||
const notes = [];
|
||||
|
||||
notesData.forEach((note) => {
|
||||
const nn = Object.assign({}, note);
|
||||
|
||||
// To support legacy notes, should be very rare case.
|
||||
if (note.individual_note && note.notes.length > 1) {
|
||||
note.notes.forEach((n) => {
|
||||
nn.notes = [n]; // override notes array to only have one item to mimick individual_note
|
||||
notes.push(nn);
|
||||
notes.push({
|
||||
...note,
|
||||
notes: [n], // override notes array to only have one item to mimick individual_note
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const oldNote = utils.findNoteObjectById(state.notes, note.id);
|
||||
nn.expanded = oldNote ? oldNote.expanded : note.expanded;
|
||||
|
||||
notes.push(nn);
|
||||
notes.push({
|
||||
...note,
|
||||
expanded: (oldNote ? oldNote.expanded : note.expanded),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
};
|
||||
|
||||
this.isRemovingSourceBranch = true;
|
||||
this.service.mergeResource.save(options)
|
||||
this.service.merge(options)
|
||||
.then(res => res.data)
|
||||
.then((data) => {
|
||||
if (data.status === 'merge_when_pipeline_succeeds') {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import emptyStateSVG from 'icons/_mr_widget_empty_state.svg';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetNothingToMerge',
|
||||
props: {
|
||||
mr: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { emptyStateSVG };
|
||||
},
|
||||
template: `
|
||||
<div class="mr-widget-body mr-widget-empty-state">
|
||||
<div class="row">
|
||||
<div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
|
||||
<span v-html="emptyStateSVG"></span>
|
||||
</div>
|
||||
<div class="text col-sm-7 col-sm-pull-5 col-xs-12">
|
||||
<span>
|
||||
Merge requests are a place to propose changes you have made to a project
|
||||
and discuss those changes with others.
|
||||
</span>
|
||||
<p>
|
||||
Interested parties can even contribute by pushing commits if they want to.
|
||||
</p>
|
||||
<p>
|
||||
Currently there are no changes in this merge request's source branch.
|
||||
Please push new commits or use a different branch.
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
v-if="mr.newBlobPath"
|
||||
:href="mr.newBlobPath"
|
||||
class="btn btn-inverted btn-save">
|
||||
Create file
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
<script>
|
||||
import emptyStateSVG from 'icons/_mr_widget_empty_state.svg';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetNothingToMerge',
|
||||
props: {
|
||||
mr: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { emptyStateSVG };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mr-widget-body mr-widget-empty-state">
|
||||
<div class="row">
|
||||
<div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
|
||||
<span v-html="emptyStateSVG"></span>
|
||||
</div>
|
||||
<div class="text col-sm-7 col-sm-pull-5 col-xs-12">
|
||||
<span>
|
||||
Merge requests are a place to propose changes you have made to a project
|
||||
and discuss those changes with others.
|
||||
</span>
|
||||
<p>
|
||||
Interested parties can even contribute by pushing commits if they want to.
|
||||
</p>
|
||||
<p>
|
||||
Currently there are no changes in this merge request's source branch.
|
||||
Please push new commits or use a different branch.
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
v-if="mr.newBlobPath"
|
||||
:href="mr.newBlobPath"
|
||||
class="btn btn-inverted btn-save">
|
||||
Create file
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -24,7 +24,7 @@ export { default as MergingState } from './components/states/mr_widget_merging.v
|
|||
export { default as WipState } from './components/states/mr_widget_wip';
|
||||
export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
|
||||
export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
|
||||
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
|
||||
export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue';
|
||||
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
|
||||
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
|
||||
export { default as ReadyToMergeState } from './components/states/mr_widget_ready_to_merge';
|
||||
|
|
|
@ -71,7 +71,8 @@ export default {
|
|||
return this.mr.deployments.length;
|
||||
},
|
||||
shouldRenderSourceBranchRemovalStatus() {
|
||||
return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch;
|
||||
return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch &&
|
||||
(!this.mr.isNothingToMergeState && !this.mr.isMergedState);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -125,6 +125,10 @@ export default class MergeRequestStore {
|
|||
return this.state === stateKey.nothingToMerge;
|
||||
}
|
||||
|
||||
get isMergedState() {
|
||||
return this.state === stateKey.merged;
|
||||
}
|
||||
|
||||
initRebase(data) {
|
||||
this.canPushToSourceBranch = data.can_push_to_source_branch;
|
||||
this.rebaseInProgress = data.rebase_in_progress;
|
||||
|
|
|
@ -49,6 +49,7 @@ export const stateKey = {
|
|||
notAllowedToMerge: 'notAllowedToMerge',
|
||||
readyToMerge: 'readyToMerge',
|
||||
rebase: 'rebase',
|
||||
merged: 'merged',
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -137,12 +137,22 @@
|
|||
z-index: 200;
|
||||
overflow: hidden;
|
||||
|
||||
a:not(.btn-retry),
|
||||
.btn-link {
|
||||
a:not(.btn) {
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
color: $gl-link-hover-color;
|
||||
|
||||
.avatar {
|
||||
border-color: rgba($avatar-border, .2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
color: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
@ -214,7 +224,7 @@
|
|||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: $md-link-color;
|
||||
color: $gl-link-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,16 +496,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
a:not(.btn-retry) {
|
||||
&:hover {
|
||||
color: $md-link-color;
|
||||
|
||||
.avatar {
|
||||
border-color: rgba($avatar-border, .2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
width: 100%;
|
||||
padding-top: 6px;
|
||||
|
@ -503,6 +503,20 @@
|
|||
|
||||
.dropdown-menu {
|
||||
width: 100%;
|
||||
|
||||
/*
|
||||
* Overwrite hover style for dropdown items, so that they are not blue
|
||||
* This should be removed during dev of https://gitlab.com/gitlab-org/gitlab-ce/issues/44040
|
||||
*/
|
||||
li a {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&.is-focused {
|
||||
@include dropdown-item-hover;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
module ImportHelper
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
def has_ci_cd_only_params?
|
||||
false
|
||||
end
|
||||
|
@ -75,17 +77,18 @@ module ImportHelper
|
|||
private
|
||||
|
||||
def github_project_url(full_path)
|
||||
"#{github_root_url}/#{full_path}"
|
||||
URI.join(github_root_url, full_path).to_s
|
||||
end
|
||||
|
||||
def github_root_url
|
||||
return @github_url if defined?(@github_url)
|
||||
strong_memoize(:github_url) do
|
||||
provider = Gitlab::Auth::OAuth::Provider.config_for('github')
|
||||
|
||||
provider = Gitlab.config.omniauth.providers.find { |p| p.name == 'github' }
|
||||
@github_url = provider.fetch('url', 'https://github.com') if provider
|
||||
provider&.dig('url').presence || 'https://github.com'
|
||||
end
|
||||
end
|
||||
|
||||
def gitea_project_url(full_path)
|
||||
"#{@gitea_host_url.sub(%r{/+\z}, '')}/#{full_path}"
|
||||
URI.join(@gitea_host_url, full_path).to_s
|
||||
end
|
||||
end
|
||||
|
|
|
@ -169,7 +169,7 @@ module NotesHelper
|
|||
reopenPath: reopen_issuable_path(issuable),
|
||||
notesPath: notes_url,
|
||||
totalNotes: issuable.discussions.length,
|
||||
lastFetchedAt: Time.now
|
||||
lastFetchedAt: Time.now.to_i
|
||||
|
||||
}.to_json
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
class Compare
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
delegate :same, :head, :base, to: :@compare
|
||||
|
||||
attr_reader :project
|
||||
|
@ -11,9 +13,10 @@ class Compare
|
|||
end
|
||||
end
|
||||
|
||||
def initialize(compare, project, straight: false)
|
||||
def initialize(compare, project, base_sha: nil, straight: false)
|
||||
@compare = compare
|
||||
@project = project
|
||||
@base_sha = base_sha
|
||||
@straight = straight
|
||||
end
|
||||
|
||||
|
@ -22,40 +25,36 @@ class Compare
|
|||
end
|
||||
|
||||
def start_commit
|
||||
return @start_commit if defined?(@start_commit)
|
||||
|
||||
strong_memoize(:start_commit) do
|
||||
commit = @compare.base
|
||||
@start_commit = commit ? ::Commit.new(commit, project) : nil
|
||||
|
||||
::Commit.new(commit, project) if commit
|
||||
end
|
||||
end
|
||||
|
||||
def head_commit
|
||||
return @head_commit if defined?(@head_commit)
|
||||
|
||||
strong_memoize(:head_commit) do
|
||||
commit = @compare.head
|
||||
@head_commit = commit ? ::Commit.new(commit, project) : nil
|
||||
|
||||
::Commit.new(commit, project) if commit
|
||||
end
|
||||
end
|
||||
alias_method :commit, :head_commit
|
||||
|
||||
def base_commit
|
||||
return @base_commit if defined?(@base_commit)
|
||||
|
||||
@base_commit = if start_commit && head_commit
|
||||
project.merge_base_commit(start_commit.id, head_commit.id)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def start_commit_sha
|
||||
start_commit.try(:sha)
|
||||
start_commit&.sha
|
||||
end
|
||||
|
||||
def base_commit_sha
|
||||
base_commit.try(:sha)
|
||||
strong_memoize(:base_commit) do
|
||||
next unless start_commit && head_commit
|
||||
|
||||
@base_sha || project.merge_base_commit(start_commit.id, head_commit.id)&.sha
|
||||
end
|
||||
end
|
||||
|
||||
def head_commit_sha
|
||||
commit.try(:sha)
|
||||
commit&.sha
|
||||
end
|
||||
|
||||
def raw_diffs(*args)
|
||||
|
|
|
@ -10,9 +10,14 @@ class CompareService
|
|||
@start_ref_name = new_start_ref_name
|
||||
end
|
||||
|
||||
def execute(target_project, target_ref, straight: false)
|
||||
def execute(target_project, target_ref, base_sha: nil, straight: false)
|
||||
raw_compare = target_project.repository.compare_source_branch(target_ref, start_project.repository, start_ref_name, straight: straight)
|
||||
|
||||
Compare.new(raw_compare, target_project, straight: straight) if raw_compare
|
||||
return unless raw_compare
|
||||
|
||||
Compare.new(raw_compare,
|
||||
target_project,
|
||||
base_sha: base_sha,
|
||||
straight: straight)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
":issue-link-base" => "issueLinkBase",
|
||||
":root-path" => "rootPath",
|
||||
":board-id" => "boardId",
|
||||
":key" => "_uid" }
|
||||
":key" => "list.id" }
|
||||
= render "shared/boards/components/sidebar", group: group
|
||||
- if @project
|
||||
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
.checkbox
|
||||
= form.label :allow_maintainer_to_push do
|
||||
= form.check_box :allow_maintainer_to_push, disabled: !issuable.can_allow_maintainer_to_push?(current_user)
|
||||
= _('Allow edits from maintainers')
|
||||
= _('Allow edits from maintainers.')
|
||||
= link_to 'About this feature', help_page_path('user/project/merge_requests/maintainer_access')
|
||||
.help-block
|
||||
= allow_maintainer_push_unavailable_reason(issuable)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix generated URL when listing repoitories for import
|
||||
merge_request: 17692
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix hover style of dropdown items in the right sidebar
|
||||
merge_request: 17519
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix "Remove source branch" button in Merge request widget during merge when pipeline
|
||||
succeeds state
|
||||
merge_request: 17192
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Use object ID to prevent duplicate keys Vue warning on Issue Boards page during
|
||||
development
|
||||
merge_request: 17682
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add partial indexes on todos to handle users with many todos
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix code and wiki search results when filename is non-ASCII
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Avoid re-fetching merge-base SHA from Gitaly unnecessarily
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move NothingToMerge vue component
|
||||
merge_request: 17544
|
||||
author: George Tsiolis
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Ensure the API returns https links when https is configured
|
||||
merge_request: 17681
|
||||
author:
|
||||
type: fixed
|
|
@ -1,7 +1,2 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
||||
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
|
||||
|
||||
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
|
||||
# Rails.backtrace_cleaner.remove_silencers!
|
||||
Rails.backtrace_cleaner.remove_silencers!
|
||||
Rails.backtrace_cleaner.add_silencer { |line| line !~ Gitlab::APP_DIRS_PATTERN }
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddPartialIndexesOnTodos < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME_PENDING="index_todos_on_user_id_and_id_pending"
|
||||
INDEX_NAME_DONE="index_todos_on_user_id_and_id_done"
|
||||
|
||||
def up
|
||||
unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_PENDING)
|
||||
add_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
|
||||
end
|
||||
unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_DONE)
|
||||
add_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
|
||||
remove_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20180309121820) do
|
||||
ActiveRecord::Schema.define(version: 20180309160427) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -1778,6 +1778,8 @@ ActiveRecord::Schema.define(version: 20180309121820) do
|
|||
add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree
|
||||
add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree
|
||||
add_index "todos", ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
|
||||
add_index "todos", ["user_id", "id"], name: "index_todos_on_user_id_and_id_done", where: "((state)::text = 'done'::text)", using: :btree
|
||||
add_index "todos", ["user_id", "id"], name: "index_todos_on_user_id_and_id_pending", where: "((state)::text = 'pending'::text)", using: :btree
|
||||
add_index "todos", ["user_id"], name: "index_todos_on_user_id", using: :btree
|
||||
|
||||
create_table "trending_projects", force: :cascade do |t|
|
||||
|
|
|
@ -548,6 +548,57 @@ On those a default key should not be provided.
|
|||
1. Properties in a Vue Component:
|
||||
Check [order of properties in components rule][vue-order].
|
||||
|
||||
#### `:key`
|
||||
When using `v-for` you need to provide a *unique* `:key` attribute for each item.
|
||||
|
||||
1. If the elements of the array being iterated have an unique `id` it is advised to use it:
|
||||
```html
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
>
|
||||
<!-- content -->
|
||||
</div>
|
||||
```
|
||||
|
||||
1. When the elements being iterated don't have a unique id, you can use the array index as the `:key` attribute
|
||||
```html
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
>
|
||||
<!-- content -->
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
1. When using `v-for` with `template` and there is more than one child element, the `:key` values must be unique. It's advised to use `kebab-case` namespaces.
|
||||
```html
|
||||
<template v-for="(item, index) in items">
|
||||
<span :key="`span-${index}`"></span>
|
||||
<button :key="`button-${index}`"></button>
|
||||
</template>
|
||||
```
|
||||
|
||||
1. When dealing with nested `v-for` use the same guidelines as above.
|
||||
```html
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
>
|
||||
<span
|
||||
v-for="element in array"
|
||||
:key="element.id"
|
||||
>
|
||||
<!-- content -->
|
||||
</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
Useful links:
|
||||
1. [`key`](https://vuejs.org/v2/guide/list.html#key)
|
||||
1. [Vue Style Guide: Keyed v-for](https://vuejs.org/v2/style-guide/#Keyed-v-for-essential )
|
||||
#### Vue and Bootstrap
|
||||
|
||||
1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
|
||||
|
|
|
@ -53,13 +53,13 @@ you can find a clear separation of concerns:
|
|||
```
|
||||
new_feature
|
||||
├── components
|
||||
│ └── component.js.es6
|
||||
│ └── component.vue
|
||||
│ └── ...
|
||||
├── store
|
||||
│ └── new_feature_store.js.es6
|
||||
├── service
|
||||
│ └── new_feature_service.js.es6
|
||||
├── new_feature_bundle.js.es6
|
||||
├── stores
|
||||
│ └── new_feature_store.js
|
||||
├── services
|
||||
│ └── new_feature_service.js
|
||||
├── new_feature_bundle.js
|
||||
```
|
||||
_For consistency purposes, we recommend you to follow the same structure._
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 48 KiB |
|
@ -15,7 +15,7 @@ module API
|
|||
url_options = Gitlab::Application.routes.default_url_options
|
||||
protocol, host, port = url_options.slice(:protocol, :host, :port).values
|
||||
|
||||
URI::HTTP.build(scheme: protocol, host: host, port: port, path: path).to_s
|
||||
URI::Generic.build(scheme: protocol, host: host, port: port, path: path).to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'rails/generators'
|
|||
module Rails
|
||||
class PostDeploymentMigrationGenerator < Rails::Generators::NamedBase
|
||||
def create_migration_file
|
||||
timestamp = Time.now.strftime('%Y%m%d%H%I%S')
|
||||
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
||||
|
||||
template "migration.rb", "db/post_migrate/#{timestamp}_#{file_name}.rb"
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@ require_dependency 'gitlab/git'
|
|||
|
||||
module Gitlab
|
||||
COM_URL = 'https://gitlab.com'.freeze
|
||||
APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}
|
||||
|
||||
def self.com?
|
||||
# Check `staging?` as well to keep parity with gitlab.com
|
||||
|
|
|
@ -4,7 +4,7 @@ module Gitlab
|
|||
class Config
|
||||
class << self
|
||||
def options
|
||||
Gitlab.config.omniauth.providers.find { |provider| provider.name == 'saml' }
|
||||
Gitlab::Auth::OAuth::Provider.config_for('saml')
|
||||
end
|
||||
|
||||
def groups
|
||||
|
|
|
@ -44,7 +44,11 @@ module Gitlab
|
|||
project.commit(head_sha)
|
||||
else
|
||||
straight = start_sha == base_sha
|
||||
CompareService.new(project, head_sha).execute(project, start_sha, straight: straight)
|
||||
|
||||
CompareService.new(project, head_sha).execute(project,
|
||||
start_sha,
|
||||
base_sha: base_sha,
|
||||
straight: straight)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -197,10 +197,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def github_omniauth_provider
|
||||
@github_omniauth_provider ||=
|
||||
Gitlab.config.omniauth.providers
|
||||
.find { |provider| provider.name == 'github' }
|
||||
.to_h
|
||||
@github_omniauth_provider ||= Gitlab::Auth::OAuth::Provider.config_for('github').to_h
|
||||
end
|
||||
|
||||
def rate_limit_counter
|
||||
|
|
|
@ -72,7 +72,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def config
|
||||
Gitlab.config.omniauth.providers.find {|provider| provider.name == "gitlab"}
|
||||
Gitlab::Auth::OAuth::Provider.config_for('gitlab')
|
||||
end
|
||||
|
||||
def gitlab_options
|
||||
|
|
|
@ -83,7 +83,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def config
|
||||
Gitlab.config.omniauth.providers.find { |provider| provider.name == "github" }
|
||||
Gitlab::Auth::OAuth::Provider.config_for('github')
|
||||
end
|
||||
|
||||
def github_options
|
||||
|
|
|
@ -7,8 +7,8 @@ module Gitlab
|
|||
|
||||
def initialize(opts = {})
|
||||
@id = opts.fetch(:id, nil)
|
||||
@filename = opts.fetch(:filename, nil)
|
||||
@basename = opts.fetch(:basename, nil)
|
||||
@filename = encode_utf8(opts.fetch(:filename, nil))
|
||||
@basename = encode_utf8(opts.fetch(:basename, nil))
|
||||
@ref = opts.fetch(:ref, nil)
|
||||
@startline = opts.fetch(:startline, nil)
|
||||
@data = encode_utf8(opts.fetch(:data, nil))
|
||||
|
|
|
@ -32,7 +32,7 @@ module GoogleApi
|
|||
private
|
||||
|
||||
def config
|
||||
Gitlab.config.omniauth.providers.find { |provider| provider.name == "google_oauth2" }
|
||||
Gitlab::Auth::OAuth::Provider.config_for('google_oauth2')
|
||||
end
|
||||
|
||||
def client
|
||||
|
|
|
@ -8,8 +8,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: gitlab 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-03-06 17:36+0100\n"
|
||||
"PO-Revision-Date: 2018-03-06 17:36+0100\n"
|
||||
"POT-Creation-Date: 2018-03-12 19:50+0100\n"
|
||||
"PO-Revision-Date: 2018-03-12 19:50+0100\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
|
@ -28,6 +28,11 @@ msgid_plural "%d commits behind"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d exporter"
|
||||
msgid_plural "%d exporters"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d issue"
|
||||
msgid_plural "%d issues"
|
||||
msgstr[0] ""
|
||||
|
@ -43,6 +48,11 @@ msgid_plural "%d merge requests"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d metric"
|
||||
msgid_plural "%d metrics"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%s additional commit has been omitted to prevent performance issues."
|
||||
msgid_plural "%s additional commits have been omitted to prevent performance issues."
|
||||
msgstr[0] ""
|
||||
|
@ -102,6 +112,9 @@ msgstr ""
|
|||
msgid "2FA enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "<strong>Removes</strong> source branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "A collection of graphs regarding Continuous Integration"
|
||||
msgstr ""
|
||||
|
||||
|
@ -111,6 +124,9 @@ msgstr ""
|
|||
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
|
||||
msgstr ""
|
||||
|
||||
msgid "A user with write access to the source branch selected this option"
|
||||
msgstr ""
|
||||
|
||||
msgid "About auto deploy"
|
||||
msgstr ""
|
||||
|
||||
|
@ -213,7 +229,7 @@ msgstr ""
|
|||
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
|
||||
msgstr ""
|
||||
|
||||
msgid "Allow edits from maintainers"
|
||||
msgid "Allow edits from maintainers."
|
||||
msgstr ""
|
||||
|
||||
msgid "Allows you to add and manage Kubernetes clusters."
|
||||
|
@ -857,6 +873,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|Learn more about environments"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Learn more about security configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Machine type"
|
||||
msgstr ""
|
||||
|
||||
|
@ -914,6 +933,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|Save changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Security"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
|
||||
msgstr ""
|
||||
|
||||
|
@ -941,6 +963,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|Something went wrong while installing %{title}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1182,6 +1207,9 @@ msgstr ""
|
|||
msgid "Create empty bare repository"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create group label"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create lists from labels. Issues with that label appear in that list."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1197,6 +1225,9 @@ msgstr ""
|
|||
msgid "Create new..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Create project label"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateNewFork|Fork"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1776,9 +1807,18 @@ msgstr ""
|
|||
msgid "Labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
|
||||
msgstr ""
|
||||
|
||||
msgid "Labels can be applied to issues and merge requests to categorize them."
|
||||
msgstr ""
|
||||
|
||||
msgid "Labels|Promote Label"
|
||||
msgstr ""
|
||||
|
||||
msgid "Labels|Promote label %{labelTitle} to Group Label?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last %d day"
|
||||
msgid_plural "Last %d days"
|
||||
msgstr[0] ""
|
||||
|
@ -1850,9 +1890,15 @@ msgstr ""
|
|||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
msgid "Manage group labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Manage labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Manage project labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Mar"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1907,6 +1953,12 @@ msgstr ""
|
|||
msgid "Milestones|Milestone %{milestoneTitle} was not found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Milestones|Promote Milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "MissingSSHKeyWarningLink|add an SSH key"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2002,6 +2054,9 @@ msgstr ""
|
|||
msgid "No file chosen"
|
||||
msgstr ""
|
||||
|
||||
msgid "No labels created yet."
|
||||
msgstr ""
|
||||
|
||||
msgid "No repository"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2251,9 +2306,15 @@ msgstr ""
|
|||
msgid "Pipelines|Loading Pipelines"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|Project cache successfully reset."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|Run Pipeline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|Something went wrong while cleaning runners cache."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|There are currently no %{scope} pipelines."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2380,9 +2441,6 @@ msgstr ""
|
|||
msgid "Project avatar in repository: %{link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project cache successfully reset."
|
||||
msgstr ""
|
||||
|
||||
msgid "Project details"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2446,6 +2504,12 @@ msgstr ""
|
|||
msgid "ProjectsDropdown|This feature requires browser localStorage support"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|%{exporters} with %{metrics} were found"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|Active"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2458,6 +2522,9 @@ msgstr ""
|
|||
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|Common metrics"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|Finding and configuring metrics..."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2479,15 +2546,9 @@ msgstr ""
|
|||
msgid "PrometheusService|Missing environment variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|Monitored"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|More information"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|No metrics are being monitored. To start monitoring, deploy to an environment."
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2503,7 +2564,16 @@ msgstr ""
|
|||
msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusService|View environments"
|
||||
msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
|
||||
msgstr ""
|
||||
|
||||
msgid "Promote"
|
||||
msgstr ""
|
||||
|
||||
msgid "Promote to Group Label"
|
||||
msgstr ""
|
||||
|
||||
msgid "Promote to Group Milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Protip:"
|
||||
|
@ -2515,9 +2585,6 @@ msgstr ""
|
|||
msgid "Public - The project can be accessed without any authentication."
|
||||
msgstr ""
|
||||
|
||||
msgid "Push access to this project is necessary in order to enable this option"
|
||||
msgstr ""
|
||||
|
||||
msgid "Push events"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3338,9 +3405,6 @@ msgstr ""
|
|||
msgid "Trigger this manual action"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to reset project cache."
|
||||
msgstr ""
|
||||
|
||||
msgid "Unlock"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3383,12 +3447,18 @@ msgstr ""
|
|||
msgid "View file @ "
|
||||
msgstr ""
|
||||
|
||||
msgid "View group labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "View labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "View open merge request"
|
||||
msgstr ""
|
||||
|
||||
msgid "View project labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "View replaced file @ "
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gitlab-org/gitlab-svgs": "^1.13.0",
|
||||
"@gitlab-org/gitlab-svgs": "^1.14.0",
|
||||
"autosize": "^4.0.0",
|
||||
"axios": "^0.17.1",
|
||||
"babel-core": "^6.26.0",
|
||||
|
|
|
@ -125,6 +125,12 @@ describe 'Merge request > User merges when pipeline succeeds', :js do
|
|||
expect(page).to have_content "canceled the automatic merge"
|
||||
end
|
||||
|
||||
it 'allows to remove source branch' do
|
||||
click_link "Remove source branch"
|
||||
|
||||
expect(page).to have_content "The source branch will be removed"
|
||||
end
|
||||
|
||||
context 'when pipeline succeeds' do
|
||||
before do
|
||||
build.success
|
||||
|
|
|
@ -27,25 +27,48 @@ describe ImportHelper do
|
|||
|
||||
describe '#provider_project_link' do
|
||||
context 'when provider is "github"' do
|
||||
let(:github_server_url) { nil }
|
||||
|
||||
before do
|
||||
setting = Settingslogic.new('name' => 'github')
|
||||
setting['url'] = github_server_url if github_server_url
|
||||
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).and_return([setting])
|
||||
end
|
||||
|
||||
context 'when provider does not specify a custom URL' do
|
||||
it 'uses default GitHub URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers)
|
||||
.and_return([Settingslogic.new('name' => 'github')])
|
||||
|
||||
expect(helper.provider_project_link('github', 'octocat/Hello-World'))
|
||||
.to include('href="https://github.com/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider specify a custom URL' do
|
||||
it 'uses custom URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers)
|
||||
.and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
|
||||
let(:github_server_url) { 'https://github.company.com' }
|
||||
|
||||
it 'uses custom URL' do
|
||||
expect(helper.provider_project_link('github', 'octocat/Hello-World'))
|
||||
.to include('href="https://github.company.com/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
|
||||
context "when custom URL contains a '/' char at the end" do
|
||||
let(:github_server_url) { 'https://github.company.com/' }
|
||||
|
||||
it "doesn't render double slash" do
|
||||
expect(helper.provider_project_link('github', 'octocat/Hello-World'))
|
||||
.to include('href="https://github.company.com/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider is missing' do
|
||||
it 'uses the default URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).and_return([])
|
||||
|
||||
expect(helper.provider_project_link('github', 'octocat/Hello-World'))
|
||||
.to include('href="https://github.com/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider is "gitea"' do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable */
|
||||
export const notesDataMock = {
|
||||
discussionsPath: '/gitlab-org/gitlab-ce/issues/26/discussions.json',
|
||||
lastFetchedAt: '1501862675',
|
||||
lastFetchedAt: 1501862675,
|
||||
markdownDocsPath: '/help/user/markdown',
|
||||
newSessionPath: '/users/sign_in?redirect_to_referer=yes',
|
||||
notesPath: '/gitlab-org/gitlab-ce/noteable/issue/98/notes',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import _ from 'underscore';
|
||||
import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
|
||||
import * as actions from '~/notes/stores/actions';
|
||||
import store from '~/notes/stores';
|
||||
import testAction from '../../helpers/vuex_action_helper';
|
||||
|
@ -145,4 +146,68 @@ describe('Actions Notes Store', () => {
|
|||
], done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('poll', () => {
|
||||
beforeEach((done) => {
|
||||
jasmine.clock().install();
|
||||
|
||||
spyOn(Vue.http, 'get').and.callThrough();
|
||||
|
||||
store.dispatch('setNotesData', notesDataMock)
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
it('calls service with last fetched state', (done) => {
|
||||
const interceptor = (request, next) => {
|
||||
next(request.respondWith(JSON.stringify({
|
||||
notes: [],
|
||||
last_fetched_at: '123456',
|
||||
}), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'poll-interval': '1000',
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
Vue.http.interceptors.push(interceptor);
|
||||
Vue.http.interceptors.push(headersInterceptor);
|
||||
|
||||
store.dispatch('poll')
|
||||
.then(() => new Promise(resolve => requestAnimationFrame(resolve)))
|
||||
.then(() => {
|
||||
expect(Vue.http.get).toHaveBeenCalledWith(jasmine.anything(), {
|
||||
url: jasmine.anything(),
|
||||
method: 'get',
|
||||
headers: {
|
||||
'X-Last-Fetched-At': undefined,
|
||||
},
|
||||
});
|
||||
expect(store.state.lastFetchedAt).toBe('123456');
|
||||
|
||||
jasmine.clock().tick(1500);
|
||||
})
|
||||
.then(() => new Promise((resolve) => {
|
||||
requestAnimationFrame(resolve);
|
||||
}))
|
||||
.then(() => {
|
||||
expect(Vue.http.get.calls.count()).toBe(2);
|
||||
expect(Vue.http.get.calls.mostRecent().args[1].headers).toEqual({
|
||||
'X-Last-Fetched-At': '123456',
|
||||
});
|
||||
})
|
||||
.then(() => store.dispatch('stopPolling'))
|
||||
.then(() => {
|
||||
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
|
||||
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -101,10 +101,21 @@ describe('Notes Store mutations', () => {
|
|||
const state = {
|
||||
notes: [],
|
||||
};
|
||||
const legacyNote = {
|
||||
id: 2,
|
||||
individual_note: true,
|
||||
notes: [{
|
||||
note: '1',
|
||||
}, {
|
||||
note: '2',
|
||||
}],
|
||||
};
|
||||
|
||||
mutations.SET_INITIAL_NOTES(state, [note]);
|
||||
mutations.SET_INITIAL_NOTES(state, [note, legacyNote]);
|
||||
expect(state.notes[0].id).toEqual(note.id);
|
||||
expect(state.notes.length).toEqual(1);
|
||||
expect(state.notes[1].notes[0].note).toBe(legacyNote.notes[0].note);
|
||||
expect(state.notes[2].notes[0].note).toBe(legacyNote.notes[1].note);
|
||||
expect(state.notes.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue';
|
||||
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
|
||||
import eventHub from '~/vue_merge_request_widget/event_hub';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
|
||||
|
@ -25,12 +26,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
targetBranchPath,
|
||||
targetBranch,
|
||||
},
|
||||
service: {
|
||||
cancelAutomaticMerge() {},
|
||||
mergeResource: {
|
||||
save() {},
|
||||
},
|
||||
},
|
||||
service: new MRWidgetService({}),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -90,18 +86,16 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
|
||||
describe('removeSourceBranch', () => {
|
||||
it('should set flag and call service then request main component to update the widget', (done) => {
|
||||
spyOn(vm.service.mergeResource, 'save').and.returnValue(new Promise((resolve) => {
|
||||
resolve({
|
||||
spyOn(vm.service, 'merge').and.returnValue(Promise.resolve({
|
||||
data: {
|
||||
status: 'merge_when_pipeline_succeeds',
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
vm.removeSourceBranch();
|
||||
setTimeout(() => {
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
|
||||
expect(vm.service.mergeResource.save).toHaveBeenCalledWith({
|
||||
expect(vm.service.merge).toHaveBeenCalledWith({
|
||||
sha,
|
||||
merge_when_pipeline_succeeds: true,
|
||||
should_remove_source_branch: true,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
import nothingToMergeComponent from '~/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge';
|
||||
import NothingToMerge from '~/vue_merge_request_widget/components/states/nothing_to_merge.vue';
|
||||
|
||||
describe('MRWidgetNothingToMerge', () => {
|
||||
describe('NothingToMerge', () => {
|
||||
describe('template', () => {
|
||||
const Component = Vue.extend(nothingToMergeComponent);
|
||||
const Component = Vue.extend(NothingToMerge);
|
||||
const newBlobPath = '/foo';
|
||||
const vm = new Component({
|
||||
el: document.createElement('div'),
|
||||
|
|
|
@ -82,6 +82,10 @@ describe('mrWidgetOptions', () => {
|
|||
});
|
||||
|
||||
describe('shouldRenderSourceBranchRemovalStatus', () => {
|
||||
beforeEach(() => {
|
||||
vm.mr.state = 'readyToMerge';
|
||||
});
|
||||
|
||||
it('should return true when cannot remove source branch and branch will be removed', () => {
|
||||
vm.mr.canRemoveSourceBranch = false;
|
||||
vm.mr.shouldRemoveSourceBranch = true;
|
||||
|
@ -102,6 +106,22 @@ describe('mrWidgetOptions', () => {
|
|||
|
||||
expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false when in merged state', () => {
|
||||
vm.mr.canRemoveSourceBranch = false;
|
||||
vm.mr.shouldRemoveSourceBranch = true;
|
||||
vm.mr.state = 'merged';
|
||||
|
||||
expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false when in nothing to merge state', () => {
|
||||
vm.mr.canRemoveSourceBranch = false;
|
||||
vm.mr.shouldRemoveSourceBranch = true;
|
||||
vm.mr.state = 'nothingToMerge';
|
||||
|
||||
expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldRenderDeployments', () => {
|
||||
|
@ -407,6 +427,7 @@ describe('mrWidgetOptions', () => {
|
|||
it('renders when user cannot remove branch and branch should be removed', (done) => {
|
||||
vm.mr.canRemoveSourceBranch = false;
|
||||
vm.mr.shouldRemoveSourceBranch = true;
|
||||
vm.mr.state = 'readyToMerge';
|
||||
|
||||
vm.$nextTick(() => {
|
||||
const tooltip = vm.$el.querySelector('.fa-question-circle');
|
||||
|
@ -419,5 +440,18 @@ describe('mrWidgetOptions', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render in merged state', (done) => {
|
||||
vm.mr.canRemoveSourceBranch = false;
|
||||
vm.mr.shouldRemoveSourceBranch = true;
|
||||
vm.mr.state = 'merged';
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.textContent).toContain('The source branch has been removed');
|
||||
expect(vm.$el.textContent).not.toContain('Removes source branch');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe API::Helpers::RelatedResourcesHelpers do
|
||||
subject(:helpers) do
|
||||
Class.new.include(described_class).new
|
||||
end
|
||||
|
||||
describe '#expose_url' do
|
||||
let(:path) { '/api/v4/awesome_endpoint' }
|
||||
subject(:url) { helpers.expose_url(path) }
|
||||
|
||||
def stub_default_url_options(protocol: 'http', host: 'example.com', port: nil)
|
||||
expect(Gitlab::Application.routes).to receive(:default_url_options)
|
||||
.and_return(protocol: protocol, host: host, port: port)
|
||||
end
|
||||
|
||||
it 'respects the protocol if it is HTTP' do
|
||||
stub_default_url_options(protocol: 'http')
|
||||
|
||||
is_expected.to start_with('http://')
|
||||
end
|
||||
|
||||
it 'respects the protocol if it is HTTPS' do
|
||||
stub_default_url_options(protocol: 'https')
|
||||
|
||||
is_expected.to start_with('https://')
|
||||
end
|
||||
|
||||
it 'accepts port to be nil' do
|
||||
stub_default_url_options(port: nil)
|
||||
|
||||
is_expected.to start_with('http://example.com/')
|
||||
end
|
||||
|
||||
it 'includes port if provided' do
|
||||
stub_default_url_options(port: 8080)
|
||||
|
||||
is_expected.to start_with('http://example.com:8080/')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -94,10 +94,12 @@ describe Gitlab::Profiler do
|
|||
|
||||
it 'strips out the private token' do
|
||||
expect(custom_logger).to receive(:add) do |severity, _progname, message|
|
||||
next if message.include?('spec/')
|
||||
|
||||
expect(severity).to eq(Logger::DEBUG)
|
||||
expect(message).to include('public').and include(described_class::FILTERED_STRING)
|
||||
expect(message).not_to include(private_token)
|
||||
end
|
||||
end.twice
|
||||
|
||||
custom_logger.debug("public #{private_token}")
|
||||
end
|
||||
|
|
|
@ -108,14 +108,26 @@ describe Gitlab::ProjectSearchResults do
|
|||
|
||||
context 'when the search returns non-ASCII data' do
|
||||
context 'with UTF-8' do
|
||||
let(:results) { project.repository.search_files_by_content("файл", 'master') }
|
||||
let(:results) { project.repository.search_files_by_content('файл', 'master') }
|
||||
|
||||
it 'returns results as UTF-8' do
|
||||
expect(subject.filename).to eq('encoding/russian.rb')
|
||||
expect(subject.basename).to eq('encoding/russian')
|
||||
expect(subject.ref).to eq('master')
|
||||
expect(subject.startline).to eq(1)
|
||||
expect(subject.data).to eq("Хороший файл")
|
||||
expect(subject.data).to eq('Хороший файл')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with UTF-8 in the filename' do
|
||||
let(:results) { project.repository.search_files_by_content('webhook', 'master') }
|
||||
|
||||
it 'returns results as UTF-8' do
|
||||
expect(subject.filename).to eq('encoding/テスト.txt')
|
||||
expect(subject.basename).to eq('encoding/テスト')
|
||||
expect(subject.ref).to eq('master')
|
||||
expect(subject.startline).to eq(3)
|
||||
expect(subject.data).to include('WebHookの確認')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,33 +37,51 @@ describe Compare do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#base_commit' do
|
||||
let(:base_commit) { Commit.new(another_sample_commit, project) }
|
||||
describe '#base_commit_sha' do
|
||||
it 'returns @base_sha if it is present' do
|
||||
expect(project).not_to receive(:merge_base_commit)
|
||||
|
||||
it 'returns project merge base commit' do
|
||||
expect(project).to receive(:merge_base_commit).with(start_commit.id, head_commit.id).and_return(base_commit)
|
||||
sha = double
|
||||
service = described_class.new(raw_compare, project, base_sha: sha)
|
||||
|
||||
expect(subject.base_commit).to eq(base_commit)
|
||||
expect(service.base_commit_sha).to eq(sha)
|
||||
end
|
||||
|
||||
it 'fetches merge base SHA from repo when @base_sha is nil' do
|
||||
expect(project).to receive(:merge_base_commit)
|
||||
.with(start_commit.id, head_commit.id)
|
||||
.once
|
||||
.and_call_original
|
||||
|
||||
expect(subject.base_commit_sha)
|
||||
.to eq(project.repository.merge_base(start_commit.id, head_commit.id))
|
||||
end
|
||||
|
||||
it 'is memoized on first call' do
|
||||
expect(project).to receive(:merge_base_commit)
|
||||
.with(start_commit.id, head_commit.id)
|
||||
.once
|
||||
.and_call_original
|
||||
|
||||
3.times { subject.base_commit_sha }
|
||||
end
|
||||
|
||||
it 'returns nil if there is no start_commit' do
|
||||
expect(subject).to receive(:start_commit).and_return(nil)
|
||||
|
||||
expect(subject.base_commit).to eq(nil)
|
||||
expect(subject.base_commit_sha).to eq(nil)
|
||||
end
|
||||
|
||||
it 'returns nil if there is no head commit' do
|
||||
expect(subject).to receive(:head_commit).and_return(nil)
|
||||
|
||||
expect(subject.base_commit).to eq(nil)
|
||||
expect(subject.base_commit_sha).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#diff_refs' do
|
||||
it 'uses base_commit sha as base_sha' do
|
||||
expect(subject).to receive(:base_commit).at_least(:once).and_call_original
|
||||
|
||||
expect(subject.diff_refs.base_sha).to eq(subject.base_commit.id)
|
||||
it 'uses base_commit_sha sha as base_sha' do
|
||||
expect(subject.diff_refs.base_sha).to eq(subject.base_commit_sha)
|
||||
end
|
||||
|
||||
it 'uses start_commit sha as start_sha' do
|
||||
|
|
|
@ -137,7 +137,7 @@ sast:container:
|
|||
dast:
|
||||
stage: dast
|
||||
allow_failure: true
|
||||
image: owasp/zap2docker-stable
|
||||
image: registry.gitlab.com/gitlab-org/security-products/zaproxy
|
||||
variables:
|
||||
POSTGRES_DB: "false"
|
||||
script:
|
||||
|
|
|
@ -54,9 +54,9 @@
|
|||
lodash "^4.2.0"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@gitlab-org/gitlab-svgs@^1.13.0":
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.13.0.tgz#9e856ef9fa7bbe49b2dce9789187a89e11311215"
|
||||
"@gitlab-org/gitlab-svgs@^1.14.0":
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.14.0.tgz#b4a5cca3106f33224c5486cf674ba3b70cee727e"
|
||||
|
||||
"@types/jquery@^2.0.40":
|
||||
version "2.0.48"
|
||||
|
|
Loading…
Reference in New Issue