Commit b042382b authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent eabf8fd7
# When adding a group as a code owner, make sure to invite the group to the
# project here: https://gitlab.com/gitlab-org/gitlab/-/project_members
# As described in https://docs.gitlab.com/ee/user/project/code_owners.html
# Backend Maintainers are the default for all ruby files
*.rb @gitlab-org/maintainers/rails-backend
*.rake @gitlab-org/maintainers/rails-backend
......@@ -28,9 +32,13 @@ lib/gitlab/github_import/ @gitlab-org/maintainers/database
/ee/app/models/project_alias.rb @patrickbajao
/ee/lib/api/project_aliases.rb @patrickbajao
# Quality owned files
/qa/ @gl-quality
# Engineering Productivity owned files
/.gitlab-ci.yml @gl-quality/eng-prod
/.gitlab/ci/ @gl-quality/eng-prod
/.gitlab/CODEOWNERS @gl-quality/eng-prod
Dangerfile @gl-quality/eng-prod
/danger/ @gl-quality/eng-prod
/lib/gitlab/danger/ @gl-quality/eng-prod
......
......@@ -1326,7 +1326,7 @@ DEPENDENCIES
pry-rails (~> 0.3.9)
rack (~> 2.0.7)
rack-attack (~> 6.2.0)
rack-cors (~> 1.0.0)
rack-cors (~> 1.0.6)
rack-oauth2 (~> 1.9.3)
rack-proxy (~> 0.6.0)
rack-timeout
......
......@@ -37,6 +37,7 @@ class ApplicationController < ActionController::Base
around_action :set_current_context
around_action :set_locale
around_action :set_session_storage
around_action :set_current_admin
after_action :set_page_title_header, if: :json_request?
after_action :limit_session_time, if: -> { !current_user }
......@@ -473,6 +474,13 @@ class ApplicationController < ActionController::Base
response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
end
def set_current_admin(&block)
return yield unless Feature.enabled?(:user_mode_in_session)
return yield unless current_user
Gitlab::Auth::CurrentUserMode.with_current_admin(current_user, &block)
end
def html_request?
request.format.html?
end
......
......@@ -10,9 +10,9 @@ module CycleAnalyticsParams
end
def cycle_analytics_group_params
return {} unless params[:cycle_analytics].present?
return {} unless params.present?
params[:cycle_analytics].permit(:start_date, :created_after, :created_before, project_ids: [])
params.permit(:group_id, :start_date, :created_after, :created_before, project_ids: [])
end
def options(params)
......
......@@ -9,7 +9,10 @@ module MergeRequests
def self.enqueue!
ids = MergeRequestDiff.ids_for_external_storage_migration(limit: MAX_JOBS)
MigrateExternalDiffsWorker.bulk_perform_async(ids.map { |id| [id] }) # rubocop:disable Scalability/BulkPerformWithContext
# rubocop:disable Scalability/BulkPerformWithContext
# https://gitlab.com/gitlab-org/gitlab/issues/202100
MigrateExternalDiffsWorker.bulk_perform_async(ids.map { |id| [id] })
# rubocop:enable Scalability/BulkPerformWithContext
end
def initialize(merge_request_diff)
......
......@@ -30,6 +30,27 @@ module Projects
settings = params[:error_tracking_setting_attributes]
return {} if settings.blank?
if error_tracking_params_partial_updates?(settings)
error_tracking_params_for_partial_update(settings)
else
error_tracking_params_for_update(settings)
end
end
def error_tracking_params_partial_updates?(settings)
# Help from @splattael :bow:
# Make sure we're converting to symbols because
# * ActionController::Parameters#keys returns a list of strings
# * in specs we're using hashes with symbols as keys
settings.keys.map(&:to_sym) == %i[enabled]
end
def error_tracking_params_for_partial_update(settings)
{ error_tracking_setting_attributes: settings }
end
def error_tracking_params_for_update(settings)
api_url = ::ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
api_host: settings[:api_host],
project_slug: settings.dig(:project, :slug),
......
......@@ -2,7 +2,12 @@
class ScheduleMigrateExternalDiffsWorker
include ApplicationWorker
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
# rubocop:disable Scalability/CronWorkerContext:
# This schedules the `MigrateExternalDiffsWorker`
# issue for adding context: https://gitlab.com/gitlab-org/gitlab/issues/202100
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext:
include Gitlab::ExclusiveLeaseHelpers
feature_category :source_code_management
......
---
title: Add non_archived param to issues API endpoint to filter issues from archived projects
merge_request: 23785
author:
type: added
---
title: Add non_archived param to group merge requests API endpoint to filter MRs from non archived projects
merge_request: 23809
author:
type: added
---
title: Add API to enable and disable error tracking settings
merge_request: 24220
author: Rajendra Kadam
type: added
---
title: Admin mode support in sidekiq jobs
merge_request: 24388
author: Diego Louzán
type: changed
......@@ -941,7 +941,7 @@ projects.each do |p|
container_repositories.each do |c|
c.tags.each do |t|
project_total_size = project_total_size + t.total_size
project_total_size = project_total_size + t.total_size unless t.total_size.nil?
end
end
......
......@@ -30,3 +30,31 @@ Example response:
"api_url": "https://sentry.io/api/0/projects/myawesomeproject/project"
}
```
### Enable or disable the Error Tracking project settings
The API allows you to enable or disable the Error Tracking settings for a project. Only for project maintainers.
```
PATCH /projects/:id/error_tracking/settings
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | integer | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `active` | boolean | yes | Pass `true` to enable the already configured error tracking settings or `false` to disable it. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/error_tracking/settings?active=true
```
Example response:
```json
{
"active": true,
"project_name": "sample sentry project",
"sentry_external_url": "https://sentry.io/myawesomeproject/project",
"api_url": "https://sentry.io/api/0/projects/myawesomeproject/project"
}
```
......@@ -222,6 +222,7 @@ GET /groups/:id/issues?confidential=true
| `updated_before` | datetime | no | Return issues updated on or before the given time |
| `confidential` | Boolean | no | Filter confidential or public issues. |
| `not` | Hash | no | Return issues that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `my_reaction_emoji`, `search`, `in` |
| `non_archived` | Boolean | no | Return issues from non archived projects. Default is true. _(Introduced in [GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23785))_ |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues
......
......@@ -397,6 +397,7 @@ Parameters:
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
| `non_archived` | Boolean | no | Return merge requests from non archived projects only. Default is true. _(Introduced in [GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23809))_ |
```json
[
......
......@@ -9,7 +9,4 @@ You can import your existing repositories by providing the Git URL:
1. Click **Create project** to begin the import process
1. Once complete, you will be redirected to your newly created project
NOTE: **Note:**
If your password has special characters, you will need to enter them URL encoded, please see the [GitLab issue](https://gitlab.com/gitlab-org/gitlab/issues/29952) for more information.
![Import project by repo URL](img/import_projects_from_repo_url.png)
......@@ -23,6 +23,34 @@ module API
present setting, with: Entities::ErrorTracking::ProjectSetting
end
desc 'Enable or disable error tracking settings for the project' do
detail 'This feature was introduced in GitLab 12.8.'
success Entities::ErrorTracking::ProjectSetting
end
params do
requires :active, type: Boolean, desc: 'Specifying whether to enable or disable error tracking settings', allow_blank: false
end
patch ':id/error_tracking/settings/' do
authorize! :admin_operations, user_project
setting = user_project.error_tracking_setting
not_found!('Error Tracking Setting') unless setting
update_params = {
error_tracking_setting_attributes: { enabled: params[:active] }
}
result = ::Projects::Operations::UpdateService.new(user_project, current_user, update_params).execute
if result[:status] == :success
present setting, with: Entities::ErrorTracking::ProjectSetting
else
result
end
end
end
end
end
......@@ -120,6 +120,7 @@ module API
end
params do
use :issues_params
optional :non_archived, type: Boolean, desc: 'Return issues from non archived projects', default: true
end
get ":id/issues" do
issues = paginate(find_issues(group_id: user_group.id, include_subgroups: true))
......
......@@ -141,6 +141,8 @@ module API
end
params do
use :merge_requests_params
optional :non_archived, type: Boolean, desc: 'Return merge requests from non archived projects',
default: true
end
get ":id/merge_requests" do
merge_requests = find_merge_requests(group_id: user_group.id, include_subgroups: true)
......
......@@ -10,12 +10,54 @@ module Gitlab
class CurrentUserMode
NotRequestedError = Class.new(StandardError)
# RequestStore entries
CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY = { res: :current_user_mode, data: :bypass_session_admin_id }.freeze
CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY = { res: :current_user_mode, data: :current_admin }.freeze
# SessionStore entries
SESSION_STORE_KEY = :current_user_mode
ADMIN_MODE_START_TIME_KEY = 'admin_mode'
ADMIN_MODE_REQUESTED_TIME_KEY = 'admin_mode_requested'
ADMIN_MODE_START_TIME_KEY = :admin_mode
ADMIN_MODE_REQUESTED_TIME_KEY = :admin_mode_requested
MAX_ADMIN_MODE_TIME = 6.hours
ADMIN_MODE_REQUESTED_GRACE_PERIOD = 5.minutes
class << self
# Admin mode activation requires storing a flag in the user session. Using this
# method when scheduling jobs in Sidekiq will bypass the session check for a
# user that was already in admin mode
def bypass_session!(admin_id)
Gitlab::SafeRequestStore[CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY] = admin_id
Gitlab::AppLogger.debug("Bypassing session in admin mode for: #{admin_id}")
yield
ensure
Gitlab::SafeRequestStore.delete(CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY)
end
def bypass_session_admin_id
Gitlab::SafeRequestStore[CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY]
end
# Store in the current request the provided user model (only if in admin mode)
# and yield
def with_current_admin(admin)
return yield unless self.new(admin).admin_mode?
Gitlab::SafeRequestStore[CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY] = admin
Gitlab::AppLogger.debug("Admin mode active for: #{admin.username}")
yield
ensure
Gitlab::SafeRequestStore.delete(CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY)
end
def current_admin
Gitlab::SafeRequestStore[CURRENT_REQUEST_ADMIN_MODE_USER_RS_KEY]
end
end
def initialize(user)
@user = user
end
......@@ -42,7 +84,7 @@ module Gitlab
raise NotRequestedError unless admin_mode_requested?
reset_request_store
reset_request_store_cache_entries
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now
......@@ -55,7 +97,7 @@ module Gitlab
def disable_admin_mode!
return unless user&.admin?
reset_request_store
reset_request_store_cache_entries
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil
current_session_data[ADMIN_MODE_START_TIME_KEY] = nil
......@@ -64,7 +106,7 @@ module Gitlab
def request_admin_mode!
return unless user&.admin?
reset_request_store
reset_request_store_cache_entries
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = Time.now
end
......@@ -73,10 +115,12 @@ module Gitlab
attr_reader :user
# RequestStore entry to cache #admin_mode? result
def admin_mode_rs_key
@admin_mode_rs_key ||= { res: :current_user_mode, user: user.id, method: :admin_mode? }
end
# RequestStore entry to cache #admin_mode_requested? result
def admin_mode_requested_rs_key
@admin_mode_requested_rs_key ||= { res: :current_user_mode, user: user.id, method: :admin_mode_requested? }
end
......@@ -86,6 +130,7 @@ module Gitlab
end
def any_session_with_admin_mode?
return true if bypass_session?
return true if current_session_data.initiated? && current_session_data[ADMIN_MODE_START_TIME_KEY].to_i > MAX_ADMIN_MODE_TIME.ago.to_i
all_sessions.any? do |session|
......@@ -103,7 +148,11 @@ module Gitlab
current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY].to_i > ADMIN_MODE_REQUESTED_GRACE_PERIOD.ago.to_i
end
def reset_request_store
def bypass_session?
user&.id && user.id == self.class.bypass_session_admin_id
end
def reset_request_store_cache_entries
Gitlab::SafeRequestStore.delete(admin_mode_rs_key)
Gitlab::SafeRequestStore.delete(admin_mode_requested_rs_key)
end
......
......@@ -17,6 +17,7 @@ module Gitlab
chain.add Gitlab::SidekiqMiddleware::BatchLoader
chain.add Labkit::Middleware::Sidekiq::Server
chain.add Gitlab::SidekiqMiddleware::InstrumentationLogger
chain.add Gitlab::SidekiqMiddleware::AdminMode::Server
chain.add Gitlab::SidekiqStatus::ServerMiddleware
chain.add Gitlab::SidekiqMiddleware::WorkerContext::Server
end
......@@ -31,6 +32,7 @@ module Gitlab
chain.add Gitlab::SidekiqMiddleware::ClientMetrics
chain.add Gitlab::SidekiqMiddleware::WorkerContext::Client # needs to be before the Labkit middleware
chain.add Labkit::Middleware::Sidekiq::Client
chain.add Gitlab::SidekiqMiddleware::AdminMode::Client
end
end
end
......
# frozen_string_literal: true
module Gitlab
module SidekiqMiddleware
module AdminMode
# Checks if admin mode is enabled for the request creating the sidekiq job
# by examining if admin mode has been enabled for the user
# If enabled then it injects a job field that persists through the job execution
class Client
def call(_worker_class, job, _queue, _redis_pool)
return yield unless Feature.enabled?(:user_mode_in_session)
# Admin mode enabled in the original request or in a nested sidekiq job
admin_mode_user_id = find_admin_user_id
if admin_mode_user_id
job['admin_mode_user_id'] ||= admin_mode_user_id
Gitlab::AppLogger.debug("AdminMode::Client injected admin mode for job: #{job.inspect}")
end
yield
end
private
def find_admin_user_id
Gitlab::Auth::CurrentUserMode.current_admin&.id ||
Gitlab::Auth::CurrentUserMode.bypass_session_admin_id
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module SidekiqMiddleware
module AdminMode
class Server
def call(_worker, job, _queue)
return yield unless Feature.enabled?(:user_mode_in_session)
admin_mode_user_id = job['admin_mode_user_id']
# Do not bypass session if this job was not enabled with admin mode on
return yield unless admin_mode_user_id
Gitlab::Auth::CurrentUserMode.bypass_session!(admin_mode_user_id) do
Gitlab::AppLogger.debug("AdminMode::Server bypasses session for admin mode in job: #{job.inspect}")
yield
end
end
end
end
end
end
......@@ -96,7 +96,7 @@ describe GroupsController do
User.where(id: [admin, owner, maintainer, developer, guest]).update_all(can_create_group: can_create_group_status)
end
[:admin, :owner].each do |member_type|
[:admin, :owner, :maintainer].each do |member_type|
context "and logged in as #{member_type.capitalize}" do
it_behaves_like 'member with ability to create subgroups' do
let(:member) { send(member_type) }
......@@ -104,7 +104,7 @@ describe GroupsController do
end
end
[:guest, :developer, :maintainer].each do |member_type|
[:guest, :developer].each do |member_type|
context "and logged in as #{member_type.capitalize}" do
it_behaves_like 'member without ability to create subgroups' do
let(:member) { send(member_type) }
......
......@@ -8,5 +8,9 @@ FactoryBot.define do
token { 'access_token_123' }
project_name { 'Sentry Project' }
organization_name { 'Sentry Org' }
trait :disabled do
enabled { false }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
# Test an operation that triggers background jobs requiring administrative rights
describe 'Admin mode for workers', :do_not_mock_admin_mode, :request_store, :clean_gitlab_redis_shared_state do
let(:user) { create(:user) }
let(:user_to_delete) { create(:user) }
before do
add_sidekiq_middleware
sign_in(user)
end
context 'as a regular user' do
it 'cannot delete user' do
visit admin_user_path(user_to_delete)
expect(page).to have_gitlab_http_status(:not_found)
end
end
context 'as an admin user' do
let(:user) { create(:admin) }
context 'when admin mode disabled' do
it 'cannot delete user', :js do
visit admin_user_path(user_to_delete)
expect(page).to have_content('Re-authentication required')
end
end
context 'when admin mode enabled', :delete do
before do
gitlab_enable_admin_mode_sign_in(user)
end
it 'can delete user', :sidekiq, :js do
visit admin_user_path(user_to_delete)
click_button 'Delete user'
page.within '.modal-dialog' do
find("input[name='username']").send_keys(user_to_delete.name)
click_button 'Delete user'
wait_for_requests
end
expect(page).to have_content('The user is being deleted.')
# Perform jobs while logged out so that admin mode is only enabled in job metadata
execute_jobs_signed_out(user)
visit admin_user_path(user_to_delete)
expect(page).to have_title('Not Found')
end
end
end
def add_sidekiq_middleware
Sidekiq::Testing.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::AdminMode::Server
end
end
def execute_jobs_signed_out(user)
gitlab_sign_out
Sidekiq::Worker.drain_all
sign_in(user)
gitlab_enable_admin_mode_sign_in(user)
end
end
......@@ -2,15 +2,32 @@
require 'spec_helper'
describe 'Admin uses repository checks' do
describe 'Admin uses repository checks', :request_store, :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
include StubENV
let(:admin) { create(:admin) }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(create(:admin))
sign_in(admin)
end
it 'to trigger a single check' do
context 'when admin mode is disabled' do
it 'admin project page requires admin mode' do
project = create(:project)
visit_admin_project_page(project)
expect(page).not_to have_css('.repository-check')
expect(page).to have_content('Enter Admin Mode')
end
end
context 'when admin mode is enabled' do
before do
gitlab_enable_admin_mode_sign_in(admin)
end
it 'to trigger a single check', :js do
project = create(:project)
visit_admin_project_page(project)
......@@ -43,6 +60,7 @@ describe 'Admin uses repository checks' do
expect(page).to have_content('Started asynchronous removal of all repository check states.')
end
end
def visit_admin_project_page(project)
visit admin_project_path(project)
......
import './get_client_rects';
import './inner_text';
import './window_scroll_to';
import Vue from 'vue';
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { waitForMutation } from 'spec/helpers/vue_test_utils_helper';
import { getJSONFixture } from 'helpers/fixtures';
import axios from '~/lib/utils/axios_utils';
import jobApp from '~/jobs/components/job_app.vue';
import JobApp from '~/jobs/components/job_app.vue';
import createStore from '~/jobs/store';
import * as types from '~/jobs/store/mutation_types';
import job from '../mock_data';
describe('Job App ', () => {
describe('Job App', () => {
const localVue = createLocalVue();
localVue.use(Vuex);
const delayedJobFixture = getJSONFixture('jobs/delayed.json');
const Component = Vue.extend(jobApp);
let store;
let vm;
let wrapper;
let mock;
const initSettings = {
......@@ -32,16 +34,24 @@ describe('Job App ', () => {
subscriptionsMoreMinutesUrl: 'https://customers.gitlab.com/buy_pipeline_minutes',
};
const waitForJobReceived = () => waitForMutation(store, types.RECEIVE_JOB_SUCCESS);
const createComponent = () => {
wrapper = mount(JobApp, { propsData: { ...props }, store });
};
const setupAndMount = ({ jobData = {}, traceData = {} } = {}) => {
mock.onGet(initSettings.endpoint).replyOnce(200, { ...job, ...jobData });
mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, traceData);
store.dispatch('init', initSettings);
const asyncInit = store.dispatch('init', initSettings);
vm = mountComponentWithStore(Component, { props, store });
createComponent();
return waitForJobReceived();
return asyncInit
.then(() => {
jest.runOnlyPendingTimers();
})
.then(() => axios.waitForAll())
.then(() => wrapper.vm.$nextTick());
};
beforeEach(() => {
......@@ -50,94 +60,81 @@ describe('Job App ', () => {
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
mock.restore();
});
describe('while loading', () => {
beforeEach(() => {
setupAndMount();
store.state.isLoading = true;
createComponent();
});
it('renders loading icon', () => {
expect(vm.$el.querySelector('.js-job-loading')).not.toBeNull();
expect(vm.$el.querySelector('.js-job-sidebar')).toBeNull();
expect(vm.$el.querySelector('.js-job-content')).toBeNull();
expect(wrapper.find('.js-job-loading').exists()).toBe(true);
expect(wrapper.find('.js-job-sidebar').exists()).toBe(false);
expect(wrapper.find('.js-job-content').exists()).toBe(false);
});
});
describe('with successful request', () => {
describe('Header section', () => {
describe('job callout message', () => {
it('should not render the reason when reason is absent', done => {
setupAndMount()
.then(() => {
expect(vm.shouldRenderCalloutMessage).toBe(false);
})
.then(done)
.catch(done.fail);
});
it('should not render the reason when reason is absent', () =>
setupAndMount().then(() => {
expect(wrapper.vm.shouldRenderCalloutMessage).toBe(false);
}));
it('should render the reason when reason is present', done => {
it('should render the reason when reason is present', () =>
setupAndMount({
jobData: {
callout_message: 'There is an unkown failure, please try again',
},
})
.then(() => {
expect(vm.shouldRenderCalloutMessage).toBe(true);
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.vm.shouldRenderCalloutMessage).toBe(true);
}));
});
describe('triggered job', () => {
beforeEach(done => {
beforeEach(() => {
const aYearAgo = new Date();
aYearAgo.setFullYear(aYearAgo.getFullYear() - 1);
setupAndMount({ jobData: { started: aYearAgo.toISOString() } })
.then(done)
.catch(done.fail);
return setupAndMount({ jobData: { started: aYearAgo.toISOString() } });
});
it('should render provided job information', () => {
expect(
vm.$el
.querySelector('.header-main-content')
.textContent.replace(/\s+/g, ' ')
wrapper
.find('.header-main-content')
.text()
.replace(/\s+/g, ' ')
.trim(),
).toContain('passed Job #4757 triggered 1 year ago by Root');
});
it('should render new issue link', () => {
expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual(
job.new_issue_path,
);
expect(wrapper.find('.js-new-issue').attributes('href')).toEqual(job.new_issue_path);
});
});
describe('created job', () => {
it('should render created key', done => {
setupAndMount()
.then(() => {
it('should render created key', () =>
setupAndMount().then(() => {
expect(
vm.$el
.querySelector('.header-main-content')
.textContent.replace(/\s+/g, ' ')
wrapper
.find('.header-main-content')
.text()
.replace(/\s+/g, ' ')
.trim(),
).toContain('passed Job #4757 created 3 weeks ago by Root');
})
.then(done)
.catch(done.fail);
});
}));
});
});
describe('stuck block', () => {
describe('without active runners availabl', () => {
it('renders stuck block when there are no runners', done => {
it('renders stuck block when there are no runners', () =>
setupAndMount({
jobData: {
status: {
......@@ -154,20 +151,14 @@ describe('Job App ', () => {
},
tags: [],
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-stuck')).not.toBeNull();
expect(
vm.$el.querySelector('.js-job-stuck .js-stuck-no-active-runner'),
).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-stuck').exists()).toBe(true);
expect(wrapper.find('.js-job-stuck .js-stuck-no-active-runner').exists()).toBe(true);
}));
});
describe('when available runners can not run specified tag', () => {
it('renders tags in stuck block when there are no runners', done => {
it('renders tags in stuck block when there are no runners', () =>
setupAndMount({
jobData: {
status: {
......@@ -183,18 +174,14 @@ describe('Job App ', () => {
online: false,
},
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]);
expect(vm.$el.querySelector('.js-job-stuck .js-stuck-with-tags')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-stuck').text()).toContain(job.tags[0]);
expect(wrapper.find('.js-job-stuck .js-stuck-with-tags').exists()).toBe(true);
}));
});
describe('when runners are offline and build has tags', () => {
it('renders message about job being stuck because of no runners with the specified tags', done => {
it('renders message about job being stuck because of no runners with the specified tags', () =>
setupAndMount({
jobData: {
status: {
......@@ -210,32 +197,24 @@ describe('Job App ', () => {
online: true,
},
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]);
expect(vm.$el.querySelector('.js-job-stuck .js-stuck-with-tags')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-stuck').text()).toContain(job.tags[0]);
expect(wrapper.find('.js-job-stuck .js-stuck-with-tags').exists()).toBe(true);
}));
});
it('does not renders stuck block when there are no runners', done => {
it('does not renders stuck block when there are no runners', () =>
setupAndMount({
jobData: {
runners: { available: true },
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-stuck')).toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-stuck').exists()).toBe(false);
}));
});
describe('unmet prerequisites block', () => {
it('renders unmet prerequisites block when there is an unmet prerequisites failure', done => {
it('renders unmet prerequisites block when there is an unmet prerequisites failure', () =>
setupAndMount({
jobData: {
status: {
......@@ -258,17 +237,13 @@ describe('Job App ', () => {
},
tags: [],
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-failed')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-failed').exists()).toBe(true);
}));
});
describe('environments block', () => {
it('renders environment block when job has environment', done => {
it('renders environment block when job has environment', () =>
setupAndMount({
jobData: {
deployment_status: {
......@@ -278,26 +253,18 @@ describe('Job App ', () => {
},
},
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-environment')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-environment').exists()).toBe(true);
}));
it('does not render environment block when job has environment', done => {
setupAndMount()
.then(() => {
expect(vm.$el.querySelector('.js-job-environment')).toBeNull();
})
.then(done)
.catch(done.fail);
});
it('does not render environment block when job has environment', () =>
setupAndMount().then(() => {
expect(wrapper.find('.js-job-environment').exists()).toBe(false);
}));
});
describe('erased block', () => {
it('renders erased block when `erased` is true', done => {
it('renders erased block when `erased` is true', () =>
setupAndMount({
jobData: {
erased_by: {
......@@ -306,30 +273,22 @@ describe('Job App ', () => {
},
erased_at: '2016-11-07T11:11:16.525Z',
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-erased-block')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-erased-block').exists()).toBe(true);
}));
it('does not render erased block when `erased` is false', done => {
it('does not render erased block when `erased` is false', () =>
setupAndMount({
jobData: {
erased_at: null,
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-erased-block')).toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-erased-block').exists()).toBe(false);
}));
});
describe('empty states block', () => {
it('renders empty state when job does not have trace and is not running', done => {
it('renders empty state when job does not have trace and is not running', () =>
setupAndMount({
jobData: {
has_trace: false,
......@@ -352,15 +311,11 @@ describe('Job App ', () => {
},
},
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-empty-state').exists()).toBe(true);
}));
it('does not render empty state when job does not have trace but it is running', done => {
it('does not render empty state when job does not have trace but it is running', () =>
setupAndMount({
jobData: {
has_trace: false,
......@@ -372,38 +327,29 @@ describe('Job App ', () => {
details_path: 'path',
},
},
})
.then(() => {
expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull();
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(wrapper.find('.js-job-empty-state').exists()).toBe(false);
}));
it('does not render empty state when job has trace but it is not running', done => {
setupAndMount({ jobData: { has_trace: true } })
.then(() => {
expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull();
})
.then(done)
.catch(done.fail);
});
it('does not render empty state when job has trace but it is not running', () =>
setupAndMount({ jobData: { has_trace: true } }).then(() => {
expect(wrapper.find('.js-job-empty-state').exists()).toBe(false);
}));
it('displays remaining time for a delayed job', done => {
it('displays remaining time for a delayed job', () => {
const oneHourInMilliseconds = 3600000;
spyOn(Date, 'now').and.callFake(
jest
.spyOn(Date, 'now')
.mockImplementation(
() => new Date(delayedJobFixture.scheduled_at).getTime() - oneHourInMilliseconds,
);
setupAndMount({ jobData: delayedJobFixture })
.then(() => {
expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull();
return setupAndMount({ jobData: delayedJobFixture }).then(() => {
expect(wrapper.find('.js-job-empty-state').exists()).toBe(true);
const title = vm.$el.querySelector('.js-job-empty-state-title');
const title = wrapper.find('.js-job-empty-state-title').text();
expect(title).toContainText('01:00:00');
})
.then(done)
.catch(done.fail);
expect(title).toEqual('This is a delayed job to run in 01:00:00');
});
});
});
......@@ -422,8 +368,11 @@ describe('Job App ', () => {
},
})
.then(() => {
vm.$el.querySelectorAll('.blocks-container > *').forEach(block => {
expect(block.textContent.trim()).not.toBe('');
const blocks = wrapper.findAll('.blocks-container > *').wrappers;
expect(blocks.length).toBeGreaterThan(0);
blocks.forEach(block => {
expect(block.text().trim()).not.toBe('');
});
})
.then(done)
......@@ -433,32 +382,24 @@ describe('Job App ', () => {
});
describe('archived job', () => {
beforeEach(done => {
setupAndMount({ jobData: { archived: true } })
.then(done)
.catch(done.fail);
});
beforeEach(() => setupAndMount({ jobData: { archived: true } }));
it('renders warning about job being archived', () => {
expect(vm.$el.querySelector('.js-archived-job ')).not.toBeNull();
expect(wrapper.find('.js-archived-job ').exists()).toBe(true);
});
});
describe('non-archived job', () => {
beforeEach(done => {
setupAndMount()
.then(done)
.catch(done.fail);
});
beforeEach(() => setupAndMount());
it('does not warning about job being archived', () => {
expect(vm.$el.querySelector('.js-archived-job ')).toBeNull();
expect(wrapper.find('.js-archived-job ').exists()).toBe(false);
});
});
describe('trace output', () => {
describe('with append flag', () => {
it('appends the log content to the existing one', done => {
it('appends the log content to the existing one', () =>
setupAndMount({
traceData: {
html: '<span>More<span>',
......@@ -469,20 +410,22 @@ describe('Job App ', () => {
},
})
.then(() => {
vm.$store.state.trace = 'Update';
store.state.trace = 'Update';
return vm.$nextTick();
return wrapper.vm.$nextTick();
})
.then(() => {
expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).toContain('Update');
})
.then(done)
.catch(done.fail);
});
expect(
wrapper
.find('.js-build-trace')
.text()
.trim(),
).toEqual('Update');
}));
});
describe('without append flag', () => {
it('replaces the trace', done => {
it('replaces the trace', () =>
setupAndMount({
traceData: {
html: '<span>Different<span>',
......@@ -490,24 +433,19 @@ describe('Job App ', () => {
append: false,
complete: true,
},
})
.then(() => {
expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).not.toContain(
'Update',
);
expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).toContain(
'Different',
);
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(
wrapper
.find('.js-build-trace')
.text()
.trim(),
).toEqual('Different');
}));
});
describe('truncated information', () => {
describe('when size is less than total', () => {
it('shows information about truncated log', done => {
it('shows information about truncated log', () => {
mock.onGet(`${props.pagePath}/trace.json`).reply(200, {
html: '<span>Update</span>',
status: 'success',
......@@ -517,7 +455,7 @@ describe('Job App ', () => {
complete: true,
});
setupAndMount({
return setupAndMount({
traceData: {
html: '<span>Update</span>',
status: 'success',
......@@ -526,19 +464,19 @@ describe('Job App ', () => {
total: 100,
complete: true,
},
})
.then(() => {
expect(vm.$el.querySelector('.js-truncated-info').textContent.trim()).toContain(
'50 bytes',
);
})
.then(done)
.catch(done.fail);
}).then(() => {
expect(
wrapper
.find('.js-truncated-info')
.text()
.trim(),
).toContain('Showing last 50 bytes');
});
});
});
describe('when size is equal than total', () => {
it('does not show the truncated information', done => {
it('does not show the truncated information', () =>
setupAndMount({
traceData: {
html: '<span>Update</span>',
......@@ -548,20 +486,19 @@ describe('Job App ', () => {
total: 100,
complete: true,
},
})
.then(() => {
expect(vm.$el.querySelector('.js-truncated-info').textContent.trim()).not.toContain(
'50 bytes',
);
})
.then(done)
.catch(done.fail);
});
}).then(() => {
expect(
wrapper
.find('.js-truncated-info')
.text()
.trim(),
).toEqual('');
}));
});
});
describe('trace controls', () => {
beforeEach(done => {
beforeEach(() =>
setupAndMount({
traceData: {
html: '<span>Update</span>',
......@@ -571,22 +508,20 @@ describe('Job App ', () => {
total: 100,
complete: true,
},
})
.then(done)
.catch(done.fail);
});
}),
);
it('should render scroll buttons', () => {
expect(vm.$el.querySelector('.js-scroll-top')).not.toBeNull();
expect(vm.$el.querySelector('.js-scroll-bottom')).not.toBeNull();
expect(wrapper.find('.js-scroll-top').exists()).toBe(true);
expect(wrapper.find('.js-scroll-bottom').exists()).toBe(true);
});
it('should render link to raw ouput', () => {
expect(vm.$el.querySelector('.js-raw-link-controller')).not.toBeNull();
expect(wrapper.find('.js-raw-link-controller').exists()).toBe(true);
});
it('should render link to erase job', () => {
expect(vm.$el.querySelector('.js-erase-link')).not.toBeNull();
expect(wrapper.find('.js-erase-link').exists()).toBe(true);
});
});
});
......
import { TEST_HOST } from 'spec/test_constants';
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
export const stages = [
{
name: 'build',
title: 'build: running',
groups: [
{
name: 'build:linux',
size: 1,
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
illustration: {
image: 'illustrations/pending_job_empty.svg',
size: 'svg-430',
title: 'This job has not started yet',
content: 'This job is in pending state and is waiting to be picked by a runner',
},
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
method: 'post',
},
},
jobs: [
{
id: 1180,
name: 'build:linux',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
playable: false,
created_at: '2018-09-28T11:09:57.229Z',
updated_at: '2018-09-28T11:09:57.503Z',
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
illustration: {
image: 'illustrations/pending_job_empty.svg',
size: 'svg-430',
title: 'This job has not started yet',
content: 'This job is in pending state and is waiting to be picked by a runner',
},
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
method: 'post',
},
},
},
],
},
{
name: 'build:osx',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
method: 'post',
},
},
jobs: [
{
id: 444,
name: 'build:osx',
started: '2018-05-18T05:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/444',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
playable: false,
created_at: '2018-05-18T15:32:54.364Z',
updated_at: '2018-05-18T15:32:54.364Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
method: 'post',
},
},
},
],
},
],
status: {
icon: 'status_running',
text: 'running',
label: 'running',
group: 'running',
tooltip: 'running',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
},
{
name: 'test',
title: 'test: passed with warnings',
groups: [
{
name: 'jenkins',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: null,
group: 'success',
tooltip: null,
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
jobs: [
{
id: 459,
name: 'jenkins',
started: '2018-05-18T09:32:20.658Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/459',
playable: false,
created_at: '2018-05-18T15:32:55.330Z',
updated_at: '2018-05-18T15:32:55.330Z',
status: {
icon: 'status_success',
text: 'passed',
label: null,
group: 'success',
tooltip: null,
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
},
],
},
{
name: 'rspec:linux',
size: 3,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
jobs: [
{
id: 445,
name: 'rspec:linux 0 3',
started: '2018-05-18T07:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/445',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/445/retry',
playable: false,
created_at: '2018-05-18T15:32:54.425Z',
updated_at: '2018-05-18T15:32:54.425Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/445',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/445/retry',
method: 'post',
},
},
},
{
id: 446,
name: 'rspec:linux 1 3',
started: '2018-05-18T07:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/446',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/446/retry',
playable: false,
created_at: '2018-05-18T15:32:54.506Z',
updated_at: '2018-05-18T15:32:54.506Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/446',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/446/retry',
method: 'post',
},
},
},
{
id: 447,
name: 'rspec:linux 2 3',
started: '2018-05-18T07:32:20.656Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/447',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/447/retry',
playable: false,
created_at: '2018-05-18T15:32:54.572Z',
updated_at: '2018-05-18T15:32:54.572Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/447',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/447/retry',
method: 'post',
},
},
},
],
},
{
name: 'rspec:osx',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/452',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
method: 'post',
},
},
jobs: [
{
id: 452,
name: 'rspec:osx',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/452',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
playable: false,
created_at: '2018-05-18T15:32:54.920Z',
updated_at: '2018-05-18T15:32:54.920Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/452',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
method: 'post',
},
},
},
],
},
{
name: 'rspec:windows',
size: 3,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
jobs: [
{
id: 448,
name: 'rspec:windows 0 3',
started: '2018-05-18T07:32:20.656Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/448',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/448/retry',
playable: false,
created_at: '2018-05-18T15:32:54.639Z',
updated_at: '2018-05-18T15:32:54.639Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/448',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/448/retry',
method: 'post',
},
},
},
{
id: 449,
name: 'rspec:windows 1 3',
started: '2018-05-18T07:32:20.656Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/449',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/449/retry',
playable: false,
created_at: '2018-05-18T15:32:54.703Z',
updated_at: '2018-05-18T15:32:54.703Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/449',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/449/retry',
method: 'post',
},
},
},
{
id: 451,
name: 'rspec:windows 2 3',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/451',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/451/retry',
playable: false,
created_at: '2018-05-18T15:32:54.853Z',
updated_at: '2018-05-18T15:32:54.853Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/451',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/451/retry',
method: 'post',
},
},
},
],
},
{
name: 'spinach:linux',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/453',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
method: 'post',
},
},
jobs: [
{
id: 453,
name: 'spinach:linux',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/453',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
playable: false,
created_at: '2018-05-18T15:32:54.993Z',
updated_at: '2018-05-18T15:32:54.993Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/453',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
method: 'post',
},
},
},
],
},
{
name: 'spinach:osx',
size: 1,
status: {
icon: 'status_warning',
text: 'failed',
label: 'failed (allowed to fail)',
group: 'failed-with-warnings',
tooltip: 'failed - (unknown failure) (allowed to fail)',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
method: 'post',
},
},
jobs: [
{
id: 454,
name: 'spinach:osx',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/454',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
playable: false,
created_at: '2018-05-18T15:32:55.053Z',
updated_at: '2018-05-18T15:32:55.053Z',
status: {
icon: 'status_warning',
text: 'failed',
label: 'failed (allowed to fail)',
group: 'failed-with-warnings',
tooltip: 'failed - (unknown failure) (allowed to fail)',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
method: 'post',
},
},
callout_message: 'There is an unknown failure, please try again',
recoverable: true,
},
],
},
],
status: {
icon: 'status_warning',
text: 'passed',
label: 'passed with warnings',
group: 'success-with-warnings',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#test',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#test',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=test',
},
{
name: 'deploy',
title: 'deploy: running',
groups: [
{
name: 'production',
size: 1,
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/457',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
method: 'post',
},
},
jobs: [
{
id: 457,
name: 'production',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/457',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
playable: false,
created_at: '2018-05-18T15:32:55.259Z',
updated_at: '2018-09-28T11:09:57.454Z',
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/457',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
method: 'post',
},
},
},
],
},
{
name: 'staging',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/455',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
method: 'post',
},
},
jobs: [
{
id: 455,
name: 'staging',
started: '2018-05-18T09:32:20.658Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/455',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
playable: false,
created_at: '2018-05-18T15:32:55.119Z',
updated_at: '2018-05-18T15:32:55.119Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/455',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
method: 'post',
},
},
},
],
},
{
name: 'stop staging',
size: 1,
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/456',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
method: 'post',
},
},
jobs: [
{
id: 456,
name: 'stop staging',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/456',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
playable: false,
created_at: '2018-05-18T15:32:55.205Z',
updated_at: '2018-09-28T11:09:57.396Z',
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/456',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
method: 'post',
},
},
},
],
},
],
status: {
icon: 'status_running',
text: 'running',
label: 'running',
group: 'running',
tooltip: 'running',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#deploy',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#deploy',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=deploy',
},
{
name: 'notify',
title: 'notify: manual action',
groups: [
{
name: 'slack',
size: 1,
status: {
icon: 'status_manual',
text: 'manual',
label: 'manual play action',
group: 'manual',
tooltip: 'manual action',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/458',
illustration: {
image: 'illustrations/manual_action.svg',
size: 'svg-394',
title: 'This job requires a manual action',
content:
'This job depends on a user to trigger its process. Often they are used to deploy code to production environments',
},
favicon:
'/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
action: {
icon: 'play',
title: 'Play',
path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
method: 'post',
},
},
jobs: [
{
id: 458,
name: 'slack',
started: null,
build_path: '/gitlab-org/gitlab-shell/-/jobs/458',
play_path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
playable: true,
created_at: '2018-05-18T15:32:55.303Z',
updated_at: '2018-05-18T15:34:08.535Z',
status: {
icon: 'status_manual',
text: 'manual',
label: 'manual play action',
group: 'manual',
tooltip: 'manual action',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/458',
illustration: {
image: 'illustrations/manual_action.svg',
size: 'svg-394',
title: 'This job requires a manual action',
content:
'This job depends on a user to trigger its process. Often they are used to deploy code to production environments',
},
favicon:
'/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
action: {
icon: 'play',
title: 'Play',
path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
method: 'post',
},
},
},
],
},
],
status: {
icon: 'status_manual',
text: 'manual',
label: 'manual action',
group: 'manual',
tooltip: 'manual action',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#notify',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#notify',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=notify',
},
];
export default {
id: 4757,
name: 'test',
build_path: '/root/ci-mock/-/jobs/4757',
retry_path: '/root/ci-mock/-/jobs/4757/retry',
cancel_path: '/root/ci-mock/-/jobs/4757/cancel',
new_issue_path: '/root/ci-mock/issues/new',
playable: false,
created_at: threeWeeksAgo.toISOString(),
updated_at: threeWeeksAgo.toISOString(),
finished_at: threeWeeksAgo.toISOString(),
queued: 9.54,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
has_details: true,
details_path: `${TEST_HOST}/root/ci-mock/-/jobs/4757`,
favicon:
'/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/-/jobs/4757/retry',
method: 'post',
},
},
coverage: 20,
erased_at: threeWeeksAgo.toISOString(),
erased: false,
duration: 6.785563,
tags: ['tag'],
user: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
erase_path: '/root/ci-mock/-/jobs/4757/erase',
artifacts: [null],
runner: {
id: 1,
description: 'local ci runner',
edit_path: '/root/ci-mock/runners/1/edit',
},
pipeline: {
id: 140,
user: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
active: false,
coverage: null,
source: 'unknown',
created_at: '2017-05-24T09:59:58.634Z',
updated_at: '2017-06-01T17:32:00.062Z',
path: '/root/ci-mock/pipelines/140',
flags: {
latest: true,
stuck: false,
yaml_errors: false,
retryable: false,
cancelable: false,
},
details: {
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
has_details: true,
details_path: '/root/ci-mock/pipelines/140',
favicon:
'/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
duration: 6,
finished_at: '2017-06-01T17:32:00.042Z',
stages: [
{
dropdown_path: '/jashkenas/underscore/pipelines/16/stage.json?stage=build',
name: 'build',
path: '/jashkenas/underscore/pipelines/16#build',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
},
title: 'build: passed',
},
{
dropdown_path: '/jashkenas/underscore/pipelines/16/stage.json?stage=test',
name: 'test',
path: '/jashkenas/underscore/pipelines/16#test',
status: {
icon: 'status_warning',
text: 'passed',
label: 'passed with warnings',
group: 'success-with-warnings',
},
title: 'test: passed with warnings',
},
],
},
ref: {
name: 'abc',
path: '/root/ci-mock/commits/abc',
tag: false,
branch: true,
},
commit: {
id: 'c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
short_id: 'c5864777',
title: 'Add new file',
created_at: '2017-05-24T10:59:52.000+01:00',
parent_ids: ['798e5f902592192afaba73f4668ae30e56eae492'],
message: 'Add new file',
author_name: 'Root',
author_email: 'admin@example.com',
authored_date: '2017-05-24T10:59:52.000+01:00',
committer_name: 'Root',
committer_email: 'admin@example.com',
committed_date: '2017-05-24T10:59:52.000+01:00',
author: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
author_gravatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url:
'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
},
},
metadata: {
timeout_human_readable: '1m 40s',
timeout_source: 'runner',
},
merge_request: {
iid: 2,
path: '/root/ci-mock/merge_requests/2',
},
raw_path: '/root/ci-mock/builds/4757/raw',
has_trace: true,
};
export const jobsInStage = {
name: 'build',
title: 'build: running',
latest_statuses: [
{
id: 1180,
name: 'build:linux',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
playable: false,
created_at: '2018-09-28T11:09:57.229Z',
updated_at: '2018-09-28T11:09:57.503Z',
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
illustration: {
image: 'illustrations/pending_job_empty.svg',
size: 'svg-430',
title: 'This job has not started yet',
content: 'This job is in pending state and is waiting to be picked by a runner',
},
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
method: 'post',
},
},
},
{
id: 444,
name: 'build:osx',
started: '2018-05-18T05:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/444',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
playable: false,
created_at: '2018-05-18T15:32:54.364Z',
updated_at: '2018-05-18T15:32:54.364Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
method: 'post',
},
},
},
],
retried: [
{
id: 443,
name: 'build:linux',
started: '2018-05-18T06:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/443',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/443/retry',
playable: false,
created_at: '2018-05-18T15:32:54.296Z',
updated_at: '2018-05-18T15:32:54.296Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed (retried)',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/443',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/443/retry',
method: 'post',
},
},
},
],
status: {
icon: 'status_running',
text: 'running',
label: 'running',
group: 'running',
tooltip: 'running',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
};
import { TEST_HOST } from 'spec/test_constants';
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
export const stages = [
{
name: 'build',
title: 'build: running',
groups: [
{
name: 'build:linux',
size: 1,
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
illustration: {
image: 'illustrations/pending_job_empty.svg',
size: 'svg-430',
title: 'This job has not started yet',
content: 'This job is in pending state and is waiting to be picked by a runner',
},
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
method: 'post',
},
},
jobs: [
{
id: 1180,
name: 'build:linux',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
playable: false,
created_at: '2018-09-28T11:09:57.229Z',
updated_at: '2018-09-28T11:09:57.503Z',
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
illustration: {
image: 'illustrations/pending_job_empty.svg',
size: 'svg-430',
title: 'This job has not started yet',
content: 'This job is in pending state and is waiting to be picked by a runner',
},
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
method: 'post',
},
},
},
],
},
{
name: 'build:osx',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
method: 'post',
},
},
jobs: [
{
id: 444,
name: 'build:osx',
started: '2018-05-18T05:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/444',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
playable: false,
created_at: '2018-05-18T15:32:54.364Z',
updated_at: '2018-05-18T15:32:54.364Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
method: 'post',
},
},
},
],
},
],
status: {
icon: 'status_running',
text: 'running',
label: 'running',
group: 'running',
tooltip: 'running',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
},
{
name: 'test',
title: 'test: passed with warnings',
groups: [
{
name: 'jenkins',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: null,
group: 'success',
tooltip: null,
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
jobs: [
{
id: 459,
name: 'jenkins',
started: '2018-05-18T09:32:20.658Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/459',
playable: false,
created_at: '2018-05-18T15:32:55.330Z',
updated_at: '2018-05-18T15:32:55.330Z',
status: {
icon: 'status_success',
text: 'passed',
label: null,
group: 'success',
tooltip: null,
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
},
],
},
{
name: 'rspec:linux',
size: 3,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
jobs: [
{
id: 445,
name: 'rspec:linux 0 3',
started: '2018-05-18T07:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/445',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/445/retry',
playable: false,
created_at: '2018-05-18T15:32:54.425Z',
updated_at: '2018-05-18T15:32:54.425Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/445',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/445/retry',
method: 'post',
},
},
},
{
id: 446,
name: 'rspec:linux 1 3',
started: '2018-05-18T07:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/446',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/446/retry',
playable: false,
created_at: '2018-05-18T15:32:54.506Z',
updated_at: '2018-05-18T15:32:54.506Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/446',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/446/retry',
method: 'post',
},
},
},
{
id: 447,
name: 'rspec:linux 2 3',
started: '2018-05-18T07:32:20.656Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/447',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/447/retry',
playable: false,
created_at: '2018-05-18T15:32:54.572Z',
updated_at: '2018-05-18T15:32:54.572Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/447',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/447/retry',
method: 'post',
},
},
},
],
},
{
name: 'rspec:osx',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/452',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
method: 'post',
},
},
jobs: [
{
id: 452,
name: 'rspec:osx',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/452',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
playable: false,
created_at: '2018-05-18T15:32:54.920Z',
updated_at: '2018-05-18T15:32:54.920Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/452',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/452/retry',
method: 'post',
},
},
},
],
},
{
name: 'rspec:windows',
size: 3,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: false,
details_path: null,
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
jobs: [
{
id: 448,
name: 'rspec:windows 0 3',
started: '2018-05-18T07:32:20.656Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/448',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/448/retry',
playable: false,
created_at: '2018-05-18T15:32:54.639Z',
updated_at: '2018-05-18T15:32:54.639Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/448',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/448/retry',
method: 'post',
},
},
},
{
id: 449,
name: 'rspec:windows 1 3',
started: '2018-05-18T07:32:20.656Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/449',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/449/retry',
playable: false,
created_at: '2018-05-18T15:32:54.703Z',
updated_at: '2018-05-18T15:32:54.703Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/449',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/449/retry',
method: 'post',
},
},
},
{
id: 451,
name: 'rspec:windows 2 3',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/451',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/451/retry',
playable: false,
created_at: '2018-05-18T15:32:54.853Z',
updated_at: '2018-05-18T15:32:54.853Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/451',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/451/retry',
method: 'post',
},
},
},
],
},
{
name: 'spinach:linux',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/453',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
method: 'post',
},
},
jobs: [
{
id: 453,
name: 'spinach:linux',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/453',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
playable: false,
created_at: '2018-05-18T15:32:54.993Z',
updated_at: '2018-05-18T15:32:54.993Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/453',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/453/retry',
method: 'post',
},
},
},
],
},
{
name: 'spinach:osx',
size: 1,
status: {
icon: 'status_warning',
text: 'failed',
label: 'failed (allowed to fail)',
group: 'failed-with-warnings',
tooltip: 'failed - (unknown failure) (allowed to fail)',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
method: 'post',
},
},
jobs: [
{
id: 454,
name: 'spinach:osx',
started: '2018-05-18T07:32:20.657Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/454',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
playable: false,
created_at: '2018-05-18T15:32:55.053Z',
updated_at: '2018-05-18T15:32:55.053Z',
status: {
icon: 'status_warning',
text: 'failed',
label: 'failed (allowed to fail)',
group: 'failed-with-warnings',
tooltip: 'failed - (unknown failure) (allowed to fail)',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
method: 'post',
},
},
callout_message: 'There is an unknown failure, please try again',
recoverable: true,
},
],
},
],
status: {
icon: 'status_warning',
text: 'passed',
label: 'passed with warnings',
group: 'success-with-warnings',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#test',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#test',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=test',
},
{
name: 'deploy',
title: 'deploy: running',
groups: [
{
name: 'production',
size: 1,
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/457',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
method: 'post',
},
},
jobs: [
{
id: 457,
name: 'production',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/457',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
playable: false,
created_at: '2018-05-18T15:32:55.259Z',
updated_at: '2018-09-28T11:09:57.454Z',
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/457',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel',
method: 'post',
},
},
},
],
},
{
name: 'staging',
size: 1,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/455',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
method: 'post',
},
},
jobs: [
{
id: 455,
name: 'staging',
started: '2018-05-18T09:32:20.658Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/455',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
playable: false,
created_at: '2018-05-18T15:32:55.119Z',
updated_at: '2018-05-18T15:32:55.119Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/455',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/455/retry',
method: 'post',
},
},
},
],
},
{
name: 'stop staging',
size: 1,
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/456',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
method: 'post',
},
},
jobs: [
{
id: 456,
name: 'stop staging',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/456',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
playable: false,
created_at: '2018-05-18T15:32:55.205Z',
updated_at: '2018-09-28T11:09:57.396Z',
status: {
icon: 'status_created',
text: 'created',
label: 'created',
group: 'created',
tooltip: 'created',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/456',
illustration: {
image: 'illustrations/job_not_triggered.svg',
size: 'svg-306',
title: 'This job has not been triggered yet',
content:
'This job depends on upstream jobs that need to succeed in order for this job to be triggered',
},
favicon:
'/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel',
method: 'post',
},
},
},
],
},
],
status: {
icon: 'status_running',
text: 'running',
label: 'running',
group: 'running',
tooltip: 'running',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#deploy',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#deploy',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=deploy',
},
{
name: 'notify',
title: 'notify: manual action',
groups: [
{
name: 'slack',
size: 1,
status: {
icon: 'status_manual',
text: 'manual',
label: 'manual play action',
group: 'manual',
tooltip: 'manual action',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/458',
illustration: {
image: 'illustrations/manual_action.svg',
size: 'svg-394',
title: 'This job requires a manual action',
content:
'This job depends on a user to trigger its process. Often they are used to deploy code to production environments',
},
favicon:
'/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
action: {
icon: 'play',
title: 'Play',
path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
method: 'post',
},
},
jobs: [
{
id: 458,
name: 'slack',
started: null,
build_path: '/gitlab-org/gitlab-shell/-/jobs/458',
play_path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
playable: true,
created_at: '2018-05-18T15:32:55.303Z',
updated_at: '2018-05-18T15:34:08.535Z',
status: {
icon: 'status_manual',
text: 'manual',
label: 'manual play action',
group: 'manual',
tooltip: 'manual action',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/458',
illustration: {
image: 'illustrations/manual_action.svg',
size: 'svg-394',
title: 'This job requires a manual action',
content:
'This job depends on a user to trigger its process. Often they are used to deploy code to production environments',
},
favicon:
'/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
action: {
icon: 'play',
title: 'Play',
path: '/gitlab-org/gitlab-shell/-/jobs/458/play',
method: 'post',
},
},
},
],
},
],
status: {
icon: 'status_manual',
text: 'manual',
label: 'manual action',
group: 'manual',
tooltip: 'manual action',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#notify',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#notify',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=notify',
},
];
export default {
id: 4757,
name: 'test',
build_path: '/root/ci-mock/-/jobs/4757',
retry_path: '/root/ci-mock/-/jobs/4757/retry',
cancel_path: '/root/ci-mock/-/jobs/4757/cancel',
new_issue_path: '/root/ci-mock/issues/new',
playable: false,
created_at: threeWeeksAgo.toISOString(),
updated_at: threeWeeksAgo.toISOString(),
finished_at: threeWeeksAgo.toISOString(),
queued: 9.54,
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
has_details: true,
details_path: `${TEST_HOST}/root/ci-mock/-/jobs/4757`,
favicon:
'/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/-/jobs/4757/retry',
method: 'post',
},
},
coverage: 20,
erased_at: threeWeeksAgo.toISOString(),
erased: false,
duration: 6.785563,
tags: ['tag'],
user: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
erase_path: '/root/ci-mock/-/jobs/4757/erase',
artifacts: [null],
runner: {
id: 1,
description: 'local ci runner',
edit_path: '/root/ci-mock/runners/1/edit',
},
pipeline: {
id: 140,
user: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
active: false,
coverage: null,
source: 'unknown',
created_at: '2017-05-24T09:59:58.634Z',
updated_at: '2017-06-01T17:32:00.062Z',
path: '/root/ci-mock/pipelines/140',
flags: {
latest: true,
stuck: false,
yaml_errors: false,
retryable: false,
cancelable: false,
},
details: {
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
has_details: true,
details_path: '/root/ci-mock/pipelines/140',
favicon:
'/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
duration: 6,
finished_at: '2017-06-01T17:32:00.042Z',
stages: [
{
dropdown_path: '/jashkenas/underscore/pipelines/16/stage.json?stage=build',
name: 'build',
path: '/jashkenas/underscore/pipelines/16#build',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
},
title: 'build: passed',
},
{
dropdown_path: '/jashkenas/underscore/pipelines/16/stage.json?stage=test',
name: 'test',
path: '/jashkenas/underscore/pipelines/16#test',
status: {
icon: 'status_warning',
text: 'passed',
label: 'passed with warnings',
group: 'success-with-warnings',
},
title: 'test: passed with warnings',
},
],
},
ref: {
name: 'abc',
path: '/root/ci-mock/commits/abc',
tag: false,
branch: true,
},
commit: {
id: 'c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
short_id: 'c5864777',
title: 'Add new file',
created_at: '2017-05-24T10:59:52.000+01:00',
parent_ids: ['798e5f902592192afaba73f4668ae30e56eae492'],
message: 'Add new file',
author_name: 'Root',
author_email: 'admin@example.com',
authored_date: '2017-05-24T10:59:52.000+01:00',
committer_name: 'Root',
committer_email: 'admin@example.com',
committed_date: '2017-05-24T10:59:52.000+01:00',
author: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
author_gravatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url:
'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
},
},
metadata: {
timeout_human_readable: '1m 40s',
timeout_source: 'runner',
},
merge_request: {
iid: 2,
path: '/root/ci-mock/merge_requests/2',
},
raw_path: '/root/ci-mock/builds/4757/raw',
has_trace: true,
};
export const jobsInStage = {
name: 'build',
title: 'build: running',
latest_statuses: [
{
id: 1180,
name: 'build:linux',
started: false,
build_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
playable: false,
created_at: '2018-09-28T11:09:57.229Z',
updated_at: '2018-09-28T11:09:57.503Z',
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/1180',
illustration: {
image: 'illustrations/pending_job_empty.svg',
size: 'svg-430',
title: 'This job has not started yet',
content: 'This job is in pending state and is waiting to be picked by a runner',
},
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
action: {
icon: 'cancel',
title: 'Cancel',
path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel',
method: 'post',
},
},
},
{
id: 444,
name: 'build:osx',
started: '2018-05-18T05:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/444',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
playable: false,
created_at: '2018-05-18T15:32:54.364Z',
updated_at: '2018-05-18T15:32:54.364Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/444',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/444/retry',
method: 'post',
},
},
},
],
retried: [
{
id: 443,
name: 'build:linux',
started: '2018-05-18T06:32:20.655Z',
build_path: '/gitlab-org/gitlab-shell/-/jobs/443',
retry_path: '/gitlab-org/gitlab-shell/-/jobs/443/retry',
playable: false,
created_at: '2018-05-18T15:32:54.296Z',
updated_at: '2018-05-18T15:32:54.296Z',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed (retried)',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/-/jobs/443',
illustration: {
image: 'illustrations/skipped-job_empty.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/gitlab-org/gitlab-shell/-/jobs/443/retry',
method: 'post',
},
},
},
],
status: {
icon: 'status_running',
text: 'running',
label: 'running',
group: 'running',
tooltip: 'running',
has_details: true,
details_path: '/gitlab-org/gitlab-shell/pipelines/27#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
},
path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
};
export { default } from '../../frontend/jobs/mock_data';
export * from '../../frontend/jobs/mock_data';
......@@ -2,10 +2,10 @@
require 'spec_helper'
describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode, :request_store do
include_context 'custom session'
let(:user) { build(:user) }
let(:user) { build_stubbed(:user) }
subject { described_class.new(user) }
......@@ -13,8 +13,7 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session])
end
describe '#admin_mode?', :request_store do
context 'when the user is a regular user' do
shared_examples 'admin mode cannot be enabled' do
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
......@@ -59,8 +58,21 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
end
describe '#admin_mode?' do
context 'when the user is a regular user' do
it_behaves_like 'admin mode cannot be enabled'
context 'bypassing session' do
it_behaves_like 'admin mode cannot be enabled' do
around do |example|
described_class.bypass_session!(user.id) { example.run }
end
end
end
end
context 'when the user is an admin' do
let(:user) { build(:user, :admin) }
let(:user) { build_stubbed(:user, :admin) }
context 'when admin mode not requested' do
it 'is false by default' do
......@@ -148,11 +160,36 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
end
end
context 'bypassing session' do
it 'is active by default' do
described_class.bypass_session!(user.id) do
expect(subject.admin_mode?).to be(true)
end
end
it 'enable has no effect' do
described_class.bypass_session!(user.id) do
subject.request_admin_mode!
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true)
end
end
it 'disable has no effect' do
described_class.bypass_session!(user.id) do
subject.disable_admin_mode!
expect(subject.admin_mode?).to be(true)
end
end
end
end
end
describe '#enable_admin_mode!' do
let(:user) { build(:user, :admin) }
let(:user) { build_stubbed(:user, :admin) }
it 'creates a timestamp in the session' do
subject.request_admin_mode!
......@@ -163,7 +200,7 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
describe '#enable_sessionless_admin_mode!' do
let(:user) { build(:user, :admin) }
let(:user) { build_stubbed(:user, :admin) }
it 'enabled admin mode without password' do
subject.enable_sessionless_admin_mode!
......@@ -173,7 +210,7 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
describe '#disable_admin_mode!' do
let(:user) { build(:user, :admin) }
let(:user) { build_stubbed(:user, :admin) }
it 'sets the session timestamp to nil' do
subject.request_admin_mode!
......@@ -183,6 +220,73 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
end
describe '.bypass_session!' do
context 'with a regular user' do
it 'admin mode is false' do
described_class.bypass_session!(user.id) do
expect(subject.admin_mode?).to be(false)
expect(described_class.bypass_session_admin_id).to be(user.id)
end
expect(described_class.bypass_session_admin_id).to be_nil
end
end
context 'with an admin user' do
let(:user) { build_stubbed(:user, :admin) }
it 'admin mode is true' do
described_class.bypass_session!(user.id) do
expect(subject.admin_mode?).to be(true)
expect(described_class.bypass_session_admin_id).to be(user.id)
end
expect(described_class.bypass_session_admin_id).to be_nil
end
end
end
describe '.with_current_request_admin_mode' do
context 'with a regular user' do
it 'user is not available inside nor outside the yielded block' do
described_class.with_current_admin(user) do
expect(described_class.current_admin).to be_nil
end
expect(described_class.bypass_session_admin_id).to be_nil
end
end
context 'with an admin user' do
let(:user) { build_stubbed(:user, :admin) }
context 'admin mode is disabled' do
it 'user is not available inside nor outside the yielded block' do
described_class.with_current_admin(user) do
expect(described_class.current_admin).to be_nil
end
expect(described_class.bypass_session_admin_id).to be_nil
end
end
context 'admin mode is enabled' do
before do
subject.request_admin_mode!
subject.enable_admin_mode!(password: user.password)
end
it 'user is available only inside the yielded block' do
described_class.with_current_admin(user) do
expect(described_class.current_admin).to be(user)
end
expect(described_class.current_admin).to be_nil
end
end
end
end
def expected_session_entry(value_matcher)
{
Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including(
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::SidekiqMiddleware::AdminMode::Client, :do_not_mock_admin_mode, :request_store do
include AdminModeHelper
let(:worker) do
Class.new do
def perform; end
end
end
let(:job) { {} }
let(:queue) { :test }
it 'yields block' do
expect do |b|
subject.call(worker, job, queue, nil, &b)
end.to yield_control.once
end
context 'user is a regular user' do
it 'no admin mode field in payload' do
subject.call(worker, job, queue, nil) { nil }
expect(job).not_to include('admin_mode_user_id')
end
end
context 'user is an administrator' do
let(:admin) { create(:admin) }
context 'admin mode disabled' do
it 'no admin mode field in payload' do
subject.call(worker, job, queue, nil) { nil }
expect(job).not_to include('admin_mode_user_id')
end
end
context 'admin mode enabled' do
before do
enable_admin_mode!(admin)
end
context 'when sidekiq required context not set' do
it 'no admin mode field in payload' do
subject.call(worker, job, queue, nil) { nil }
expect(job).not_to include('admin_mode_user_id')
end
end
context 'when user stored in current request' do
it 'has admin mode field in payload' do
Gitlab::Auth::CurrentUserMode.with_current_admin(admin) do
subject.call(worker, job, queue, nil) { nil }
expect(job).to include('admin_mode_user_id' => admin.id)
end
end
end
context 'when bypassing session' do
it 'has admin mode field in payload' do
Gitlab::Auth::CurrentUserMode.bypass_session!(admin.id) do
subject.call(worker, job, queue, nil) { nil }
expect(job).to include('admin_mode_user_id' => admin.id)
end
end
end
end
end
context 'admin mode feature disabled' do
before do
stub_feature_flags(user_mode_in_session: false)
end
it 'yields block' do
expect do |b|
subject.call(worker, job, queue, nil, &b)
end.to yield_control.once
end
it 'no admin mode field in payload' do
subject.call(worker, job, queue, nil) { nil }
expect(job).not_to include('admin_mode_user_id')
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::SidekiqMiddleware::AdminMode::Server, :do_not_mock_admin_mode, :request_store do
include AdminModeHelper
let(:worker) do
Class.new do
def perform; end
end
end
let(:job) { {} }
let(:queue) { :test }
it 'yields block' do
expect do |b|
subject.call(worker, job, queue, &b)
end.to yield_control.once
end
context 'job has no admin mode field' do
it 'session is not bypassed' do
subject.call(worker, job, queue) do
expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil
end
end
end
context 'job has admin mode field' do
let(:admin) { create(:admin) }
context 'nil admin mode id' do
let(:job) { { 'admin_mode_user_id' => nil } }
it 'session is not bypassed' do
subject.call(worker, job, queue) do
expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil
end
end
end
context 'valid admin mode id' do
let(:job) { { 'admin_mode_user_id' => admin.id } }
it 'session is bypassed' do
subject.call(worker, job, queue) do
expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be(admin.id)
end
end
end
end
context 'admin mode feature disabled' do
before do
stub_feature_flags(user_mode_in_session: false)
end
it 'yields block' do
expect do |b|
subject.call(worker, job, queue, &b)
end.to yield_control.once
end
it 'session is not bypassed' do
subject.call(worker, job, queue) do
expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil
end
end
end
end
......@@ -45,7 +45,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqMiddleware::ArgumentsLogger,
Gitlab::SidekiqMiddleware::MemoryKiller,
Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
Gitlab::SidekiqMiddleware::WorkerContext::Server
Gitlab::SidekiqMiddleware::WorkerContext::Server,
Gitlab::SidekiqMiddleware::AdminMode::Server
]
end
let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
......@@ -115,7 +116,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqStatus::ClientMiddleware,
Gitlab::SidekiqMiddleware::ClientMetrics,
Gitlab::SidekiqMiddleware::WorkerContext::Client,
Labkit::Middleware::Sidekiq::Client
Labkit::Middleware::Sidekiq::Client,
Gitlab::SidekiqMiddleware::AdminMode::Client
]
end
......
......@@ -2985,9 +2985,9 @@ describe User, :do_not_mock_admin_mode do
end
end
describe '#can_read_all_resources?' do
describe '#can_read_all_resources?', :request_store do
it 'returns false for regular user' do
user = build(:user)
user = build_stubbed(:user)
expect(user.can_read_all_resources?).to be_falsy
end
......@@ -2995,7 +2995,7 @@ describe User, :do_not_mock_admin_mode do
context 'for admin user' do
include_context 'custom session'
let(:user) { build(:user, :admin) }
let(:user) { build_stubbed(:user, :admin) }
context 'when admin mode is disabled' do
it 'returns false' do
......
......@@ -23,8 +23,8 @@ describe BasePolicy, :do_not_mock_admin_mode do
end
describe 'read cross project' do
let(:current_user) { create(:user) }
let(:user) { create(:user) }
let(:current_user) { build_stubbed(:user) }
let(:user) { build_stubbed(:user) }
subject { described_class.new(current_user, [user]) }
......@@ -38,7 +38,7 @@ describe BasePolicy, :do_not_mock_admin_mode do
it { is_expected.not_to be_allowed(:read_cross_project) }
context 'for admins' do
let(:current_user) { build(:admin) }
let(:current_user) { build_stubbed(:admin) }
subject { described_class.new(current_user, nil) }
......@@ -56,14 +56,14 @@ describe BasePolicy, :do_not_mock_admin_mode do
end
describe 'full private access' do
let(:current_user) { create(:user) }
let(:current_user) { build_stubbed(:user) }
subject { described_class.new(current_user, nil) }
it { is_expected.not_to be_allowed(:read_all_resources) }
context 'for admins' do
let(:current_user) { build(:admin) }
let(:current_user) { build_stubbed(:admin) }
it 'allowed when in admin mode' do
enable_admin_mode!(current_user)
......
......@@ -3,11 +3,129 @@
require 'spec_helper'
describe API::ErrorTracking do
describe "GET /projects/:id/error_tracking/settings" do
let(:user) { create(:user) }
let(:setting) { create(:project_error_tracking_setting) }
let(:project) { setting.project }
shared_examples 'returns project settings' do
it 'returns correct project settings' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(
'active' => setting.reload.enabled,
'project_name' => setting.project_name,
'sentry_external_url' => setting.sentry_external_url,
'api_url' => setting.api_url
)
end
end
shared_examples 'returns 404' do
it 'returns correct project settings' do
subject
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message'])
.to eq('404 Error Tracking Setting Not Found')
end
end
describe "PATCH /projects/:id/error_tracking/settings" do
def make_patch_request(**params)
patch api("/projects/#{project.id}/error_tracking/settings", user), params: params
end
context 'when authenticated as maintainer' do
before do
project.add_maintainer(user)
end
context 'patch settings' do
subject do
make_patch_request(active: false)
end
it_behaves_like 'returns project settings'
it 'returns active is invalid if non boolean' do
make_patch_request(active: "randomstring")
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error'])
.to eq('active is invalid')
end
it 'returns 400 if active is empty' do
make_patch_request(active: '')
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error'])
.to eq('active is empty')
end
end
context 'without a project setting' do
let(:project) { create(:project) }
before do
project.add_maintainer(user)
end
context 'patch settings' do
subject do
make_patch_request(active: true)
end
it_behaves_like 'returns 404'
end
end
end
context 'when authenticated as reporter' do
before do
project.add_reporter(user)
end
it 'returns 403 for update request' do
make_patch_request(active: true)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when authenticated as developer' do
before do
project.add_developer(user)
end
it 'returns 403 for update request' do
make_patch_request(active: true)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when authenticated as non-member' do
it 'returns 404 for update request' do
make_patch_request(active: false)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when unauthenticated' do
let(:user) { nil }
it 'returns 401 for update request' do
make_patch_request(active: true)
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
describe "GET /projects/:id/error_tracking/settings" do
def make_request
get api("/projects/#{project.id}/error_tracking/settings", user)
end
......@@ -17,16 +135,12 @@ describe API::ErrorTracking do
project.add_maintainer(user)
end
it 'returns project settings' do
context 'get settings' do
subject do
make_request
end
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(
'active' => setting.enabled,
'project_name' => setting.project_name,
'sentry_external_url' => setting.sentry_external_url,
'api_url' => setting.api_url
)
it_behaves_like 'returns project settings'
end
end
......@@ -37,12 +151,12 @@ describe API::ErrorTracking do
project.add_maintainer(user)
end
it 'returns 404' do
context 'get settings' do
subject do
make_request
end
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message'])
.to eq('404 Error Tracking Setting Not Found')
it_behaves_like 'returns 404'
end
end
......@@ -58,6 +172,18 @@ describe API::ErrorTracking do
end
end
context 'when authenticated as developer' do
before do
project.add_developer(user)
end
it 'returns 403' do
make_request
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when authenticated as non-member' do
it 'returns 404' do
make_request
......
......@@ -778,6 +778,32 @@ describe API::Issues do
expect(json_response["error"]).to include("mutually exclusive")
end
end
context 'filtering by non_archived' do
let_it_be(:group1) { create(:group) }
let_it_be(:archived_project) { create(:project, :archived, namespace: group1) }
let_it_be(:active_project) { create(:project, namespace: group1) }
let_it_be(:issue1) { create(:issue, project: active_project) }
let_it_be(:issue2) { create(:issue, project: active_project) }
let_it_be(:issue3) { create(:issue, project: archived_project) }
before do
archived_project.add_developer(user)
active_project.add_developer(user)
end
it 'returns issues from non archived projects only by default' do
get api("/groups/#{group1.id}/issues", user), params: { scope: 'all' }
expect_response_contain_exactly(issue2, issue1)
end
it 'returns issues from archived and non archived projects when non_archived is false' do
get api("/groups/#{group1.id}/issues", user), params: { non_archived: false, scope: 'all' }
expect_response_contain_exactly(issue1, issue2, issue3)
end
end
end
context "when returns issue merge_requests_count for different access levels" do
......@@ -862,4 +888,9 @@ describe API::Issues do
include_examples 'time tracking endpoints', 'issue'
end
def expect_response_contain_exactly(*items)
expect(json_response.length).to eq(items.size)
expect(json_response.map { |element| element['id'] }).to contain_exactly(*items.map(&:id))
end
end
......@@ -807,6 +807,38 @@ describe API::MergeRequests do
end
end
end
context 'with archived projects' do
let(:project2) { create(:project, :public, :archived, namespace: group) }
let!(:merge_request_archived) { create(:merge_request, title: 'archived mr', author: user, source_project: project2, target_project: project2) }
it 'returns an array excluding merge_requests from archived projects' do
get api(endpoint_path, user)
expect_response_contain_exactly(
merge_request_merged,
merge_request_locked,
merge_request_closed,
merge_request
)
end
context 'with non_archived param set as false' do
it 'returns an array including merge_requests from archived projects' do
path = endpoint_path + '?non_archived=false'
get api(path, user)
expect_response_contain_exactly(
merge_request_merged,
merge_request_locked,
merge_request_closed,
merge_request,
merge_request_archived
)
end
end
end
end
describe "GET /projects/:id/merge_requests/:merge_request_iid" do
......
......@@ -4,7 +4,7 @@ require 'spec_helper'
describe Projects::Operations::UpdateService do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:project, refind: true) { create(:project) }
let(:result) { subject.execute }
......@@ -145,6 +145,48 @@ describe Projects::Operations::UpdateService do
end
end
context 'partial_update' do
let(:params) do
{
error_tracking_setting_attributes: {
enabled: true
}
}
end
context 'with setting' do
before do
create(:project_error_tracking_setting, :disabled, project: project)
end
it 'service succeeds' do
expect(result[:status]).to eq(:success)
end
it 'updates attributes' do
expect { result }
.to change { project.reload.error_tracking_setting.enabled }
.from(false)
.to(true)
end
it 'only updates enabled attribute' do
result
expect(project.error_tracking_setting.previous_changes.keys)
.to contain_exactly('enabled')
end
end
context 'without setting' do
it 'does not create a setting' do
expect(result[:status]).to eq(:error)
expect(project.reload.error_tracking_setting).to be_nil
end
end
end
context 'with masked param token' do
let(:params) do
{
......
# frozen_string_literal: true
module ControllerHelpers
# It seems Devise::Test::ControllerHelpers#sign_in doesn't clear out the @current_user
# variable of the controller, hence it's not overwritten on retries.
# This should be fixed in Devise:
# - https://github.com/heartcombo/devise/issues/5190
# - https://github.com/heartcombo/devise/pull/5191
def sign_in(resource, deprecated = nil, scope: nil)
super
scope ||= Devise::Mapping.find_scope!(resource)
@controller.instance_variable_set(:"@current_#{scope}", nil)
end
end
Devise::Test::ControllerHelpers.prepend(ControllerHelpers)
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