Commit 618eb57f authored by Robert Speicher's avatar Robert Speicher

Merge branch 'ce-to-ee-2018-03-06' into 'master'

CE upstream - 2018-03-06 20:12 UTC

Closes #4170

See merge request gitlab-org/gitlab-ee!4869
parents 35286350 7896f160
......@@ -140,3 +140,4 @@ export default {
</div>
</div>
</template>
<script>
/* global ListIssue */
import _ from 'underscore';
import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import Api from '../../api';
export default {
name: 'BoardProjectSelect',
components: {
loadingIcon,
},
props: {
groupId: {
type: Number,
required: true,
default: 0,
},
},
data() {
return {
loading: true,
selectedProject: {},
};
},
computed: {
selectedProjectName() {
return this.selectedProject.name || 'Select a project';
},
},
mounted() {
$(this.$refs.projectsDropdown).glDropdown({
filterable: true,
filterRemote: true,
search: {
fields: ['name_with_namespace'],
},
clicked: ({ $el, e }) => {
e.preventDefault();
this.selectedProject = {
id: $el.data('project-id'),
name: $el.data('project-name'),
};
eventHub.$emit('setSelectedProject', this.selectedProject);
},
selectable: true,
data: (term, callback) => {
this.loading = true;
return Api.groupProjects(this.groupId, term, (projects) => {
this.loading = false;
callback(projects);
});
},
renderRow(project) {
return `
<li>
<a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
${_.escape(project.name)}
</a>
</li>
`;
},
text: project => project.name,
});
},
};
</script>
<template>
<div>
<label class="label-light prepend-top-10">
Project
</label>
<div
ref="projectsDropdown"
class="dropdown"
>
<button
class="dropdown-menu-toggle wide"
type="button"
data-toggle="dropdown"
aria-expanded="false"
>
{{ selectedProjectName }}
<i
class="fa fa-chevron-down"
aria-hidden="true"
>
</i>
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
<div class="dropdown-title">
<span>Projects</span>
<button
aria-label="Close"
type="button"
class="dropdown-title-button dropdown-menu-close"
>
<i
aria-hidden="true"
data-hidden="true"
class="fa fa-times dropdown-menu-close-icon"
>
</i>
</button>
</div>
<div class="dropdown-input">
<input
class="dropdown-input-field"
type="search"
placeholder="Search projects"
/>
<i
aria-hidden="true"
data-hidden="true"
class="fa fa-search dropdown-input-search"
>
</i>
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading">
<loading-icon />
</div>
</div>
</div>
</div>
</template>
......@@ -13,6 +13,7 @@ import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import
import './models/issue';
import './models/list';
import './models/milestone';
import './models/project';
import './models/assignee';
import './stores/boards_store';
import './stores/modal_store';
......
export default class IssueProject {
constructor(obj) {
this.id = obj.id;
this.path = obj.path;
}
}
......@@ -117,7 +117,10 @@
</script>
<template>
<section class="settings no-animate expanded">
<section
id="cluster-applications"
class="settings no-animate expanded"
>
<div class="settings-header">
<h4>
{{ s__('ClusterIntegration|Applications') }}
......
......@@ -7,34 +7,82 @@
import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store';
import eventHub from '../event_hub';
import { convertPermissionToBoolean } from '../../lib/utils/common_utils';
export default {
components: {
Graph,
GraphGroup,
EmptyState,
},
data() {
const metricsData = document.querySelector('#prometheus-graphs').dataset;
const store = new MonitoringStore();
props: {
hasMetrics: {
type: Boolean,
required: false,
default: true,
},
showLegend: {
type: Boolean,
required: false,
default: true,
},
showPanels: {
type: Boolean,
required: false,
default: true,
},
forceSmallGraph: {
type: Boolean,
required: false,
default: false,
},
documentationPath: {
type: String,
required: true,
},
settingsPath: {
type: String,
required: true,
},
clustersPath: {
type: String,
required: true,
},
tagsPath: {
type: String,
required: true,
},
projectPath: {
type: String,
required: true,
},
metricsEndpoint: {
type: String,
required: true,
},
deploymentEndpoint: {
type: String,
required: false,
default: null,
},
emptyGettingStartedSvgPath: {
type: String,
required: true,
},
emptyLoadingSvgPath: {
type: String,
required: true,
},
emptyUnableToConnectSvgPath: {
type: String,
required: true,
},
},
data() {
return {
store,
store: new MonitoringStore(),
state: 'gettingStarted',
hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
documentationPath: metricsData.documentationPath,
settingsPath: metricsData.settingsPath,
clustersPath: metricsData.clustersPath,
tagsPath: metricsData.tagsPath,
projectPath: metricsData.projectPath,
metricsEndpoint: metricsData.additionalMetrics,
deploymentEndpoint: metricsData.deploymentEndpoint,
emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath,
emptyLoadingSvgPath: metricsData.emptyLoadingSvgPath,
emptyUnableToConnectSvgPath: metricsData.emptyUnableToConnectSvgPath,
showEmptyState: true,
updateAspectRatio: false,
updatedAspectRatios: 0,
......@@ -67,6 +115,7 @@
window.addEventListener('resize', this.resizeThrottled, false);
}
},
methods: {
getGraphsData() {
this.state = 'loading';
......@@ -115,6 +164,7 @@
v-for="(groupData, index) in store.groups"
:key="index"
:name="groupData.group"
:show-panels="showPanels"
>
<graph
v-for="(graphData, index) in groupData.metrics"
......@@ -125,6 +175,8 @@
:deployment-data="store.deploymentData"
:project-path="projectPath"
:tags-path="tagsPath"
:show-legend="showLegend"
:small-graph="forceSmallGraph"
/>
</graph-group>
</div>
......
......@@ -52,6 +52,16 @@
type: String,
required: true,
},
showLegend: {
type: Boolean,
required: false,
default: true,
},
smallGraph: {
type: Boolean,
required: false,
default: false,
},
},
data() {
......@@ -130,7 +140,7 @@
const breakpointSize = bp.getBreakpointSize();
const query = this.graphData.queries[0];
this.margin = measurements.large.margin;
if (breakpointSize === 'xs' || breakpointSize === 'sm') {
if (this.smallGraph || breakpointSize === 'xs' || breakpointSize === 'sm') {
this.graphHeight = 300;
this.margin = measurements.small.margin;
this.measurements = measurements.small;
......@@ -182,7 +192,9 @@
this.graphHeightOffset,
);
if (this.timeSeries.length > 3) {
if (!this.showLegend) {
this.baseGraphHeight -= 50;
} else if (this.timeSeries.length > 3) {
this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
}
......@@ -255,6 +267,7 @@
:time-series="timeSeries"
:unit-of-display="unitOfDisplay"
:current-data-index="currentDataIndex"
:show-legend-group="showLegend"
/>
<svg
class="graph-data"
......
......@@ -39,6 +39,11 @@
type: Number,
required: true,
},
showLegendGroup: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
......@@ -57,8 +62,9 @@
},
rectTransform() {
const yCoordinate = ((this.graphHeight - this.margin.top) / 2)
+ (this.yLabelWidth / 2) + 10 || 0;
const yCoordinate = (((this.graphHeight - this.margin.top)
+ this.measurements.axisLabelLineOffset) / 2)
+ (this.yLabelWidth / 2) || 0;
return `translate(0, ${yCoordinate}) rotate(-90)`;
},
......@@ -166,39 +172,41 @@
>
Time
</text>
<g
class="legend-group"
v-for="(series, index) in timeSeries"
:key="index"
:transform="translateLegendGroup(index)"
>
<line
:stroke="series.lineColor"
:stroke-width="measurements.legends.height"
:stroke-dasharray="strokeDashArray(series.lineStyle)"
:x1="measurements.legends.offsetX"
:x2="measurements.legends.offsetX + measurements.legends.width"
:y1="graphHeight - measurements.legends.offsetY"
:y2="graphHeight - measurements.legends.offsetY"
/>
<text
v-if="timeSeries.length > 1"
class="legend-metric-title"
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30"
>
{{ createSeriesString(index, series) }}
</text>
<text
v-else
class="legend-metric-title"
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30"
<template v-if="showLegendGroup">
<g
class="legend-group"
v-for="(series, index) in timeSeries"
:key="index"
:transform="translateLegendGroup(index)"
>
{{ legendTitle }} {{ formatMetricUsage(series) }}
</text>
</g>
<line
:stroke="series.lineColor"
:stroke-width="measurements.legends.height"
:stroke-dasharray="strokeDashArray(series.lineStyle)"
:x1="measurements.legends.offsetX"
:x2="measurements.legends.offsetX + measurements.legends.width"
:y1="graphHeight - measurements.legends.offsetY"
:y2="graphHeight - measurements.legends.offsetY"
/>
<text
v-if="timeSeries.length > 1"
class="legend-metric-title"
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30"
>
{{ createSeriesString(index, series) }}
</text>
<text
v-else
class="legend-metric-title"
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30"
>
{{ legendTitle }} {{ formatMetricUsage(series) }}
</text>
</g>
</template>
</g>
</template>
......@@ -5,12 +5,20 @@
type: String,
required: true,
},
showPanels: {
type: Boolean,
required: false,
default: true,
},
},
};
</script>
<template>
<div class="panel panel-default prometheus-panel">
<div
v-if="showPanels"
class="panel panel-default prometheus-panel"
>
<div class="panel-heading">
<h4>{{ name }}</h4>
</div>
......@@ -18,4 +26,10 @@
<slot></slot>
</div>
</div>
<div
v-else
class="prometheus-graph-group"
>
<slot></slot>
</div>
</template>
import Vue from 'vue';
import { convertPermissionToBoolean } from '~/lib/utils/common_utils';
import Dashboard from './components/dashboard.vue';
export default () => new Vue({
el: '#prometheus-graphs',
render: createElement => createElement(Dashboard),
});
export default () => {
const el = document.getElementById('prometheus-graphs');
if (el && el.dataset) {
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(Dashboard, {
props: {
...el.dataset,
hasMetrics: convertPermissionToBoolean(el.dataset.hasMetrics),
},
});
},
});
}
};
......@@ -40,6 +40,9 @@ export default class MonitoringService {
}
getDeploymentData() {
if (!this.deploymentEndpoint) {
return Promise.resolve([]);
}
return backOffRequest(() => axios.get(this.deploymentEndpoint))
.then(resp => resp.data)
.then((response) => {
......
export default {
animation: 200,
forceFallback: true,
fallbackClass: 'is-dragging',
fallbackOnBody: true,
ghostClass: 'is-ghost',
};
......@@ -529,7 +529,8 @@
}
> text {
font-size: 12px;
fill: $theme-gray-600;
font-size: 10px;
}
}
......
......@@ -9,6 +9,7 @@ class Commit
include Mentionable
include Referable
include StaticModel
include ::Gitlab::Utils::StrongMemoize
attr_mentionable :safe_message, pipeline: :single_line
......@@ -225,11 +226,13 @@ class Commit
end
def parents
@parents ||= parent_ids.map { |id| project.commit(id) }
@parents ||= parent_ids.map { |oid| Commit.lazy(project, oid) }
end
def parent
@parent ||= project.commit(self.parent_id) if self.parent_id
strong_memoize(:parent) do
project.commit_by(oid: self.parent_id) if self.parent_id
end
end
def notes
......
......@@ -281,7 +281,8 @@ class Project < ActiveRecord::Base
scope :without_storage_feature, ->(feature) { where('storage_version < :version OR storage_version IS NULL', version: HASHED_STORAGE_FEATURES[feature]) }
scope :with_unmigrated_storage, -> { where('storage_version < :version OR storage_version IS NULL', version: LATEST_STORAGE_VERSION) }
scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) }
# last_activity_at is throttled every minute, but last_repository_updated_at is updated with every push
scope :sorted_by_activity, -> { reorder("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC") }
scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
......@@ -789,7 +790,7 @@ class Project < ActiveRecord::Base
end
def last_activity_date
last_repository_updated_at || last_activity_at || updated_at
[last_activity_at, last_repository_updated_at, updated_at].compact.max
end
def project_id
......
......@@ -15,7 +15,8 @@
"empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'),
"empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'),
"empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'),
"additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json),
"metrics-endpoint": additional_metrics_project_environment_path(@project, @environment, format: :json),
"deployment-endpoint": project_environment_deployments_path(@project, @environment, format: :json),
"project-path": project_path(@project),
"tags-path": project_tags_path(@project),
"has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: project_environment_deployments_path(@project, @environment, format: :json) } }
"has-metrics": "#{@environment.has_metrics?}" } }
- @no_container = true
- @sort ||= sort_value_recently_updated
- page_title s_('TagsPage|Tags')
- add_to_breadcrumbs("Repository", project_tree_path(@project))
.flex-list{ class: container_class }
.top-area.adjust
......
......@@ -48,7 +48,6 @@
- pipeline_default:build_trace_sections
- pipeline_default:pipeline_metrics
- pipeline_default:pipeline_notification
- pipeline_default:update_head_pipeline_for_merge_request
- pipeline_hooks:build_hooks
- pipeline_hooks:pipeline_hooks
- pipeline_processing:build_finished
......@@ -58,6 +57,7 @@
- pipeline_processing:pipeline_success
- pipeline_processing:pipeline_update
- pipeline_processing:stage_update
- pipeline_processing:update_head_pipeline_for_merge_request
- repository_check:repository_check_clear
- repository_check:repository_check_single_repository
......
......@@ -2,6 +2,8 @@ class UpdateHeadPipelineForMergeRequestWorker
include ApplicationWorker
include PipelineQueue
queue_namespace :pipeline_processing
def perform(merge_request_id)
merge_request = MergeRequest.find(merge_request_id)
pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last
......
---
title: Remove extra breadcrumb on tags
merge_request: 17562
author: Takuya Noguchi
type: fixed
---
title: Started translation into Turkish, Indonesian and Filipino
merge_request: 17526
author:
type: other
---
title: Add one group board to Libre
merge_request:
author:
type: added
---
title: Fix project dashboard showing the wrong timestamps
merge_request:
author:
type: fixed
class AddGroupIdToBoardsCe < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
return if group_id_exists?
add_column :boards, :group_id, :integer
add_foreign_key :boards, :namespaces, column: :group_id, on_delete: :cascade
add_concurrent_index :boards, :group_id
change_column_null :boards, :project_id, true
end
def down
return unless group_id_exists?
remove_foreign_key :boards, column: :group_id
remove_index :boards, :group_id if index_exists? :boards, :group_id
remove_column :boards, :group_id
execute "DELETE from boards WHERE project_id IS NULL"
change_column_null :boards, :project_id, false
end
private
def group_id_exists?
column_exists?(:boards, :group_id)
end
end
class MigrateUpdateHeadPipelineForMergeRequestSidekiqQueue < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
sidekiq_queue_migrate 'pipeline_default:update_head_pipeline_for_merge_request',
to: 'pipeline_processing:update_head_pipeline_for_merge_request'
end
def down
sidekiq_queue_migrate 'pipeline_processing:update_head_pipeline_for_merge_request',
to: 'pipeline_default:update_head_pipeline_for_merge_request'
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180306074045) do
ActiveRecord::Schema.define(version: 20180307012445) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......
......@@ -139,6 +139,14 @@ CREATE EXTENSION pg_trgm;
On some systems you may need to install an additional package (e.g.
`postgresql-contrib`) for this extension to become available.
#### Additional requirements for GitLab Geo
If you are using [GitLab Geo](https://docs.gitlab.com/ee/development/geo.html), the [tracking database](https://docs.gitlab.com/ee/development/geo.html#geo-tracking-database) also requires the `postgres_fdw` extension.
```
CREATE EXTENSION postgres_fdw;
```
## Unicorn Workers
It's possible to increase the amount of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
......
......@@ -329,6 +329,16 @@ Click the button at the top right to toggle focus mode on and off. In focus mode
[Developers and up](../permissions.md) can use all the functionality of the
Issue Board, that is create/delete lists and drag issues around.
## Group Issue Board
>Introduced in GitLab 10.6
Group issue board is analogous to project-level issue board and it is accessible at the group
navigation level. A group-level issue board allows you to view all issues from all projects in that group
(currently, it does not see issues from projects in subgroups). Similarly, you can only filter by group labels for these
boards. When updating milestones and labels for an issue through the sidebar update mechanism, again only
group-level objects are available.
## Tips
A few things to remember:
......
......@@ -2,12 +2,16 @@ class AddGroupIdToBoards < ActiveRecord::Migration
DOWNTIME = false
def up
return if group_id_exists?
change_column_null :boards, :project_id, true
add_column :boards, :group_id, :integer
end
def down
return unless group_id_exists?
# We cannot rollback project_id not null constraint if there are records
# with null values.
execute "DELETE from boards WHERE project_id IS NULL"
......@@ -15,4 +19,10 @@ class AddGroupIdToBoards < ActiveRecord::Migration
remove_column :boards, :group_id
change_column :boards, :project_id, :integer, null: false
end
private
def group_id_exists?
column_exists?(:boards, :group_id)
end
end
......@@ -6,14 +6,26 @@ class AddGroupBoardsIndexes < ActiveRecord::Migration
DOWNTIME = false
def up
return if foreign_key_exists?(:boards, :group_id)
add_concurrent_foreign_key :boards, :namespaces, column: :group_id, on_delete: :cascade
add_concurrent_index :boards, :group_id
end
def down
return unless foreign_key_exists?(:boards, :group_id)
remove_foreign_key :boards, column: :group_id
remove_concurrent_index :boards, :group_id
end
private
def foreign_key_exists?(table, column)
foreign_keys(table).any? do |key|
key.options[:column] == column.to_s
end
end
end
......@@ -132,6 +132,7 @@ module API
mount ::API::Events
mount ::API::Features
mount ::API::Files
mount ::API::GroupBoards
mount ::API::Groups
mount ::API::GroupBoards
mount ::API::GroupMilestones
......
......@@ -137,7 +137,8 @@ module Gitlab
job.create_job_artifacts_trace!(
project: job.project,
file_type: :trace,
file: stream)
file: stream,
file_sha256: Digest::SHA256.file(path).hexdigest)
end
end
......
......@@ -18,7 +18,10 @@ module Gitlab
'uk' => 'Українська',
'ja' => '日本語',
'ko' => '한국어',
'nl_NL' => 'Nederlands'
'nl_NL' => 'Nederlands',
'tr_TR' => 'Türkçe',
'id_ID' => 'Bahasa Indonesia',
'fil_PH' => 'Filipino'
}.freeze
def available_locales
......
......@@ -37,6 +37,14 @@ feature 'Dashboard Projects' do
expect(page).to have_xpath("//time[@datetime='#{project.last_repository_updated_at.getutc.iso8601}']")
end
it 'shows the last_activity_at attribute as the update date' do
project.update_attributes!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.now)
visit dashboard_projects_path
expect(page).to have_xpath("//time[@datetime='#{project.last_activity_at.getutc.iso8601}']")
end
end
context 'when last_repository_updated_at and last_activity_at are missing' do
......
require 'spec_helper'
describe Projects::EnvironmentsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'environments-project') }
let(:environment) { create(:environment, name: 'production', project: project) }
render_views
before(:all) do
clean_frontend_fixtures('environments/metrics')
end
before do
sign_in(admin)
end
it 'environments/metrics/metrics.html.raw' do |example|
get :metrics,
namespace_id: project.namespace,
project_id: project,
id: environment.id
expect(response).to be_success
store_frontend_fixture(response, example.description)
end
end
......@@ -5,24 +5,35 @@ import axios from '~/lib/utils/axios_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint } from './mock_data';
describe('Dashboard', () => {
const fixtureName = 'environments/metrics/metrics.html.raw';
let DashboardComponent;
let component;
preloadFixtures(fixtureName);
const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
metricsEndpoint: mockApiEndpoint,
deploymentEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
};
beforeEach(() => {
loadFixtures(fixtureName);
setFixtures('<div class="prometheus-graphs"></div>');
DashboardComponent = Vue.extend(Dashboard);
});
describe('no metrics are available yet', () => {
it('shows a getting started empty state when no metrics are present', () => {
component = new DashboardComponent({
el: document.querySelector('#prometheus-graphs'),
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData,
});
component.$mount();
expect(component.$el.querySelector('#prometheus-graphs')).toBe(null);
expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
expect(component.state).toEqual('gettingStarted');
});
});
......@@ -30,11 +41,8 @@ describe('Dashboard', () => {
describe('requests information to the server', () => {
let mock;
beforeEach(() => {
document.querySelector('#prometheus-graphs').setAttribute('data-has-metrics', 'true');
mock = new MockAdapter(axios);
mock.onGet(mockApiEndpoint).reply(200, {
metricsGroupsAPIResponse,
});
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
});
afterEach(() => {
......@@ -42,14 +50,43 @@ describe('Dashboard', () => {
});
it('shows up a loading state', (done) => {
component = new DashboardComponent({
el: document.querySelector('#prometheus-graphs'),
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true },
});
component.$mount();
Vue.nextTick(() => {
expect(component.state).toEqual('loading');
done();
});
});
it('hides the legend when showLegend is false', (done) => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showLegend: false },
});
setTimeout(() => {
expect(component.showEmptyState).toEqual(false);
expect(component.$el.querySelector('.legend-group')).toEqual(null);
expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
done();
});
});
it('hides the group panels when showPanels is false', (done) => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
});
setTimeout(() => {
expect(component.showEmptyState).toEqual(false);
expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
done();
});
});
});
});
......@@ -413,7 +413,8 @@ describe Gitlab::Ci::Trace do
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(File.exist?(src_path)).to be_falsy
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).digest)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).hexdigest)
expect(build.job_artifacts_trace.file_sha256).to eq(src_checksum)
end
end
......@@ -438,7 +439,8 @@ describe Gitlab::Ci::Trace do
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(build.old_trace).to be_nil
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).digest)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).hexdigest)
expect(build.job_artifacts_trace.file_sha256).to eq(src_checksum)
end
end
......@@ -457,7 +459,7 @@ describe Gitlab::Ci::Trace do
context 'when trace file stored in default path' do
let!(:build) { create(:ci_build, :success, :trace_live) }
let!(:src_path) { trace.read { |s| return s.path } }
let!(:src_checksum) { Digest::SHA256.file(src_path).digest }
let!(:src_checksum) { Digest::SHA256.file(src_path).hexdigest }
it_behaves_like 'archive trace file'
......@@ -483,7 +485,7 @@ describe Gitlab::Ci::Trace do
context 'when trace is stored in database' do
let(:build) { create(:ci_build, :success) }
let(:trace_content) { 'Sample trace' }
let!(:src_checksum) { Digest::SHA256.digest(trace_content) }
let!(:src_checksum) { Digest::SHA256.hexdigest(trace_content) }
before do
build.update_column(:trace, trace_content)
......
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20180307012445_migrate_update_head_pipeline_for_merge_request_sidekiq_queue.rb')
describe MigrateUpdateHeadPipelineForMergeRequestSidekiqQueue, :sidekiq, :redis do
include Gitlab::Database::MigrationHelpers
context 'when there are jobs in the queues' do
it 'correctly migrates queue when migrating up' do
Sidekiq::Testing.disable! do
stubbed_worker(queue: 'pipeline_default:update_head_pipeline_for_merge_request').perform_async('Something', [1])
stubbed_worker(queue: 'pipeline_processing:update_head_pipeline_for_merge_request').perform_async('Something', [1])
described_class.new.up
expect(sidekiq_queue_length('pipeline_default:update_head_pipeline_for_merge_request')).to eq 0
expect(sidekiq_queue_length('pipeline_processing:update_head_pipeline_for_merge_request')).to eq 2
end
end
it 'does not affect other queues under the same namespace' do
Sidekiq::Testing.disable! do
stubbed_worker(queue: 'pipeline_default:build_coverage').perform_async('Something', [1])
stubbed_worker(queue: 'pipeline_default:build_trace_sections').perform_async('Something', [1])
stubbed_worker(queue: 'pipeline_default:pipeline_metrics').perform_async('Something', [1])
stubbed_worker(queue: 'pipeline_default:pipeline_notification').perform_async('Something', [1])
described_class.new.up
expect(sidekiq_queue_length('pipeline_default:build_coverage')).to eq 1
expect(sidekiq_queue_length('pipeline_default:build_trace_sections')).to eq 1
expect(sidekiq_queue_length('pipeline_default:pipeline_metrics')).to eq 1
expect(sidekiq_queue_length('pipeline_default:pipeline_notification')).to eq 1
end
end
it 'correctly migrates queue when migrating down' do
Sidekiq::Testing.disable! do
stubbed_worker(queue: 'pipeline_processing:update_head_pipeline_for_merge_request').perform_async('Something', [1])
described_class.new.down
expect(sidekiq_queue_length('pipeline_default:update_head_pipeline_for_merge_request')).to eq 1
expect(sidekiq_queue_length('pipeline_processing:update_head_pipeline_for_merge_request')).to eq 0
end
end
end
context 'when there are no jobs in the queues' do
it 'does not raise error when migrating up' do
expect { described_class.new.up }.not_to raise_error
end
it 'does not raise error when migrating down' do
expect { described_class.new.down }.not_to raise_error
end
end
def stubbed_worker(queue:)
Class.new do
include Sidekiq::Worker
sidekiq_options queue: queue
end
end
end
......@@ -564,6 +564,20 @@ describe Project do
it 'returns the project\'s last update date if it has no events' do
expect(project.last_activity_date).to eq(project.updated_at)
end
it 'returns the most recent timestamp' do
project.update_attributes(updated_at: nil,
last_activity_at: timestamp,
last_repository_updated_at: timestamp - 1.hour)
expect(project.last_activity_date).to eq(timestamp)
project.update_attributes(updated_at: timestamp,
last_activity_at: timestamp - 1.hour,
last_repository_updated_at: nil)
expect(project.last_activity_date).to eq(timestamp)
end
end
end
......
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