Commit e7238677 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent c2b98d3d
...@@ -2,6 +2,7 @@ extends: ...@@ -2,6 +2,7 @@ extends:
- '@gitlab' - '@gitlab'
- plugin:promise/recommended - plugin:promise/recommended
- plugin:no-jquery/slim - plugin:no-jquery/slim
- plugin:no-jquery/deprecated-3.4
globals: globals:
__webpack_public_path__: true __webpack_public_path__: true
gl: false gl: false
...@@ -48,8 +49,10 @@ rules: ...@@ -48,8 +49,10 @@ rules:
no-jquery/no-animate: off no-jquery/no-animate: off
# all offenses of no-jquery/no-animate-toggle are false positives ( $toast.show() ) # all offenses of no-jquery/no-animate-toggle are false positives ( $toast.show() )
no-jquery/no-animate-toggle: off no-jquery/no-animate-toggle: off
no-jquery/no-event-shorthand: off
no-jquery/no-fade: off no-jquery/no-fade: off
no-jquery/no-serialize: error no-jquery/no-serialize: error
no-jquery/no-sizzle: off
promise/always-return: off promise/always-return: off
promise/no-callback-in-promise: off promise/no-callback-in-promise: off
overrides: overrides:
......
...@@ -220,9 +220,6 @@ export default { ...@@ -220,9 +220,6 @@ export default {
this.assignedDiscussions = false; this.assignedDiscussions = false;
this.fetchData(false); this.fetchData(false);
}, },
isLatestVersion() {
return window.location.search.indexOf('diff_id') === -1;
},
startDiffRendering() { startDiffRendering() {
requestIdleCallback( requestIdleCallback(
() => { () => {
...@@ -232,7 +229,7 @@ export default { ...@@ -232,7 +229,7 @@ export default {
); );
}, },
fetchData(toggleTree = true) { fetchData(toggleTree = true) {
if (this.isLatestVersion() && this.glFeatures.diffsBatchLoad) { if (this.glFeatures.diffsBatchLoad) {
this.fetchDiffFilesMeta() this.fetchDiffFilesMeta()
.then(() => { .then(() => {
if (toggleTree) this.hideTreeListIfJustOneFile(); if (toggleTree) this.hideTreeListIfJustOneFile();
......
...@@ -90,14 +90,13 @@ export const fetchDiffFiles = ({ state, commit }) => { ...@@ -90,14 +90,13 @@ export const fetchDiffFiles = ({ state, commit }) => {
}; };
export const fetchDiffFilesBatch = ({ commit, state }) => { export const fetchDiffFilesBatch = ({ commit, state }) => {
const baseUrl = `${state.endpointBatch}?per_page=${DIFFS_PER_PAGE}`;
const url = page => (page ? `${baseUrl}&page=${page}` : baseUrl);
commit(types.SET_BATCH_LOADING, true); commit(types.SET_BATCH_LOADING, true);
const getBatch = page => const getBatch = page =>
axios axios
.get(url(page)) .get(state.endpointBatch, {
params: { page, per_page: DIFFS_PER_PAGE, w: state.showWhitespace ? '0' : '1' },
})
.then(({ data: { pagination, diff_files } }) => { .then(({ data: { pagination, diff_files } }) => {
commit(types.SET_DIFF_DATA_BATCH, { diff_files }); commit(types.SET_DIFF_DATA_BATCH, { diff_files });
commit(types.SET_BATCH_LOADING, false); commit(types.SET_BATCH_LOADING, false);
......
...@@ -115,5 +115,30 @@ export const isOnDefaultBranch = (_state, getters) => ...@@ -115,5 +115,30 @@ export const isOnDefaultBranch = (_state, getters) =>
export const canPushToBranch = (_state, getters) => export const canPushToBranch = (_state, getters) =>
getters.currentBranch && getters.currentBranch.can_push; getters.currentBranch && getters.currentBranch.can_push;
export const isFileDeletedAndReadded = (state, getters) => path => {
const stagedFile = getters.getStagedFile(path);
const file = state.entries[path];
return Boolean(stagedFile && stagedFile.deleted && file.tempFile);
};
// checks if any diff exists in the staged or unstaged changes for this path
export const getDiffInfo = (state, getters) => path => {
const stagedFile = getters.getStagedFile(path);
const file = state.entries[path];
const renamed = file.prevPath ? file.path !== file.prevPath : false;
const deletedAndReadded = getters.isFileDeletedAndReadded(path);
const deleted = deletedAndReadded ? false : file.deleted;
const tempFile = deletedAndReadded ? false : file.tempFile;
const changed = file.content !== (deletedAndReadded ? stagedFile.raw : file.raw);
return {
exists: changed || renamed || deleted || tempFile,
changed,
renamed,
deleted,
tempFile,
};
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests // prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {}; export default () => {};
...@@ -3,11 +3,16 @@ import ZenMode from '~/zen_mode'; ...@@ -3,11 +3,16 @@ import ZenMode from '~/zen_mode';
import LineHighlighter from '~/line_highlighter'; import LineHighlighter from '~/line_highlighter';
import BlobViewer from '~/blob/viewer'; import BlobViewer from '~/blob/viewer';
import snippetEmbed from '~/snippet/snippet_embed'; import snippetEmbed from '~/snippet/snippet_embed';
import initSnippetsApp from '~/snippets';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new LineHighlighter(); // eslint-disable-line no-new if (!gon.features.snippetsVue) {
new BlobViewer(); // eslint-disable-line no-new new LineHighlighter(); // eslint-disable-line no-new
initNotes(); new BlobViewer(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new initNotes();
snippetEmbed(); new ZenMode(); // eslint-disable-line no-new
snippetEmbed();
} else {
initSnippetsApp();
}
}); });
...@@ -104,10 +104,10 @@ export default { ...@@ -104,10 +104,10 @@ export default {
return acc.concat({ return acc.concat({
name, name,
path, path,
to: `/tree/${this.ref}${path}`, to: `/-/tree/${this.ref}${path}`,
}); });
}, },
[{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}/` }], [{ name: this.projectShortPath, path: '/', to: `/-/tree/${this.ref}/` }],
); );
}, },
canCreateMrFromFork() { canCreateMrFromFork() {
......
...@@ -15,7 +15,7 @@ export default { ...@@ -15,7 +15,7 @@ export default {
const splitArray = this.path.split('/'); const splitArray = this.path.split('/');
splitArray.pop(); splitArray.pop();
return { path: `/tree/${this.commitRef}/${splitArray.join('/')}` }; return { path: `/-/tree/${this.commitRef}/${splitArray.join('/')}` };
}, },
}, },
methods: { methods: {
......
...@@ -84,7 +84,7 @@ export default { ...@@ -84,7 +84,7 @@ export default {
}, },
computed: { computed: {
routerLinkTo() { routerLinkTo() {
return this.isFolder ? { path: `/tree/${this.ref}/${this.path}` } : null; return this.isFolder ? { path: `/-/tree/${this.ref}/${this.path}` } : null;
}, },
iconName() { iconName() {
return `fa-${getIconName(this.type, this.path)}`; return `fa-${getIconName(this.type, this.path)}`;
......
...@@ -12,7 +12,7 @@ export default function createRouter(base, baseRef) { ...@@ -12,7 +12,7 @@ export default function createRouter(base, baseRef) {
base: joinPaths(gon.relative_url_root || '', base), base: joinPaths(gon.relative_url_root || '', base),
routes: [ routes: [
{ {
path: `/tree/${baseRef}(/.*)?`, path: `/-/tree/${baseRef}(/.*)?`,
name: 'treePath', name: 'treePath',
component: TreePage, component: TreePage,
props: route => ({ props: route => ({
......
<script> <script>
import getSnippet from '../queries/getSnippet.query.graphql'; import GetSnippetQuery from '../queries/snippet.query.graphql';
import SnippetHeader from './snippet_header.vue';
import { GlLoadingIcon } from '@gitlab/ui';
export default { export default {
components: {
SnippetHeader,
GlLoadingIcon,
},
apollo: { apollo: {
snippetData: { snippet: {
query: getSnippet, query: GetSnippetQuery,
variables() { variables() {
return { return {
ids: this.snippetGid, ids: this.snippetGid,
...@@ -21,11 +27,24 @@ export default { ...@@ -21,11 +27,24 @@ export default {
}, },
data() { data() {
return { return {
snippetData: {}, snippet: {},
}; };
}, },
computed: {
isLoading() {
return this.$apollo.queries.snippet.loading;
},
},
}; };
</script> </script>
<template> <template>
<div class="js-snippet-view"></div> <div class="js-snippet-view">
<gl-loading-icon
v-if="isLoading"
:label="__('Loading snippet')"
:size="2"
class="loading-animation prepend-top-20 append-bottom-20"
/>
<snippet-header v-else :snippet="snippet" />
</div>
</template> </template>
<script>
import { __ } from '~/locale';
import {
GlAvatar,
GlIcon,
GlSprintf,
GlButton,
GlModal,
GlAlert,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
} from '@gitlab/ui';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql';
import CanCreatePersonalSnippet from '../queries/userPermissions.query.graphql';
import CanCreateProjectSnippet from '../queries/projectPermissions.query.graphql';
export default {
components: {
GlAvatar,
GlIcon,
GlSprintf,
GlButton,
GlModal,
GlAlert,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
TimeAgoTooltip,
},
apollo: {
canCreateSnippet: {
query() {
return this.snippet.project ? CanCreateProjectSnippet : CanCreatePersonalSnippet;
},
variables() {
return {
fullPath: this.snippet.project ? this.snippet.project.fullPath : undefined,
};
},
update(data) {
return this.snippet.project
? data.project.userPermissions.createSnippet
: data.currentUser.userPermissions.createSnippet;
},
},
},
props: {
snippet: {
type: Object,
required: true,
},
},
data() {
return {
isDeleting: false,
errorMessage: '',
canCreateSnippet: false,
};
},
computed: {
personalSnippetActions() {
return [
{
condition: this.snippet.userPermissions.updateSnippet,
text: __('Edit'),
href: this.editLink,
click: undefined,
variant: 'outline-info',
cssClass: undefined,
},
{
condition: this.snippet.userPermissions.adminSnippet,
text: __('Delete'),
href: undefined,
click: this.showDeleteModal,
variant: 'outline-danger',
cssClass: 'btn-inverted btn-danger ml-2',
},
{
condition: this.canCreateSnippet,
text: __('New snippet'),
href: this.snippet.project
? `${this.snippet.project.webUrl}/snippets/new`
: '/snippets/new',
click: undefined,
variant: 'outline-success',
cssClass: 'btn-inverted btn-success ml-2',
},
];
},
editLink() {
return `${this.snippet.webUrl}/edit`;
},
visibility() {
return this.snippet.visibilityLevel;
},
snippetVisibilityLevelDescription() {
switch (this.visibility) {
case 'private':
return this.snippet.project !== null
? __('The snippet is visible only to project members.')
: __('The snippet is visible only to me.');
case 'internal':
return __('The snippet is visible to any logged in user.');
default:
return __('The snippet can be accessed without any authentication.');
}
},
visibilityLevelIcon() {
switch (this.visibility) {
case 'private':
return 'lock';
case 'internal':
return 'shield';
default:
return 'earth';
}
},
},
methods: {
redirectToSnippets() {
window.location.pathname = 'dashboard/snippets';
},
closeDeleteModal() {
this.$refs.deleteModal.hide();
},
showDeleteModal() {
this.$refs.deleteModal.show();
},
deleteSnippet() {
this.isDeleting = true;
this.$apollo
.mutate({
mutation: DeleteSnippetMutation,
variables: { id: this.snippet.id },
})
.then(() => {
this.isDeleting = false;
this.errorMessage = undefined;
this.closeDeleteModal();
this.redirectToSnippets();
})
.catch(err => {
this.isDeleting = false;
this.errorMessage = err.message;
});
},
},
};
</script>
<template>
<div class="detail-page-header">
<div class="detail-page-header-body">
<div
class="snippet-box qa-snippet-box has-tooltip d-flex align-items-center append-right-5 mb-1"
:title="snippetVisibilityLevelDescription"
data-container="body"
>
<span class="sr-only">
{{ s__(`VisibilityLevel|${visibility}`) }}
</span>
<gl-icon :name="visibilityLevelIcon" :size="14" />
</div>
<div class="creator">
<gl-sprintf message="Authored %{timeago} by %{author}">
<template #timeago>
<time-ago-tooltip
:time="snippet.createdAt"
tooltip-placement="bottom"
css-class="snippet_updated_ago"
/>
</template>
<template #author>
<a :href="snippet.author.webUrl" class="d-inline">
<gl-avatar :size="24" :src="snippet.author.avatarUrl" />
<span class="bold">{{ snippet.author.name }}</span>
</a>
</template>
</gl-sprintf>
</div>
</div>
<div class="detail-page-header-actions">
<div class="d-none d-sm-block">
<template v-for="(action, index) in personalSnippetActions">
<gl-button
v-if="action.condition"
:key="index"
:variant="action.variant"
:class="action.cssClass"
:href="action.href || undefined"
@click="action.click ? action.click() : undefined"
>
{{ action.text }}
</gl-button>
</template>
</div>
<div class="d-block d-sm-none dropdown">
<gl-dropdown :text="__('Options')" class="w-100" toggle-class="text-center">
<gl-dropdown-item
v-for="(action, index) in personalSnippetActions"
:key="index"
:href="action.href || undefined"
@click="action.click ? action.click() : undefined"
>{{ action.text }}</gl-dropdown-item
>
</gl-dropdown>
</div>
</div>
<gl-modal ref="deleteModal" modal-id="delete-modal" title="Example title">
<template #modal-title>{{ __('Delete snippet?') }}</template>
<gl-alert v-if="errorMessage" variant="danger" class="mb-2" @dismiss="errorMessage = ''">{{
errorMessage
}}</gl-alert>
<gl-sprintf message="Are you sure you want to delete %{name}?">
<template #name
><strong>{{ snippet.title }}</strong></template
>
</gl-sprintf>
<template #modal-footer>
<gl-button @click="closeDeleteModal">{{ __('Cancel') }}</gl-button>
<gl-button
variant="danger"
:disabled="isDeleting"
data-qa-selector="delete_snippet_button"
@click="deleteSnippet"
>
<gl-loading-icon v-if="isDeleting" inline />
{{ __('Delete snippet') }}
</gl-button>
</template>
</gl-modal>
</div>
</template>
fragment Author on Snippet {
author {
name,
avatarUrl,
username,
webUrl
}
}
\ No newline at end of file
fragment Project on Snippet {
project {
fullPath
webUrl
}
}
\ No newline at end of file
fragment SnippetBase on Snippet {
id
title
description
createdAt
updatedAt
visibilityLevel
webUrl
userPermissions {
adminSnippet
updateSnippet
}
}
\ No newline at end of file
mutation DeleteSnippet($id: ID!) {
destroySnippet(input: {id: $id}) {
errors
}
}
\ No newline at end of file
query getSnippet($ids: [ID!]) {
snippets(ids: $ids) {
edges {
node {
title
description
createdAt
updatedAt
visibility
}
}
}
}
query CanCreateProjectSnippet($fullPath: ID!) {
project(fullPath: $fullPath) {
userPermissions {
createSnippet
}
}
}
\ No newline at end of file
#import '../fragments/snippetBase.fragment.graphql'
#import '../fragments/project.fragment.graphql'
#import '../fragments/author.fragment.graphql'
query GetSnippetQuery($ids: [ID!]) {
snippets(ids: $ids) {
edges {
node {
...SnippetBase
...Project
...Author
}
}
}
}
query CanCreatePersonalSnippet {
currentUser {
userPermissions {
createSnippet
}
}
}
\ No newline at end of file
...@@ -79,10 +79,10 @@ export default { ...@@ -79,10 +79,10 @@ export default {
return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`; return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`;
}, },
fullOldPath() { fullOldPath() {
return `${this.basePath}${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`; return `${this.basePath}${this.projectPath}/-/raw/${this.oldSha}/${this.oldPath}`;
}, },
fullNewPath() { fullNewPath() {
return `${this.basePath}${this.projectPath}/raw/${this.newSha}/${this.newPath}`; return `${this.basePath}${this.projectPath}/-/raw/${this.newSha}/${this.newPath}`;
}, },
}, },
}; };
......
...@@ -40,12 +40,12 @@ ...@@ -40,12 +40,12 @@
// Classes using mixins coming from @gitlab-ui // Classes using mixins coming from @gitlab-ui
// can be removed once https://gitlab.com/gitlab-org/gitlab/merge_requests/19021 has been merged // can be removed once https://gitlab.com/gitlab-org/gitlab/merge_requests/19021 has been merged
.gl-bg-blue-500 { @include gl-bg-blue-500; }
.gl-bg-red-100 { @include gl-bg-red-100; } .gl-bg-red-100 { @include gl-bg-red-100; }
.gl-bg-orange-100 { @include gl-bg-orange-100; } .gl-bg-orange-100 { @include gl-bg-orange-100; }
.gl-bg-gray-100 { @include gl-bg-gray-100; } .gl-bg-gray-100 { @include gl-bg-gray-100; }
.gl-bg-green-100 { @include gl-bg-green-100;} .gl-bg-green-100 { @include gl-bg-green-100;}
.gl-text-blue-500 { @include gl-text-blue-500; }
.gl-text-gray-900 { @include gl-text-gray-900; } .gl-text-gray-900 { @include gl-text-gray-900; }
.gl-text-red-700 { @include gl-text-red-700; } .gl-text-red-700 { @include gl-text-red-700; }
.gl-text-orange-700 { @include gl-text-orange-700; } .gl-text-orange-700 { @include gl-text-orange-700; }
......
...@@ -43,6 +43,8 @@ module SubmoduleHelper ...@@ -43,6 +43,8 @@ module SubmoduleHelper
elsif github_dot_com_url?(url) elsif github_dot_com_url?(url)
standard_links('github.com', namespace, project, submodule_item_id) standard_links('github.com', namespace, project, submodule_item_id)
elsif gitlab_dot_com_url?(url) elsif gitlab_dot_com_url?(url)
# This need to be replaced with /-/tree routing once one is landed on
# GitLab.com. Issue https://gitlab.com/gitlab-org/gitlab/issues/42764
standard_links('gitlab.com', namespace, project, submodule_item_id) standard_links('gitlab.com', namespace, project, submodule_item_id)
else else
[sanitize_submodule_url(url), nil] [sanitize_submodule_url(url), nil]
......
...@@ -38,13 +38,13 @@ module TreeHelper ...@@ -38,13 +38,13 @@ module TreeHelper
# many paths, as with a repository tree that has thousands of items. # many paths, as with a repository tree that has thousands of items.
def fast_project_blob_path(project, blob_path) def fast_project_blob_path(project, blob_path)
ActionDispatch::Journey::Router::Utils.escape_path( ActionDispatch::Journey::Router::Utils.escape_path(
File.join(relative_url_root, project.path_with_namespace, 'blob', blob_path) File.join(relative_url_root, project.path_with_namespace, '-', 'blob', blob_path)
) )
end end
def fast_project_tree_path(project, tree_path) def fast_project_tree_path(project, tree_path)
ActionDispatch::Journey::Router::Utils.escape_path( ActionDispatch::Journey::Router::Utils.escape_path(
File.join(relative_url_root, project.path_with_namespace, 'tree', tree_path) File.join(relative_url_root, project.path_with_namespace, '-', 'tree', tree_path)
) )
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module Clusters module Clusters
module Applications module Applications
class Knative < ApplicationRecord class Knative < ApplicationRecord
VERSION = '0.7.0' VERSION = '0.9.0'
REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts' REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'
METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml' METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'
FETCH_IP_ADDRESS_DELAY = 30.seconds FETCH_IP_ADDRESS_DELAY = 30.seconds
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
= f.check_box :external do = f.check_box :external do
External External
%p.light %p.light
External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects or groups. External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets.
%row.hidden#warning_external_automatically_set.hidden %row.hidden#warning_external_automatically_set.hidden
.badge.badge-warning.text-white .badge.badge-warning.text-white
= _('Automatically marked as default internal user') = _('Automatically marked as default internal user')
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
- if current_user && current_user.snippets.any? || @snippets.any? - if current_user && current_user.snippets.any? || @snippets.any?
.page-title-controls .page-title-controls
= link_to _("New snippet"), new_snippet_path, class: "btn btn-success", title: _("New snippet") - if can?(current_user, :create_personal_snippet)
= link_to _("New snippet"), new_snippet_path, class: "btn btn-success", title: _("New snippet")
.top-area .top-area
%ul.nav-links.nav.nav-tabs %ul.nav-links.nav.nav-tabs
......
- @hide_top_links = true - @hide_top_links = true
- page_title "Snippets" - page_title "Snippets"
- header_title "Snippets", dashboard_snippets_path - header_title "Snippets", dashboard_snippets_path
- button_path = new_snippet_path if can?(current_user, :create_personal_snippet)
= render 'dashboard/snippets_head' = render 'dashboard/snippets_head'
- if current_user.snippets.exists? - if current_user.snippets.exists?
...@@ -9,4 +10,4 @@ ...@@ -9,4 +10,4 @@
- if current_user.snippets.exists? - if current_user.snippets.exists?
= render partial: 'shared/snippets/list', locals: { link_project: true } = render partial: 'shared/snippets/list', locals: { link_project: true }
- else - else
= render 'shared/empty_states/snippets', button_path: new_snippet_path = render 'shared/empty_states/snippets', button_path: button_path
...@@ -16,6 +16,6 @@ ...@@ -16,6 +16,6 @@
%a.provider-btn %a.provider-btn
= s_('Profiles|Active') = s_('Profiles|Active')
- elsif link_allowed - elsif link_allowed
= link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn gl-bg-blue-500' do = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn gl-text-blue-500' do
= s_('Profiles|Connect') = s_('Profiles|Connect')
= render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: group_saml_identities = render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: group_saml_identities
...@@ -8,8 +8,7 @@ ...@@ -8,8 +8,7 @@
- if can?(current_user, :create_project_snippet, @project) - if can?(current_user, :create_project_snippet, @project)
.nav-controls .nav-controls
- if can?(current_user, :create_project_snippet, @project) = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-success", title: _("New snippet")
= link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-success", title: _("New snippet")
= render 'shared/snippets/list' = render 'shared/snippets/list'
- else - else
......
...@@ -3,13 +3,16 @@ ...@@ -3,13 +3,16 @@
- breadcrumb_title @snippet.to_reference - breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets") - page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
= render 'shared/snippets/header' - if Feature.enabled?(:snippets_vue)
#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id} }
- else
= render 'shared/snippets/header'
.project-snippets .project-snippets
%article.file-holder.snippet-file-content %article.file-holder.snippet-file-content
= render 'shared/snippets/blob' = render 'shared/snippets/blob'
.row-content-block.top-block.content-component-block .row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true = render 'award_emoji/awards_block', awardable: @snippet, inline: true
#notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => true #notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => true
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
%p %p
= s_('SnippetsEmptyState|They can be either public or private.') = s_('SnippetsEmptyState|They can be either public or private.')
.text-center .text-center
= link_to s_('SnippetsEmptyState|New snippet'), button_path, class: 'btn btn-success', title: s_('SnippetsEmptyState|New snippet'), id: 'new_snippet_link' - if button_path
= link_to s_('SnippetsEmptyState|New snippet'), button_path, class: 'btn btn-success', title: s_('SnippetsEmptyState|New snippet'), id: 'new_snippet_link'
- unless current_page?(dashboard_snippets_path) - unless current_page?(dashboard_snippets_path)
= link_to s_('SnippetsEmptyState|Explore public snippets'), explore_snippets_path, class: 'btn btn-default', title: s_('SnippetsEmptyState|Explore public snippets') = link_to s_('SnippetsEmptyState|Explore public snippets'), explore_snippets_path, class: 'btn btn-default', title: s_('SnippetsEmptyState|Explore public snippets')
- else - else
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- current_user_empty_message_header = s_('UserProfile|You haven\'t created any snippets.') - current_user_empty_message_header = s_('UserProfile|You haven\'t created any snippets.')
- current_user_empty_message_description = s_('UserProfile|Snippets in GitLab can either be private, internal, or public.') - current_user_empty_message_description = s_('UserProfile|Snippets in GitLab can either be private, internal, or public.')
- primary_button_label = _('New snippet') - primary_button_label = _('New snippet')
- primary_button_link = new_snippet_path - primary_button_link = new_snippet_path if can?(current_user, :create_personal_snippet)
- visitor_empty_message = s_('UserProfile|No snippets found.') - visitor_empty_message = s_('UserProfile|No snippets found.')
.snippets-list-holder .snippets-list-holder
......
...@@ -21,6 +21,7 @@ const plugins = [ ...@@ -21,6 +21,7 @@ const plugins = [
'@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-json-strings', '@babel/plugin-proposal-json-strings',
'@babel/plugin-proposal-private-methods', '@babel/plugin-proposal-private-methods',
'@babel/plugin-proposal-optional-chaining',
]; ];
// add code coverage tooling if necessary // add code coverage tooling if necessary
......
---
title: "!21542 Part 1: Add new utils for Web IDE store"
merge_request: 21673
author:
type: fixed
---
title: Match external user new snippet button visibility to permissions
merge_request: 21718
author:
type: fixed
---
title: Move repository routes under - scope
merge_request: 20455
author:
type: deprecated
---
title: Update Knative to 0.9.0
merge_request: 21361
author: cab105
type: added
---
title: Added migration which adds service desk username column
merge_request: 21733
author:
type: added
...@@ -209,6 +209,6 @@ panel_groups: ...@@ -209,6 +209,6 @@ panel_groups:
weight: 1 weight: 1
metrics: metrics:
- id: system_metrics_knative_function_invocation_count - id: system_metrics_knative_function_invocation_count
query_range: 'sum(ceil(rate(istio_requests_total{destination_service_namespace="%{kube_namespace}", destination_app=~"%{function_name}.*"}[1m])*60))' query_range: 'sum(ceil(rate(istio_requests_total{destination_service_namespace="%{kube_namespace}", destination_service=~"%{function_name}.*"}[1m])*60))'
label: invocations / minute label: invocations / minute
unit: requests unit: requests
...@@ -554,7 +554,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -554,7 +554,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
:forks, :group_links, :import, :avatar, :mirror, :forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers, :cycle_analytics, :mattermost, :variables, :triggers,
:environments, :protected_environments, :error_tracking, :environments, :protected_environments, :error_tracking,
:serverless, :clusters, :audit_events, :wikis, :merge_requests) :serverless, :clusters, :audit_events, :wikis, :merge_requests,
:blob, :tree, :raw, :blame, :commits, :create_dir, :find_file, :files)
end end
# rubocop: disable Cop/PutProjectRoutesUnderScope # rubocop: disable Cop/PutProjectRoutesUnderScope
......
...@@ -65,7 +65,7 @@ scope format: false do ...@@ -65,7 +65,7 @@ scope format: false do
resources :protected_tags, only: [:index, :show, :create, :update, :destroy] resources :protected_tags, only: [:index, :show, :create, :update, :destroy]
end end
scope constraints: { id: /[^\0]+/ } do scope path: '-', constraints: { id: /[^\0]+/ } do
scope controller: :blob do scope controller: :blob do
get '/new/*id', action: :new, as: :new_blob get '/new/*id', action: :new, as: :new_blob
post '/create/*id', action: :create, as: :create_blob post '/create/*id', action: :create, as: :create_blob
......
# frozen_string_literal: true
class AddServiceDeskUsername < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :service_desk_settings, :outgoing_name, :string, limit: 255
end
end
# frozen_string_literal: true
class Knative09PrometheusUpdate < ActiveRecord::Migration[5.2]
DOWNTIME = false
def up
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end
def down
# no-op
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_12_16_183532) do ActiveRecord::Schema.define(version: 2019_12_17_160632) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm" enable_extension "pg_trgm"
...@@ -3663,6 +3663,7 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do ...@@ -3663,6 +3663,7 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
create_table "service_desk_settings", primary_key: "project_id", id: :bigint, default: nil, force: :cascade do |t| create_table "service_desk_settings", primary_key: "project_id", id: :bigint, default: nil, force: :cascade do |t|
t.string "issue_template_key", limit: 255 t.string "issue_template_key", limit: 255
t.string "outgoing_name", limit: 255
end end
create_table "services", id: :serial, force: :cascade do |t| create_table "services", id: :serial, force: :cascade do |t|
......
...@@ -10,11 +10,6 @@ migrations automatically reschedule themselves for a later point in time. ...@@ -10,11 +10,6 @@ migrations automatically reschedule themselves for a later point in time.
## When To Use Background Migrations ## When To Use Background Migrations
> **Note:**
> When adding background migrations _you must_ make sure they are announced in the
> monthly release post along with an estimate of how long it will take to complete
> the migrations.
In the vast majority of cases you will want to use a regular Rails migration In the vast majority of cases you will want to use a regular Rails migration
instead. Background migrations should be used when migrating _data_ in instead. Background migrations should be used when migrating _data_ in
tables that have so many rows this process would take hours when performed in a tables that have so many rows this process would take hours when performed in a
...@@ -34,6 +29,11 @@ Some examples where background migrations can be useful: ...@@ -34,6 +29,11 @@ Some examples where background migrations can be useful:
- Populating one column based on JSON stored in another column. - Populating one column based on JSON stored in another column.
- Migrating data that depends on the output of external services (e.g. an API). - Migrating data that depends on the output of external services (e.g. an API).
> **Note:**
> If the background migration is part of an important upgrade, make sure it's announced
> in the release post. Discuss with your Project Manager if you're not sure the migration falls
> into this category.
## Isolation ## Isolation
Background migrations must be isolated and can not use application code (e.g. Background migrations must be isolated and can not use application code (e.g.
......
...@@ -39,43 +39,31 @@ To distinguish queries from mutations and fragments, the following naming conven ...@@ -39,43 +39,31 @@ To distinguish queries from mutations and fragments, the following naming conven
- `addUser.mutation.graphql` for mutations; - `addUser.mutation.graphql` for mutations;
- `basicUser.fragment.graphql` for fragments. - `basicUser.fragment.graphql` for fragments.
GraphQL:
- Queries are stored in `(ee/)app/assets/javascripts/` under the feature. For example, `respository/queries`. Frontend components can use these stored queries.
- Mutations are stored in
`(ee/)app/assets/javascripts/<subfolders>/<name of mutation>.mutation.graphql`.
### Fragments ### Fragments
Fragments are a way to make your complex GraphQL queries more readable and re-usable. Fragments are a way to make your complex GraphQL queries more readable and re-usable. Here is an example of GraphQL fragment:
They can be stored in a separate file and imported.
For example, a fragment that references another fragment: ```javascript
fragment DesignListItem on Design {
```ruby
fragment BaseEpic on Epic {
id id
iid image
title event
webPath filename
relativePosition notesCount
userPermissions {
adminEpic
createEpic
}
} }
```
fragment EpicNode on Epic { Fragments can be stored in separate files, imported and used in queries, mutations or other fragments.
...BaseEpic
state ```javascript
reference(full: true) #import "./designList.fragment.graphql"
relationPath #import "./diffRefs.fragment.graphql"
createdAt
closedAt fragment DesignItem on Design {
hasChildren ...DesignListItem
hasIssues fullPath
group { diffRefs {
fullPath ...DesignDiffRefs
} }
} }
``` ```
......
...@@ -253,7 +253,7 @@ project and should only have access to that project. ...@@ -253,7 +253,7 @@ project and should only have access to that project.
External users: External users:
- Cannot create groups or projects. - Cannot create groups, projects, or personal snippets.
- Can only access projects to which they are explicitly granted access, - Can only access projects to which they are explicitly granted access,
thus hiding all other internal or private ones from them (like being thus hiding all other internal or private ones from them (like being
logged out). logged out).
......
...@@ -152,11 +152,18 @@ module Banzai ...@@ -152,11 +152,18 @@ module Banzai
def rebuild_relative_uri(uri) def rebuild_relative_uri(uri)
file_path = nested_file_path_if_exists(uri) file_path = nested_file_path_if_exists(uri)
resource_type = uri_type(file_path)
# Repository routes are under /-/ scope now.
# Since we craft a path without using route helpers we must
# ensure - is added here.
prefix = '-' if %w(tree blob raw commits).include?(resource_type.to_s)
uri.path = [ uri.path = [
relative_url_root, relative_url_root,
project.full_path, project.full_path,
uri_type(file_path), prefix,
resource_type,
Addressable::URI.escape(ref).gsub('#', '%23'), Addressable::URI.escape(ref).gsub('#', '%23'),
Addressable::URI.escape(file_path) Addressable::URI.escape(file_path)
].compact.join('/').squeeze('/').chomp('/') ].compact.join('/').squeeze('/').chomp('/')
......
...@@ -12,10 +12,12 @@ module Gitlab ...@@ -12,10 +12,12 @@ module Gitlab
def link_dependencies def link_dependencies
link_json('ImportPath') do |path| link_json('ImportPath') do |path|
case path case path
when %r{\A(?<repo>github\.com/#{REPO_REGEX})/(?<path>.+)\z}
"https://#{$~[:repo]}/tree/master/#{$~[:path]}"
when %r{\A(?<repo>gitlab\.com/#{NESTED_REPO_REGEX})\.git/(?<path>.+)\z}, when %r{\A(?<repo>gitlab\.com/#{NESTED_REPO_REGEX})\.git/(?<path>.+)\z},
%r{\A(?<repo>git(lab|hub)\.com/#{REPO_REGEX})/(?<path>.+)\z} %r{\A(?<repo>gitlab\.com/#{REPO_REGEX})/(?<path>.+)\z}
"https://#{$~[:repo]}/tree/master/#{$~[:path]}" "https://#{$~[:repo]}/-/tree/master/#{$~[:path]}"
when /\Agolang\.org/ when /\Agolang\.org/
"https://godoc.org/#{path}" "https://godoc.org/#{path}"
else else
......
...@@ -220,6 +220,8 @@ excluded_attributes: ...@@ -220,6 +220,8 @@ excluded_attributes:
- :encrypted_token - :encrypted_token
- :encrypted_token_iv - :encrypted_token_iv
- :enabled - :enabled
service_desk_setting:
- :outgoing_name
methods: methods:
notes: notes:
......
...@@ -60,7 +60,7 @@ module Gitlab ...@@ -60,7 +60,7 @@ module Gitlab
end end
meta_import_tag = tag :meta, name: 'go-import', content: "#{import_prefix} git #{repository_url}" meta_import_tag = tag :meta, name: 'go-import', content: "#{import_prefix} git #{repository_url}"
meta_source_tag = tag :meta, name: 'go-source', content: "#{import_prefix} #{project_url} #{project_url}/tree/#{branch}{/dir} #{project_url}/blob/#{branch}{/dir}/{file}#L{line}" meta_source_tag = tag :meta, name: 'go-source', content: "#{import_prefix} #{project_url} #{project_url}/-/tree/#{branch}{/dir} #{project_url}/-/blob/#{branch}{/dir}/{file}#L{line}"
head_tag = content_tag :head, meta_import_tag + meta_source_tag head_tag = content_tag :head, meta_import_tag + meta_source_tag
html_tag = content_tag :html, head_tag + body_tag html_tag = content_tag :html, head_tag + body_tag
[html_tag, 200] [html_tag, 200]
......
...@@ -5,6 +5,10 @@ module Quality ...@@ -5,6 +5,10 @@ module Quality
UnknownTestLevelError = Class.new(StandardError) UnknownTestLevelError = Class.new(StandardError)
TEST_LEVEL_FOLDERS = { TEST_LEVEL_FOLDERS = {
migration: %w[
migrations
lib/gitlab/background_migration
],
unit: %w[ unit: %w[
bin bin
config config
...@@ -19,7 +23,6 @@ module Quality ...@@ -19,7 +23,6 @@ module Quality
initializers initializers
javascripts javascripts
lib lib
migrations
models models
policies policies
presenters presenters
...@@ -36,10 +39,6 @@ module Quality ...@@ -36,10 +39,6 @@ module Quality
workers workers
elastic_integration elastic_integration
], ],
migration: %w[
migrations
lib/gitlab/background_migration
],
integration: %w[ integration: %w[
controllers controllers
mailers mailers
......
...@@ -4994,12 +4994,18 @@ msgstr "" ...@@ -4994,12 +4994,18 @@ msgstr ""
msgid "Could not create group" msgid "Could not create group"
msgstr "" msgstr ""
msgid "Could not create issue"
msgstr ""
msgid "Could not create project" msgid "Could not create project"
msgstr "" msgstr ""
msgid "Could not delete chat nickname %{chat_name}." msgid "Could not delete chat nickname %{chat_name}."
msgstr "" msgstr ""
msgid "Could not fetch projects"
msgstr ""
msgid "Could not remove the trigger." msgid "Could not remove the trigger."
msgstr "" msgstr ""
...@@ -5644,6 +5650,12 @@ msgstr "" ...@@ -5644,6 +5650,12 @@ msgstr ""
msgid "Delete list" msgid "Delete list"
msgstr "" msgstr ""
msgid "Delete snippet"
msgstr ""
msgid "Delete snippet?"
msgstr ""
msgid "Delete source branch" msgid "Delete source branch"
msgstr "" msgstr ""
...@@ -10001,6 +10013,9 @@ msgstr "" ...@@ -10001,6 +10013,9 @@ msgstr ""
msgid "IssuesAnalytics|Total:" msgid "IssuesAnalytics|Total:"
msgstr "" msgstr ""
msgid "Issue|Title"
msgstr ""
msgid "It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected." msgid "It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected."
msgstr "" msgstr ""
...@@ -10709,6 +10724,9 @@ msgstr "" ...@@ -10709,6 +10724,9 @@ msgstr ""
msgid "Loading issues" msgid "Loading issues"
msgstr "" msgstr ""
msgid "Loading snippet"
msgstr ""
msgid "Loading the GitLab IDE..." msgid "Loading the GitLab IDE..."
msgstr "" msgstr ""
...@@ -11724,6 +11742,9 @@ msgstr "" ...@@ -11724,6 +11742,9 @@ msgstr ""
msgid "New issue" msgid "New issue"
msgstr "" msgstr ""
msgid "New issue title"
msgstr ""
msgid "New label" msgid "New label"
msgstr "" msgstr ""
......
...@@ -44,7 +44,7 @@ gitlab: ...@@ -44,7 +44,7 @@ gitlab:
memory: 37.5M memory: 37.5M
maxReplicas: 3 maxReplicas: 3
hpa: hpa:
targetAverageValue: 130m targetAverageValue: 500m
deployment: deployment:
livenessProbe: livenessProbe:
timeoutSeconds: 5 timeoutSeconds: 5
...@@ -56,6 +56,8 @@ gitlab: ...@@ -56,6 +56,8 @@ gitlab:
limits: limits:
cpu: 975m cpu: 975m
memory: 1450M memory: 1450M
hpa:
targetAverageValue: 650m
task-runner: task-runner:
resources: resources:
requests: requests:
......
...@@ -72,7 +72,7 @@ describe MetricsDashboard do ...@@ -72,7 +72,7 @@ describe MetricsDashboard do
it 'includes project_blob_path only for project dashboards' do it 'includes project_blob_path only for project dashboards' do
expect(system_dashboard['project_blob_path']).to be_nil expect(system_dashboard['project_blob_path']).to be_nil
expect(project_dashboard['project_blob_path']).to eq("/#{project.namespace.path}/#{project.name}/blob/master/.gitlab/dashboards/test.yml") expect(project_dashboard['project_blob_path']).to eq("/#{project.namespace.path}/#{project.name}/-/blob/master/.gitlab/dashboards/test.yml")
end end
describe 'project permissions' do describe 'project permissions' do
......
...@@ -36,7 +36,7 @@ describe Projects::BlameController do ...@@ -36,7 +36,7 @@ describe Projects::BlameController do
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/master") .to redirect_to("/#{project.full_path}/-/tree/master")
end end
end end
......
...@@ -33,7 +33,7 @@ describe Projects::BlobController do ...@@ -33,7 +33,7 @@ describe Projects::BlobController do
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/master") .to redirect_to("/#{project.full_path}/-/tree/master")
end end
end end
...@@ -115,7 +115,7 @@ describe Projects::BlobController do ...@@ -115,7 +115,7 @@ describe Projects::BlobController do
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/markdown/doc") .to redirect_to("/#{project.full_path}/-/tree/markdown/doc")
end end
end end
end end
......
...@@ -37,7 +37,7 @@ describe Projects::BranchesController do ...@@ -37,7 +37,7 @@ describe Projects::BranchesController do
let(:ref) { "master" } let(:ref) { "master" }
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/merge_branch") .to redirect_to("/#{project.full_path}/-/tree/merge_branch")
end end
end end
...@@ -46,7 +46,7 @@ describe Projects::BranchesController do ...@@ -46,7 +46,7 @@ describe Projects::BranchesController do
let(:ref) { "master" } let(:ref) { "master" }
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/alert('merge');") .to redirect_to("/#{project.full_path}/-/tree/alert('merge');")
end end
end end
...@@ -88,7 +88,7 @@ describe Projects::BranchesController do ...@@ -88,7 +88,7 @@ describe Projects::BranchesController do
} }
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/1-feature-branch") .to redirect_to("/#{project.full_path}/-/tree/1-feature-branch")
end end
it 'posts a system note' do it 'posts a system note' do
......
...@@ -69,7 +69,7 @@ describe Projects::RawController do ...@@ -69,7 +69,7 @@ describe Projects::RawController do
env: :raw_blob_request_limit, env: :raw_blob_request_limit,
remote_ip: '0.0.0.0', remote_ip: '0.0.0.0',
request_method: 'GET', request_method: 'GET',
path: "/#{project.full_path}/raw/#{file_path}" path: "/#{project.full_path}/-/raw/#{file_path}"
} }
expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once
......
...@@ -149,6 +149,14 @@ describe Projects::Serverless::FunctionsController do ...@@ -149,6 +149,14 @@ describe Projects::Serverless::FunctionsController do
include_examples 'GET #show with valid data' include_examples 'GET #show with valid data'
end end
context 'on Knative 0.9.0' do
before do
prepare_knative_stubs(knative_09_service(knative_stub_options))
end
include_examples 'GET #show with valid data'
end
end end
end end
...@@ -210,6 +218,14 @@ describe Projects::Serverless::FunctionsController do ...@@ -210,6 +218,14 @@ describe Projects::Serverless::FunctionsController do
include_examples 'GET #index with data' include_examples 'GET #index with data'
end end
context 'on Knative 0.9.0' do
before do
prepare_knative_stubs(knative_09_service(knative_stub_options))
end
include_examples 'GET #index with data'
end
end end
def prepare_knative_stubs(knative_service) def prepare_knative_stubs(knative_service)
......
...@@ -45,7 +45,7 @@ describe Projects::TreeController do ...@@ -45,7 +45,7 @@ describe Projects::TreeController do
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/master") .to redirect_to("/#{project.full_path}/-/tree/master")
end end
end end
...@@ -60,7 +60,7 @@ describe Projects::TreeController do ...@@ -60,7 +60,7 @@ describe Projects::TreeController do
it 'redirects' do it 'redirects' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/empty-branch") .to redirect_to("/#{project.full_path}/-/tree/empty-branch")
end end
end end
...@@ -125,7 +125,7 @@ describe Projects::TreeController do ...@@ -125,7 +125,7 @@ describe Projects::TreeController do
let(:id) { 'master/README.md' } let(:id) { 'master/README.md' }
it 'redirects' do it 'redirects' do
redirect_url = "/#{project.full_path}/blob/master/README.md" redirect_url = "/#{project.full_path}/-/blob/master/README.md"
expect(subject) expect(subject)
.to redirect_to(redirect_url) .to redirect_to(redirect_url)
end end
...@@ -153,7 +153,7 @@ describe Projects::TreeController do ...@@ -153,7 +153,7 @@ describe Projects::TreeController do
it 'redirects to the new directory' do it 'redirects to the new directory' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/#{branch_name}/#{path}") .to redirect_to("/#{project.full_path}/-/tree/#{branch_name}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created.') expect(flash[:notice]).to eq('The directory has been successfully created.')
end end
end end
...@@ -164,7 +164,7 @@ describe Projects::TreeController do ...@@ -164,7 +164,7 @@ describe Projects::TreeController do
it 'does not allow overwriting of existing files' do it 'does not allow overwriting of existing files' do
expect(subject) expect(subject)
.to redirect_to("/#{project.full_path}/tree/master") .to redirect_to("/#{project.full_path}/-/tree/master")
expect(flash[:alert]).to eq('A file with this name already exists') expect(flash[:alert]).to eq('A file with this name already exists')
end end
end end
......
...@@ -943,7 +943,7 @@ describe ProjectsController do ...@@ -943,7 +943,7 @@ describe ProjectsController do
end end
it 'renders JSON body with image links expanded' do it 'renders JSON body with image links expanded' do
expanded_path = "/#{project_with_repo.full_path}/raw/master/files/images/logo-white.png" expanded_path = "/#{project_with_repo.full_path}/-/raw/master/files/images/logo-white.png"
post :preview_markdown, params: preview_markdown_params post :preview_markdown, params: preview_markdown_params
......
...@@ -6,6 +6,7 @@ describe 'Dashboard snippets' do ...@@ -6,6 +6,7 @@ describe 'Dashboard snippets' do
context 'when the project has snippets' do context 'when the project has snippets' do
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
before do before do
allow(Snippet).to receive(:default_per_page).and_return(1) allow(Snippet).to receive(:default_per_page).and_return(1)
sign_in(project.owner) sign_in(project.owner)
...@@ -13,10 +14,16 @@ describe 'Dashboard snippets' do ...@@ -13,10 +14,16 @@ describe 'Dashboard snippets' do
end end
it_behaves_like 'paginated snippets' it_behaves_like 'paginated snippets'
it 'shows new snippet button in header' do
parent_element = page.find('.page-title-controls')
expect(parent_element).to have_link('New snippet')
end
end end
context 'when there are no project snippets', :js do context 'when there are no project snippets', :js do
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
before do before do
sign_in(project.owner) sign_in(project.owner)
visit dashboard_snippets_path visit dashboard_snippets_path
...@@ -28,6 +35,11 @@ describe 'Dashboard snippets' do ...@@ -28,6 +35,11 @@ describe 'Dashboard snippets' do
expect(element).to have_content("Snippets are small pieces of code or notes that you want to keep.") expect(element).to have_content("Snippets are small pieces of code or notes that you want to keep.")
expect(element.find('.svg-content img')['src']).to have_content('illustrations/snippets_empty') expect(element.find('.svg-content img')['src']).to have_content('illustrations/snippets_empty')
end end
it 'shows new snippet button in main content area' do
parent_element = page.find('.row.empty-state')
expect(parent_element).to have_link('New snippet')
end
end end
context 'filtering by visibility' do context 'filtering by visibility' do
...@@ -76,4 +88,26 @@ describe 'Dashboard snippets' do ...@@ -76,4 +88,26 @@ describe 'Dashboard snippets' do
expect(page).to have_content(snippets[0].title) expect(page).to have_content(snippets[0].title)
end end
end end
context 'as an external user' do
let(:user) { create(:user, :external) }
before do
sign_in(user)
visit dashboard_snippets_path
end
context 'without snippets' do
it 'hides new snippet button' do
expect(page).not_to have_link('New snippet')
end
end
context 'with snippets' do
let!(:snippets) { create(:personal_snippet, author: user) }
it 'hides new snippet button' do
expect(page).not_to have_link('New snippet')
end
end
end
end end
...@@ -8,6 +8,7 @@ describe 'Thread Comments Snippet', :js do ...@@ -8,6 +8,7 @@ describe 'Thread Comments Snippet', :js do
let(:snippet) { create(:project_snippet, :private, project: project, author: user) } let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
......
...@@ -70,6 +70,7 @@ describe 'issue move to another project' do ...@@ -70,6 +70,7 @@ describe 'issue move to another project' do
context 'user does not have permission to move the issue to a project', :js do context 'user does not have permission to move the issue to a project', :js do
let!(:private_project) { create(:project, :private) } let!(:private_project) { create(:project, :private) }
let(:another_project) { create(:project) } let(:another_project) { create(:project) }
before do before do
another_project.add_guest(user) another_project.add_guest(user)
end end
......
...@@ -6,6 +6,7 @@ describe "User views issues" do ...@@ -6,6 +6,7 @@ describe "User views issues" do
let!(:closed_issue) { create(:closed_issue, project: project) } let!(:closed_issue) { create(:closed_issue, project: project) }
let!(:open_issue1) { create(:issue, project: project) } let!(:open_issue1) { create(:issue, project: project) }
let!(:open_issue2) { create(:issue, project: project) } let!(:open_issue2) { create(:issue, project: project) }
set(:user) { create(:user) } set(:user) { create(:user) }
shared_examples "opens issue from list" do shared_examples "opens issue from list" do
......
...@@ -9,6 +9,7 @@ describe 'User sorts merge requests' do ...@@ -9,6 +9,7 @@ describe 'User sorts merge requests' do
let!(:merge_request2) do let!(:merge_request2) do
create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test') create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
end end
set(:user) { create(:user) } set(:user) { create(:user) }
set(:group) { create(:group) } set(:group) { create(:group) }
set(:group_member) { create(:group_member, :maintainer, user: user, group: group) } set(:group_member) { create(:group_member, :maintainer, user: user, group: group) }
......
...@@ -113,6 +113,7 @@ describe 'User views open merge requests' do ...@@ -113,6 +113,7 @@ describe 'User views open merge requests' do
context 'when project is internal' do context 'when project is internal' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
set(:project) { create(:project, :internal, :repository) } set(:project) { create(:project, :internal, :repository) }
context 'when signed in' do context 'when signed in' do
......
...@@ -33,6 +33,7 @@ describe 'OAuth Login', :js, :allow_forgery_protection do ...@@ -33,6 +33,7 @@ describe 'OAuth Login', :js, :allow_forgery_protection do
let(:remember_me) { false } let(:remember_me) { false }
let(:user) { create(:omniauth_user, extern_uid: uid, provider: provider.to_s) } let(:user) { create(:omniauth_user, extern_uid: uid, provider: provider.to_s) }
let(:two_factor_user) { create(:omniauth_user, :two_factor, extern_uid: uid, provider: provider.to_s) } let(:two_factor_user) { create(:omniauth_user, :two_factor, extern_uid: uid, provider: provider.to_s) }
provider == :salesforce ? let(:additional_info) { { extra: { email_verified: true } } } : let(:additional_info) { {} } provider == :salesforce ? let(:additional_info) { { extra: { email_verified: true } } } : let(:additional_info) { {} }
before do before do
......
...@@ -34,6 +34,7 @@ describe 'Member autocomplete', :js do ...@@ -34,6 +34,7 @@ describe 'Member autocomplete', :js do
context 'adding a new note on a Issue' do context 'adding a new note on a Issue' do
let(:noteable) { create(:issue, author: author, project: project) } let(:noteable) { create(:issue, author: author, project: project) }
before do before do
visit project_issue_path(project, noteable) visit project_issue_path(project, noteable)
end end
...@@ -47,6 +48,7 @@ describe 'Member autocomplete', :js do ...@@ -47,6 +48,7 @@ describe 'Member autocomplete', :js do
create(:merge_request, source_project: project, create(:merge_request, source_project: project,
target_project: project, author: author) target_project: project, author: author)
end end
before do before do
visit project_merge_request_path(project, noteable) visit project_merge_request_path(project, noteable)
end end
......
...@@ -194,6 +194,7 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do ...@@ -194,6 +194,7 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
context 'when third party offers are disabled' do context 'when third party offers are disabled' do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
before do before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin) sign_in(admin)
......
...@@ -193,6 +193,7 @@ describe 'Projects > Files > User edits files', :js do ...@@ -193,6 +193,7 @@ describe 'Projects > Files > User edits files', :js do
context 'when the user already had a fork of the project', :js do context 'when the user already had a fork of the project', :js do
let!(:forked_project) { fork_project(project2, user, namespace: user.namespace, repository: true) } let!(:forked_project) { fork_project(project2, user, namespace: user.namespace, repository: true) }
before do before do
visit(project2_tree_path_root_ref) visit(project2_tree_path_root_ref)
wait_for_requests wait_for_requests
......
...@@ -18,6 +18,7 @@ describe 'Projects > Snippets > Create Snippet', :js do ...@@ -18,6 +18,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
context 'when a user is authenticated' do context 'when a user is authenticated' do
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
...@@ -76,6 +77,10 @@ describe 'Projects > Snippets > Create Snippet', :js do ...@@ -76,6 +77,10 @@ describe 'Projects > Snippets > Create Snippet', :js do
end end
context 'when a user is not authenticated' do context 'when a user is not authenticated' do
before do
stub_feature_flags(snippets_vue: false)
end
it 'shows a public snippet on the index page but not the New snippet button' do it 'shows a public snippet on the index page but not the New snippet button' do
snippet = create(:project_snippet, :public, project: project) snippet = create(:project_snippet, :public, project: project)
......
...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > Project snippet', :js do ...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > Project snippet', :js do
let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) } let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) }
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
end end
......
...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > User comments on a snippet', :js do ...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > User comments on a snippet', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
......
...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > User deletes a snippet' do ...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > User deletes a snippet' do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
......
...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > User updates a snippet' do ...@@ -8,6 +8,7 @@ describe 'Projects > Snippets > User updates a snippet' do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
......
...@@ -7,6 +7,7 @@ describe 'Reportable note on snippets', :js do ...@@ -7,6 +7,7 @@ describe 'Reportable note on snippets', :js do
let(:project) { create(:project) } let(:project) { create(:project) }
before do before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
end end
......
...@@ -53,6 +53,7 @@ describe 'Internal Group access' do ...@@ -53,6 +53,7 @@ describe 'Internal Group access' do
describe 'GET /groups/:path/merge_requests' do describe 'GET /groups/:path/merge_requests' do
let(:project) { create(:project, :internal, :repository, group: group) } let(:project) { create(:project, :internal, :repository, group: group) }
subject { merge_requests_group_path(group) } subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -53,6 +53,7 @@ describe 'Private Group access' do ...@@ -53,6 +53,7 @@ describe 'Private Group access' do
describe 'GET /groups/:path/merge_requests' do describe 'GET /groups/:path/merge_requests' do
let(:project) { create(:project, :private, :repository, group: group) } let(:project) { create(:project, :private, :repository, group: group) }
subject { merge_requests_group_path(group) } subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -53,6 +53,7 @@ describe 'Public Group access' do ...@@ -53,6 +53,7 @@ describe 'Public Group access' do
describe 'GET /groups/:path/merge_requests' do describe 'GET /groups/:path/merge_requests' do
let(:project) { create(:project, :public, :repository, group: group) } let(:project) { create(:project, :public, :repository, group: group) }
subject { merge_requests_group_path(group) } subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -129,6 +129,7 @@ describe "Internal Project Access" do ...@@ -129,6 +129,7 @@ describe "Internal Project Access" do
describe "GET /:project_path/blob" do describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { project_blob_path(project, File.join(commit.id, '.gitignore')) } subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -186,6 +187,7 @@ describe "Internal Project Access" do ...@@ -186,6 +187,7 @@ describe "Internal Project Access" do
describe "GET /:project_path/issues/:id/edit" do describe "GET /:project_path/issues/:id/edit" do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
subject { edit_project_issue_path(project, issue) } subject { edit_project_issue_path(project, issue) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -327,6 +329,7 @@ describe "Internal Project Access" do ...@@ -327,6 +329,7 @@ describe "Internal Project Access" do
describe "GET /:project_path/pipelines/:id" do describe "GET /:project_path/pipelines/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
subject { project_pipeline_path(project, pipeline) } subject { project_pipeline_path(project, pipeline) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -379,6 +382,7 @@ describe "Internal Project Access" do ...@@ -379,6 +382,7 @@ describe "Internal Project Access" do
describe "GET /:project_path/builds/:id" do describe "GET /:project_path/builds/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
subject { project_job_path(project, build.id) } subject { project_job_path(project, build.id) }
context "when allowed for public and internal" do context "when allowed for public and internal" do
...@@ -417,6 +421,7 @@ describe "Internal Project Access" do ...@@ -417,6 +421,7 @@ describe "Internal Project Access" do
describe 'GET /:project_path/builds/:id/trace' do describe 'GET /:project_path/builds/:id/trace' do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
subject { trace_project_job_path(project, build.id) } subject { trace_project_job_path(project, build.id) }
context 'when allowed for public and internal' do context 'when allowed for public and internal' do
...@@ -482,6 +487,7 @@ describe "Internal Project Access" do ...@@ -482,6 +487,7 @@ describe "Internal Project Access" do
describe "GET /:project_path/-/environments/:id" do describe "GET /:project_path/-/environments/:id" do
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
subject { project_environment_path(project, environment) } subject { project_environment_path(project, environment) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -497,6 +503,7 @@ describe "Internal Project Access" do ...@@ -497,6 +503,7 @@ describe "Internal Project Access" do
describe "GET /:project_path/-/environments/:id/deployments" do describe "GET /:project_path/-/environments/:id/deployments" do
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
subject { project_environment_deployments_path(project, environment) } subject { project_environment_deployments_path(project, environment) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -129,6 +129,7 @@ describe "Private Project Access" do ...@@ -129,6 +129,7 @@ describe "Private Project Access" do
describe "GET /:project_path/blob" do describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { project_blob_path(project, File.join(commit.id, '.gitignore')) } subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -186,6 +187,7 @@ describe "Private Project Access" do ...@@ -186,6 +187,7 @@ describe "Private Project Access" do
describe "GET /:project_path/issues/:id/edit" do describe "GET /:project_path/issues/:id/edit" do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
subject { edit_project_issue_path(project, issue) } subject { edit_project_issue_path(project, issue) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -311,6 +313,7 @@ describe "Private Project Access" do ...@@ -311,6 +313,7 @@ describe "Private Project Access" do
describe "GET /:project_path/pipelines/:id" do describe "GET /:project_path/pipelines/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
subject { project_pipeline_path(project, pipeline) } subject { project_pipeline_path(project, pipeline) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -365,6 +368,7 @@ describe "Private Project Access" do ...@@ -365,6 +368,7 @@ describe "Private Project Access" do
describe "GET /:project_path/builds/:id" do describe "GET /:project_path/builds/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
subject { project_job_path(project, build.id) } subject { project_job_path(project, build.id) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -398,6 +402,7 @@ describe "Private Project Access" do ...@@ -398,6 +402,7 @@ describe "Private Project Access" do
describe 'GET /:project_path/builds/:id/trace' do describe 'GET /:project_path/builds/:id/trace' do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
subject { trace_project_job_path(project, build.id) } subject { trace_project_job_path(project, build.id) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -443,6 +448,7 @@ describe "Private Project Access" do ...@@ -443,6 +448,7 @@ describe "Private Project Access" do
describe "GET /:project_path/-/environments/:id" do describe "GET /:project_path/-/environments/:id" do
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
subject { project_environment_path(project, environment) } subject { project_environment_path(project, environment) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -458,6 +464,7 @@ describe "Private Project Access" do ...@@ -458,6 +464,7 @@ describe "Private Project Access" do
describe "GET /:project_path/-/environments/:id/deployments" do describe "GET /:project_path/-/environments/:id/deployments" do
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
subject { project_environment_deployments_path(project, environment) } subject { project_environment_deployments_path(project, environment) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -143,6 +143,7 @@ describe "Public Project Access" do ...@@ -143,6 +143,7 @@ describe "Public Project Access" do
describe "GET /:project_path/pipelines/:id" do describe "GET /:project_path/pipelines/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
subject { project_pipeline_path(project, pipeline) } subject { project_pipeline_path(project, pipeline) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -195,6 +196,7 @@ describe "Public Project Access" do ...@@ -195,6 +196,7 @@ describe "Public Project Access" do
describe "GET /:project_path/builds/:id" do describe "GET /:project_path/builds/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
subject { project_job_path(project, build.id) } subject { project_job_path(project, build.id) }
context "when allowed for public" do context "when allowed for public" do
...@@ -233,6 +235,7 @@ describe "Public Project Access" do ...@@ -233,6 +235,7 @@ describe "Public Project Access" do
describe 'GET /:project_path/builds/:id/trace' do describe 'GET /:project_path/builds/:id/trace' do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
subject { trace_project_job_path(project, build.id) } subject { trace_project_job_path(project, build.id) }
context 'when allowed for public' do context 'when allowed for public' do
...@@ -298,6 +301,7 @@ describe "Public Project Access" do ...@@ -298,6 +301,7 @@ describe "Public Project Access" do
describe "GET /:project_path/-/environments/:id" do describe "GET /:project_path/-/environments/:id" do
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
subject { project_environment_path(project, environment) } subject { project_environment_path(project, environment) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -313,6 +317,7 @@ describe "Public Project Access" do ...@@ -313,6 +317,7 @@ describe "Public Project Access" do
describe "GET /:project_path/-/environments/:id/deployments" do describe "GET /:project_path/-/environments/:id/deployments" do
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
subject { project_environment_deployments_path(project, environment) } subject { project_environment_deployments_path(project, environment) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -399,6 +404,7 @@ describe "Public Project Access" do ...@@ -399,6 +404,7 @@ describe "Public Project Access" do
describe "GET /:project_path/issues/:id/edit" do describe "GET /:project_path/issues/:id/edit" do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
subject { edit_project_issue_path(project, issue) } subject { edit_project_issue_path(project, issue) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -6,30 +6,59 @@ describe 'Explore Snippets' do ...@@ -6,30 +6,59 @@ describe 'Explore Snippets' do
let!(:public_snippet) { create(:personal_snippet, :public) } let!(:public_snippet) { create(:personal_snippet, :public) }
let!(:internal_snippet) { create(:personal_snippet, :internal) } let!(:internal_snippet) { create(:personal_snippet, :internal) }
let!(:private_snippet) { create(:personal_snippet, :private) } let!(:private_snippet) { create(:personal_snippet, :private) }
let(:user) { nil }
it 'User should see snippets that are not private' do before do
sign_in create(:user) sign_in(user) if user
visit explore_snippets_path visit explore_snippets_path
expect(page).to have_content(public_snippet.title)
expect(page).to have_content(internal_snippet.title)
expect(page).not_to have_content(private_snippet.title)
end end
it 'External user should see only public snippets' do context 'User' do
sign_in create(:user, :external) let(:user) { create(:user) }
visit explore_snippets_path
it 'see snippets that are not private' do
expect(page).to have_content(public_snippet.title)
expect(page).to have_content(internal_snippet.title)
expect(page).not_to have_content(private_snippet.title)
end
expect(page).to have_content(public_snippet.title) it 'shows new snippet button in header' do
expect(page).not_to have_content(internal_snippet.title) parent_element = page.find('.page-title-controls')
expect(page).not_to have_content(private_snippet.title) expect(parent_element).to have_link('New snippet')
end
end end
it 'Not authenticated user should see only public snippets' do context 'External user' do
visit explore_snippets_path let(:user) { create(:user, :external) }
it 'see only public snippets' do
expect(page).to have_content(public_snippet.title)
expect(page).not_to have_content(internal_snippet.title)
expect(page).not_to have_content(private_snippet.title)
end
context 'without snippets' do
before do
Snippet.delete_all
end
it 'hides new snippet button' do
expect(page).not_to have_link('New snippet')
end
end
context 'with snippets' do
it 'hides new snippet button' do
expect(page).not_to have_link('New snippet')
end
end
end
expect(page).to have_content(public_snippet.title) context 'Not authenticated user' do
expect(page).not_to have_content(internal_snippet.title) it 'see only public snippets' do
expect(page).not_to have_content(private_snippet.title) expect(page).to have_content(public_snippet.title)
expect(page).not_to have_content(internal_snippet.title)
expect(page).not_to have_content(private_snippet.title)
end
end end
end end
...@@ -146,6 +146,7 @@ describe 'Task Lists' do ...@@ -146,6 +146,7 @@ describe 'Task Lists' do
describe 'for Notes' do describe 'for Notes' do
let!(:issue) { create(:issue, author: user, project: project) } let!(:issue) { create(:issue, author: user, project: project) }
describe 'multiple tasks' do describe 'multiple tasks' do
let!(:note) do let!(:note) do
create(:note, note: markdown, noteable: issue, create(:note, note: markdown, noteable: issue,
......
...@@ -406,6 +406,7 @@ describe 'Login' do ...@@ -406,6 +406,7 @@ describe 'Login' do
describe 'with required two-factor authentication enabled' do describe 'with required two-factor authentication enabled' do
let(:user) { create(:user) } let(:user) { create(:user) }
# TODO: otp_grace_period_started_at # TODO: otp_grace_period_started_at
context 'global setting' do context 'global setting' do
......
...@@ -4,6 +4,7 @@ require 'spec_helper' ...@@ -4,6 +4,7 @@ require 'spec_helper'
describe ClustersFinder do describe ClustersFinder do
let(:project) { create(:project) } let(:project) { create(:project) }
set(:user) { create(:user) } set(:user) { create(:user) }
describe '#execute' do describe '#execute' do
......
...@@ -6,6 +6,7 @@ describe GroupDescendantsFinder do ...@@ -6,6 +6,7 @@ describe GroupDescendantsFinder do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:params) { {} } let(:params) { {} }
subject(:finder) do subject(:finder) do
described_class.new(current_user: user, parent_group: group, params: params) described_class.new(current_user: user, parent_group: group, params: params)
end end
......
...@@ -132,11 +132,13 @@ describe GroupProjectsFinder do ...@@ -132,11 +132,13 @@ describe GroupProjectsFinder do
context "only shared" do context "only shared" do
let(:options) { { only_shared: true } } let(:options) { { only_shared: true } }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) } it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
end end
context "only owned" do context "only owned" do
let(:options) { { only_owned: true } } let(:options) { { only_owned: true } }
it { is_expected.to eq([private_project, public_project]) } it { is_expected.to eq([private_project, public_project]) }
end end
......
...@@ -111,6 +111,7 @@ describe GroupsFinder do ...@@ -111,6 +111,7 @@ describe GroupsFinder do
context 'authorized to private project' do context 'authorized to private project' do
context 'project one level deep' do context 'project one level deep' do
let!(:subproject) { create(:project, :private, namespace: private_subgroup) } let!(:subproject) { create(:project, :private, namespace: private_subgroup) }
before do before do
subproject.add_guest(user) subproject.add_guest(user)
end end
...@@ -129,6 +130,7 @@ describe GroupsFinder do ...@@ -129,6 +130,7 @@ describe GroupsFinder do
context 'project two levels deep' do context 'project two levels deep' do
let!(:private_subsubgroup) { create(:group, :private, parent: private_subgroup) } let!(:private_subsubgroup) { create(:group, :private, parent: private_subgroup) }
let!(:subsubproject) { create(:project, :private, namespace: private_subsubgroup) } let!(:subsubproject) { create(:project, :private, namespace: private_subsubgroup) }
before do before do
subsubproject.add_guest(user) subsubproject.add_guest(user)
end end
......
...@@ -786,6 +786,7 @@ describe IssuesFinder do ...@@ -786,6 +786,7 @@ describe IssuesFinder do
describe '#with_confidentiality_access_check' do describe '#with_confidentiality_access_check' do
let(:guest) { create(:user) } let(:guest) { create(:user) }
set(:authorized_user) { create(:user) } set(:authorized_user) { create(:user) }
set(:project) { create(:project, namespace: authorized_user.namespace) } set(:project) { create(:project, namespace: authorized_user.namespace) }
set(:public_issue) { create(:issue, project: project) } set(:public_issue) { create(:issue, project: project) }
......
...@@ -6,6 +6,7 @@ describe MergeRequestTargetProjectFinder do ...@@ -6,6 +6,7 @@ describe MergeRequestTargetProjectFinder do
include ProjectForksHelper include ProjectForksHelper
let(:user) { create(:user) } let(:user) { create(:user) }
subject(:finder) { described_class.new(current_user: user, source_project: forked_project) } subject(:finder) { described_class.new(current_user: user, source_project: forked_project) }
shared_examples 'finding related projects' do shared_examples 'finding related projects' do
......
...@@ -6,6 +6,7 @@ describe PipelinesFinder do ...@@ -6,6 +6,7 @@ describe PipelinesFinder do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil } let(:current_user) { nil }
let(:params) { {} } let(:params) { {} }
subject { described_class.new(project, current_user, params).execute } subject { described_class.new(project, current_user, params).execute }
describe "#execute" do describe "#execute" do
......
...@@ -181,6 +181,7 @@ describe ProjectsFinder, :do_not_mock_admin_mode do ...@@ -181,6 +181,7 @@ describe ProjectsFinder, :do_not_mock_admin_mode do
describe 'filter by non_public' do describe 'filter by non_public' do
let(:params) { { non_public: true } } let(:params) { { non_public: true } }
before do before do
private_project.add_developer(current_user) private_project.add_developer(current_user)
end end
...@@ -190,6 +191,7 @@ describe ProjectsFinder, :do_not_mock_admin_mode do ...@@ -190,6 +191,7 @@ describe ProjectsFinder, :do_not_mock_admin_mode do
describe 'filter by starred' do describe 'filter by starred' do
let(:params) { { starred: true } } let(:params) { { starred: true } }
before do before do
current_user.toggle_star(public_project) current_user.toggle_star(public_project)
end end
......
...@@ -96,6 +96,7 @@ describe TagsFinder do ...@@ -96,6 +96,7 @@ describe TagsFinder do
context 'filter and sort' do context 'filter and sort' do
let(:tags_to_compare) { %w[v1.0.0 v1.1.0] } let(:tags_to_compare) { %w[v1.0.0 v1.1.0] }
subject { described_class.new(repository, params).execute.select { |tag| tags_to_compare.include?(tag.name) } } subject { described_class.new(repository, params).execute.select { |tag| tags_to_compare.include?(tag.name) } }
context 'when sort by updated_desc' do context 'when sort by updated_desc' do
......
...@@ -99,6 +99,15 @@ ...@@ -99,6 +99,15 @@
"access_level": 50, "access_level": 50,
"notification_level": 3 "notification_level": 3
} }
},
"_links": {
"self": "https://gitlab.com/api/v4/projects/278964",
"issues": "https://gitlab.com/api/v4/projects/278964/issues",
"merge_requests": "https://gitlab.com/api/v4/projects/278964/merge_requests",
"repo_branches": "https://gitlab.com/api/v4/projects/278964/repository/branches",
"labels": "https://gitlab.com/api/v4/projects/278964/labels",
"events": "https://gitlab.com/api/v4/projects/278964/events",
"members": "https://gitlab.com/api/v4/projects/278964/members"
} }
}, { }, {
"id": 7, "id": 7,
......
import './get_client_rects'; import './get_client_rects';
import './inner_text';
// workaround for JSDOM not supporting innerText
// see https://github.com/jsdom/jsdom/issues/1245
Object.defineProperty(global.Element.prototype, 'innerText', {
get() {
return this.textContent;
},
set(value) {
this.textContext = value;
},
configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment