Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7e20809103
commit
8ac91ecfd1
13 changed files with 176 additions and 108 deletions
|
@ -1,5 +1,7 @@
|
|||
import sqljs from 'sql.js';
|
||||
import { template as _template } from 'underscore';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { successCodes } from '~/lib/utils/http_status';
|
||||
|
||||
const PREVIEW_TEMPLATE = _template(`
|
||||
<div class="card">
|
||||
|
@ -16,30 +18,25 @@ class BalsamiqViewer {
|
|||
}
|
||||
|
||||
loadFile(endpoint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', endpoint, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onload = loadEvent => this.fileLoaded(loadEvent, resolve, reject);
|
||||
xhr.onerror = reject;
|
||||
|
||||
xhr.send();
|
||||
});
|
||||
return axios
|
||||
.get(endpoint, {
|
||||
responseType: 'arraybuffer',
|
||||
validateStatus(status) {
|
||||
return status !== successCodes.OK;
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
this.renderFile(data);
|
||||
})
|
||||
.catch(e => {
|
||||
throw new Error(e);
|
||||
});
|
||||
}
|
||||
|
||||
fileLoaded(loadEvent, resolve, reject) {
|
||||
if (loadEvent.target.status !== 200) return reject();
|
||||
|
||||
this.renderFile(loadEvent);
|
||||
|
||||
return resolve();
|
||||
}
|
||||
|
||||
renderFile(loadEvent) {
|
||||
renderFile(fileBuffer) {
|
||||
const container = document.createElement('ul');
|
||||
|
||||
this.initDatabase(loadEvent.target.response);
|
||||
this.initDatabase(fileBuffer);
|
||||
|
||||
const previews = this.getPreviews();
|
||||
previews.forEach(preview => {
|
||||
|
|
|
@ -877,11 +877,16 @@ pre.light-well {
|
|||
flex-direction: column;
|
||||
|
||||
// Disable Flexbox for admin page
|
||||
&.admin-projects {
|
||||
&.admin-projects,
|
||||
&.group-settings-projects {
|
||||
display: block;
|
||||
|
||||
.project-row {
|
||||
display: block;
|
||||
|
||||
.description > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
new_file_anchor_data,
|
||||
readme_anchor_data,
|
||||
changelog_anchor_data,
|
||||
contribution_guide_anchor_data
|
||||
contribution_guide_anchor_data,
|
||||
gitlab_ci_anchor_data
|
||||
].compact.reject { |item| item.is_link }
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
- page_title "Projects"
|
||||
- page_title _('Projects')
|
||||
- params[:visibility_level] ||= []
|
||||
|
||||
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
|
||||
.prepend-top-default
|
||||
%ul.nav-links.nav.nav-tabs
|
||||
- opts = params[:visibility_level].present? ? {} : { page: admin_projects_path }
|
||||
= nav_link(opts) do
|
||||
= link_to _('All'), admin_projects_path
|
||||
|
||||
= nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s) }) do
|
||||
= link_to _('Private'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
= nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s) }) do
|
||||
= link_to _('Internal'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
= nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s) }) do
|
||||
= link_to _('Public'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
|
||||
.nav-controls
|
||||
.search-holder
|
||||
= render 'shared/projects/search_form', autofocus: true, admin_view: true
|
||||
.dropdown
|
||||
|
@ -22,20 +34,4 @@
|
|||
New Project
|
||||
= button_tag "Search", class: "btn btn-primary btn-search hide"
|
||||
|
||||
%ul.nav-links.nav.nav-tabs
|
||||
- opts = params[:visibility_level].present? ? {} : { page: admin_projects_path }
|
||||
= nav_link(opts) do
|
||||
= link_to admin_projects_path do
|
||||
All
|
||||
|
||||
= nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s) }) do
|
||||
= link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do
|
||||
Private
|
||||
= nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s) }) do
|
||||
= link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do
|
||||
Internal
|
||||
= nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s) }) do
|
||||
= link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do
|
||||
Public
|
||||
|
||||
= render 'projects'
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
- breadcrumb_title @cluster.name
|
||||
- page_title _('Kubernetes Cluster')
|
||||
- manage_prometheus_path = edit_project_service_path(@cluster.project, 'prometheus') if @project
|
||||
- cluster_environments_path = clusterable.environments_cluster_path(@cluster)
|
||||
|
||||
- expanded = expanded_by_default?
|
||||
|
||||
|
@ -16,7 +17,7 @@
|
|||
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
|
||||
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
|
||||
update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
|
||||
cluster_environments_path: clusterable.environments_cluster_path(@cluster),
|
||||
cluster_environments_path: cluster_environments_path,
|
||||
toggle_status: @cluster.enabled? ? 'true': 'false',
|
||||
has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
|
||||
cluster_type: @cluster.cluster_type,
|
||||
|
@ -37,7 +38,7 @@
|
|||
%h4= @cluster.name
|
||||
= render 'banner'
|
||||
|
||||
= render_if_exists 'clusters/clusters/group_cluster_environments', expanded: expanded
|
||||
|
||||
- unless Gitlab.ee?
|
||||
- if cluster_environments_path.present?
|
||||
= render_if_exists 'clusters/clusters/group_cluster_environments', expanded: expanded
|
||||
- else
|
||||
= render 'configure', expanded: expanded
|
||||
|
|
|
@ -8,21 +8,38 @@
|
|||
.controls
|
||||
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
|
||||
New project
|
||||
%ul.content-list
|
||||
%ul.projects-list.content-list.group-settings-projects
|
||||
- @projects.each do |project|
|
||||
%li
|
||||
.list-item-name
|
||||
%span{ class: visibility_level_color(project.visibility_level) }
|
||||
= visibility_level_icon(project.visibility_level)
|
||||
%strong= link_to project.full_name, project
|
||||
.float-right
|
||||
%li.project-row{ class: ('no-description' if project.description.blank?) }
|
||||
.controls
|
||||
= link_to _('Members'), project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn"
|
||||
= link_to _('Edit'), edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn"
|
||||
= link_to _('Remove'), project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-remove"
|
||||
|
||||
.stats
|
||||
%span.badge.badge-pill
|
||||
= storage_counter(project.statistics&.storage_size)
|
||||
- if project.archived
|
||||
%span.badge.badge-warning archived
|
||||
%span.badge.badge-pill
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
= link_to 'Members', project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
|
||||
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
|
||||
= link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove"
|
||||
|
||||
.title
|
||||
= link_to(project_path(project)) do
|
||||
.dash-project-avatar
|
||||
.avatar-container.rect-avatar.s40
|
||||
= project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
|
||||
%span.project-full-name
|
||||
%span.namespace-name
|
||||
- if project.namespace
|
||||
= project.namespace.human_name
|
||||
\/
|
||||
%span.project-name
|
||||
= project.name
|
||||
%span{ class: visibility_level_color(project.visibility_level) }
|
||||
= visibility_level_icon(project.visibility_level)
|
||||
|
||||
- if project.description.present?
|
||||
.description
|
||||
= markdown_field(project, :description)
|
||||
- if @projects.blank?
|
||||
.nothing-here-block This group has no projects yet
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve UI for admin/projects and group/settings/projects pages
|
||||
merge_request: 17247
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show the "Set up CI/CD" prompt in empty repositories when applicable.
|
||||
merge_request: 17274
|
||||
author: Ben McCormick
|
||||
type: changed
|
|
@ -110,7 +110,7 @@ The following table lists available parameters for jobs:
|
|||
| [`dependencies`](#dependencies) | Other jobs that a job depends on so that you can pass artifacts between them. |
|
||||
| [`coverage`](#coverage) | Code coverage settings for a given job. |
|
||||
| [`retry`](#retry) | When and how many times a job can be auto-retried in case of a failure. |
|
||||
| [`timeout`](#timeout) | Define a custom timeout that would take precedence over the project-wide one. |
|
||||
| [`timeout`](#timeout) | Define a custom job-level timeout that takes precedence over the project-wide setting. |
|
||||
| [`parallel`](#parallel) | How many instances of a job should be run in parallel. |
|
||||
| [`trigger`](#trigger-premium) | Defines a downstream pipeline trigger. |
|
||||
| [`include`](#include) | Allows this job to include external YAML files. Also available: `include:local`, `include:file`, `include:template`, and `include:remote`. |
|
||||
|
@ -1996,9 +1996,11 @@ Possible values for `when` are:
|
|||
- `missing_dependency_failure`: Retry if a dependency was missing.
|
||||
- `runner_unsupported`: Retry if the runner was unsupported.
|
||||
|
||||
### timeout
|
||||
### `timeout`
|
||||
|
||||
`timeout` allows you to configure a timeout for a specific job:
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/14887) in GitLab 12.3.
|
||||
|
||||
`timeout` allows you to configure a timeout for a specific job. For example:
|
||||
|
||||
```yaml
|
||||
build:
|
||||
|
@ -2129,7 +2131,7 @@ step-1:
|
|||
stage: stage1
|
||||
script:
|
||||
- echo "Can be canceled"
|
||||
|
||||
|
||||
step-2:
|
||||
stage: stage2
|
||||
script:
|
||||
|
|
|
@ -19,7 +19,7 @@ CE specs should remain untouched as much as possible and extra specs
|
|||
should be added for EE. Licensed features can be stubbed using the
|
||||
spec helper `stub_licensed_features` in `EE::LicenseHelpers`.
|
||||
|
||||
You can force Webpack to act as CE by either deleting the `ee/` directory or by
|
||||
You can force GitLab to act as CE by either deleting the `ee/` directory or by
|
||||
setting the [`IS_GITLAB_EE` environment variable](https://gitlab.com/gitlab-org/gitlab/blob/master/config/helpers/is_ee_env.js)
|
||||
to something that evaluates as `false`. The same works for running tests
|
||||
(for example `IS_GITLAB_EE=0 yarn jest`).
|
||||
|
|
|
@ -13,7 +13,7 @@ describe 'Clusterable > Show page' do
|
|||
sign_in(current_user)
|
||||
end
|
||||
|
||||
shared_examples 'editing domain' do
|
||||
shared_examples 'show page' do
|
||||
before do
|
||||
clusterable.add_maintainer(current_user)
|
||||
end
|
||||
|
@ -53,6 +53,12 @@ describe 'Clusterable > Show page' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not show the environments tab' do
|
||||
visit cluster_path
|
||||
|
||||
expect(page).not_to have_selector('.js-cluster-nav-environments', text: 'Environments')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'editing a GCP cluster' do
|
||||
|
@ -113,42 +119,30 @@ describe 'Clusterable > Show page' do
|
|||
end
|
||||
|
||||
context 'when clusterable is a project' do
|
||||
it_behaves_like 'editing domain' do
|
||||
let(:clusterable) { create(:project) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) }
|
||||
let(:cluster_path) { project_cluster_path(clusterable, cluster) }
|
||||
end
|
||||
let(:clusterable) { create(:project) }
|
||||
let(:cluster_path) { project_cluster_path(clusterable, cluster) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) }
|
||||
|
||||
it_behaves_like 'editing a GCP cluster' do
|
||||
let(:clusterable) { create(:project) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) }
|
||||
let(:cluster_path) { project_cluster_path(clusterable, cluster) }
|
||||
end
|
||||
it_behaves_like 'show page'
|
||||
|
||||
it_behaves_like 'editing a GCP cluster'
|
||||
|
||||
it_behaves_like 'editing a user-provided cluster' do
|
||||
let(:clusterable) { create(:project) }
|
||||
let(:cluster) { create(:cluster, :provided_by_user, :project, projects: [clusterable]) }
|
||||
let(:cluster_path) { project_cluster_path(clusterable, cluster) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when clusterable is a group' do
|
||||
it_behaves_like 'editing domain' do
|
||||
let(:clusterable) { create(:group) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) }
|
||||
let(:cluster_path) { group_cluster_path(clusterable, cluster) }
|
||||
end
|
||||
let(:clusterable) { create(:group) }
|
||||
let(:cluster_path) { group_cluster_path(clusterable, cluster) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) }
|
||||
|
||||
it_behaves_like 'editing a GCP cluster' do
|
||||
let(:clusterable) { create(:group) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) }
|
||||
let(:cluster_path) { group_cluster_path(clusterable, cluster) }
|
||||
end
|
||||
it_behaves_like 'show page'
|
||||
|
||||
it_behaves_like 'editing a GCP cluster'
|
||||
|
||||
it_behaves_like 'editing a user-provided cluster' do
|
||||
let(:clusterable) { create(:group) }
|
||||
let(:cluster) { create(:cluster, :provided_by_user, :group, groups: [clusterable]) }
|
||||
let(:cluster_path) { group_cluster_path(clusterable, cluster) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import sqljs from 'sql.js';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import BalsamiqViewer from '~/blob/balsamiq/balsamiq_viewer';
|
||||
import ClassSpecHelper from '../../helpers/class_spec_helper';
|
||||
|
||||
describe('BalsamiqViewer', () => {
|
||||
const mockArrayBuffer = new ArrayBuffer(10);
|
||||
let balsamiqViewer;
|
||||
let viewer;
|
||||
|
||||
|
@ -19,44 +21,65 @@ describe('BalsamiqViewer', () => {
|
|||
});
|
||||
|
||||
describe('loadFile', () => {
|
||||
let xhr;
|
||||
let loadFile;
|
||||
let bv;
|
||||
const endpoint = 'endpoint';
|
||||
const requestSuccess = Promise.resolve({
|
||||
data: mockArrayBuffer,
|
||||
status: 200,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
xhr = jasmine.createSpyObj('xhr', ['open', 'send']);
|
||||
|
||||
balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['renderFile']);
|
||||
|
||||
spyOn(window, 'XMLHttpRequest').and.returnValue(xhr);
|
||||
|
||||
loadFile = BalsamiqViewer.prototype.loadFile.call(balsamiqViewer, endpoint);
|
||||
viewer = {};
|
||||
bv = new BalsamiqViewer(viewer);
|
||||
});
|
||||
|
||||
it('should call .open', () => {
|
||||
expect(xhr.open).toHaveBeenCalledWith('GET', endpoint, true);
|
||||
it('should call `axios.get` on `endpoint` param with responseType set to `arraybuffer', () => {
|
||||
spyOn(axios, 'get').and.returnValue(requestSuccess);
|
||||
spyOn(bv, 'renderFile').and.stub();
|
||||
|
||||
bv.loadFile(endpoint);
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith(
|
||||
endpoint,
|
||||
jasmine.objectContaining({
|
||||
responseType: 'arraybuffer',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should set .responseType', () => {
|
||||
expect(xhr.responseType).toBe('arraybuffer');
|
||||
it('should call `renderFile` on request success', done => {
|
||||
spyOn(axios, 'get').and.returnValue(requestSuccess);
|
||||
spyOn(bv, 'renderFile').and.callFake(() => {});
|
||||
|
||||
bv.loadFile(endpoint)
|
||||
.then(() => {
|
||||
expect(bv.renderFile).toHaveBeenCalledWith(mockArrayBuffer);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('should call .send', () => {
|
||||
expect(xhr.send).toHaveBeenCalled();
|
||||
});
|
||||
it('should not call `renderFile` on request failure', done => {
|
||||
spyOn(axios, 'get').and.returnValue(Promise.reject());
|
||||
spyOn(bv, 'renderFile');
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(loadFile).toEqual(jasmine.any(Promise));
|
||||
bv.loadFile(endpoint)
|
||||
.then(() => {
|
||||
done.fail('Expected loadFile to throw error!');
|
||||
})
|
||||
.catch(() => {
|
||||
expect(bv.renderFile).not.toHaveBeenCalled();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderFile', () => {
|
||||
let container;
|
||||
let loadEvent;
|
||||
let previews;
|
||||
|
||||
beforeEach(() => {
|
||||
loadEvent = { target: { response: {} } };
|
||||
viewer = jasmine.createSpyObj('viewer', ['appendChild']);
|
||||
previews = [document.createElement('ul'), document.createElement('ul')];
|
||||
|
||||
|
@ -73,11 +96,11 @@ describe('BalsamiqViewer', () => {
|
|||
container = containerElement;
|
||||
});
|
||||
|
||||
BalsamiqViewer.prototype.renderFile.call(balsamiqViewer, loadEvent);
|
||||
BalsamiqViewer.prototype.renderFile.call(balsamiqViewer, mockArrayBuffer);
|
||||
});
|
||||
|
||||
it('should call .initDatabase', () => {
|
||||
expect(balsamiqViewer.initDatabase).toHaveBeenCalledWith(loadEvent.target.response);
|
||||
expect(balsamiqViewer.initDatabase).toHaveBeenCalledWith(mockArrayBuffer);
|
||||
});
|
||||
|
||||
it('should call .getPreviews', () => {
|
||||
|
|
|
@ -430,4 +430,26 @@ describe ProjectPresenter do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty_repo_statistics_buttons' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:presenter) { described_class.new(project, current_user: user) }
|
||||
|
||||
subject(:empty_repo_statistics_buttons) { presenter.empty_repo_statistics_buttons }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
allow(project).to receive(:auto_devops_enabled?).and_return(false)
|
||||
end
|
||||
|
||||
it 'orders the items correctly in an empty project' do
|
||||
expect(empty_repo_statistics_buttons.map(&:label)).to start_with(
|
||||
a_string_including('New'),
|
||||
a_string_including('README'),
|
||||
a_string_including('CHANGELOG'),
|
||||
a_string_including('CONTRIBUTING'),
|
||||
a_string_including('CI/CD')
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue