Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ad1e4b8fb8
commit
f1bb2a307e
27 changed files with 306 additions and 198 deletions
|
@ -1 +1 @@
|
|||
1.67.0
|
||||
1.70.0
|
||||
|
|
|
@ -58,6 +58,7 @@ export default {
|
|||
<template>
|
||||
<div class="ide-stage card prepend-top-default">
|
||||
<div
|
||||
ref="cardHeader"
|
||||
:class="{
|
||||
'border-bottom-0': stage.isCollapsed,
|
||||
}"
|
||||
|
@ -79,7 +80,7 @@ export default {
|
|||
</div>
|
||||
<icon :name="collapseIcon" class="ide-stage-collapse-icon" />
|
||||
</div>
|
||||
<div v-show="!stage.isCollapsed" class="card-body">
|
||||
<div v-show="!stage.isCollapsed" ref="jobList" class="card-body">
|
||||
<gl-loading-icon v-if="showLoadingIcon" />
|
||||
<template v-else>
|
||||
<item v-for="job in stage.jobs" :key="job.id" :job="job" @clickViewLog="clickViewLog" />
|
||||
|
|
|
@ -95,10 +95,10 @@ export class SearchAutocomplete {
|
|||
this.createAutocomplete();
|
||||
}
|
||||
|
||||
this.searchInput.addClass('disabled');
|
||||
this.saveTextLength();
|
||||
this.bindEvents();
|
||||
this.dropdownToggle.dropdown();
|
||||
this.searchInput.addClass('js-autocomplete-disabled');
|
||||
}
|
||||
|
||||
// Finds an element inside wrapper element
|
||||
|
@ -338,7 +338,7 @@ export class SearchAutocomplete {
|
|||
if (!this.dropdown.hasClass('show')) {
|
||||
this.loadingSuggestions = false;
|
||||
this.dropdownToggle.dropdown('toggle');
|
||||
return this.searchInput.removeClass('disabled');
|
||||
return this.searchInput.removeClass('js-autocomplete-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,8 +432,8 @@ export class SearchAutocomplete {
|
|||
}
|
||||
|
||||
disableAutocomplete() {
|
||||
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('show')) {
|
||||
this.searchInput.addClass('disabled');
|
||||
if (!this.searchInput.hasClass('js-autocomplete-disabled') && this.dropdown.hasClass('show')) {
|
||||
this.searchInput.addClass('js-autocomplete-disabled');
|
||||
this.dropdown.removeClass('show').trigger('hidden.bs.dropdown');
|
||||
this.restoreMenu();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RedirectsForMissingPathOnTree
|
||||
def redirect_to_tree_root_for_missing_path(project, ref, path)
|
||||
redirect_to project_tree_path(project, ref), notice: missing_path_on_ref(path, ref)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def missing_path_on_ref(path, ref)
|
||||
_('"%{path}" did not exist on "%{ref}"') % { path: truncate_path(path), ref: ref }
|
||||
end
|
||||
|
||||
def truncate_path(path)
|
||||
path.reverse.truncate(60, separator: "/").reverse
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@
|
|||
# Controller for viewing a file's blame
|
||||
class Projects::BlameController < Projects::ApplicationController
|
||||
include ExtractsPath
|
||||
include RedirectsForMissingPathOnTree
|
||||
|
||||
before_action :require_non_empty_project
|
||||
before_action :assign_ref_vars
|
||||
|
@ -11,7 +12,9 @@ class Projects::BlameController < Projects::ApplicationController
|
|||
def show
|
||||
@blob = @repository.blob_at(@commit.id, @path)
|
||||
|
||||
return render_404 unless @blob
|
||||
unless @blob
|
||||
return redirect_to_tree_root_for_missing_path(@project, @ref, @path)
|
||||
end
|
||||
|
||||
environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
|
||||
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
|
||||
|
|
|
@ -7,6 +7,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
include RendersBlob
|
||||
include NotesHelper
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
include RedirectsForMissingPathOnTree
|
||||
prepend_before_action :authenticate_user!, only: [:edit]
|
||||
|
||||
around_action :allow_gitaly_ref_name_caching, only: [:show]
|
||||
|
@ -119,7 +120,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
return render_404
|
||||
return redirect_to_tree_root_for_missing_path(@project, @ref, @path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ class Projects::JobsController < Projects::ApplicationController
|
|||
before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize]
|
||||
before_action :verify_api_request!, only: :terminal_websocket_authorize
|
||||
before_action only: [:show] do
|
||||
push_frontend_feature_flag(:job_log_json)
|
||||
push_frontend_feature_flag(:job_log_json, project)
|
||||
end
|
||||
|
||||
layout 'project'
|
||||
|
|
|
@ -5,6 +5,7 @@ class Projects::TreeController < Projects::ApplicationController
|
|||
include ExtractsPath
|
||||
include CreatesCommit
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
include RedirectsForMissingPathOnTree
|
||||
|
||||
around_action :allow_gitaly_ref_name_caching, only: [:show]
|
||||
|
||||
|
@ -19,12 +20,9 @@ class Projects::TreeController < Projects::ApplicationController
|
|||
|
||||
if tree.entries.empty?
|
||||
if @repository.blob_at(@commit.id, @path)
|
||||
return redirect_to(
|
||||
project_blob_path(@project,
|
||||
File.join(@ref, @path))
|
||||
)
|
||||
return redirect_to project_blob_path(@project, File.join(@ref, @path))
|
||||
elsif @path.present?
|
||||
return render_404
|
||||
return redirect_to_tree_root_for_missing_path(@project, @ref, @path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: When a user views a file's blame or blob and switches to a branch where the
|
||||
current file does not exist, they will now be redirected to the root of the repository.
|
||||
merge_request: 18169
|
||||
author: Jesse Hall @jessehall3
|
||||
type: changed
|
5
changelogs/unreleased/pokstad1-gitaly-1-70-0.yml
Normal file
5
changelogs/unreleased/pokstad1-gitaly-1-70-0.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Bump Gitaly to 1.70.0 and remove cache invalidation feature flag
|
||||
merge_request: 18766
|
||||
author:
|
||||
type: other
|
5
changelogs/unreleased/sh-use-template-project-id.yml
Normal file
5
changelogs/unreleased/sh-use-template-project-id.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix incorrect selection of custom templates
|
||||
merge_request: 17205
|
||||
author:
|
||||
type: fixed
|
|
@ -3,71 +3,17 @@
|
|||
class SetSelfMonitoringProjectAlertingToken < ActiveRecord::Migration[5.2]
|
||||
DOWNTIME = false
|
||||
|
||||
module Migratable
|
||||
module Alerting
|
||||
class ProjectAlertingSetting < ApplicationRecord
|
||||
self.table_name = 'project_alerting_settings'
|
||||
|
||||
belongs_to :project
|
||||
|
||||
validates :token, presence: true
|
||||
|
||||
attr_encrypted :token,
|
||||
mode: :per_attribute_iv,
|
||||
key: Settings.attr_encrypted_db_key_base_truncated,
|
||||
algorithm: 'aes-256-gcm'
|
||||
|
||||
before_validation :ensure_token
|
||||
|
||||
private
|
||||
|
||||
def ensure_token
|
||||
self.token ||= generate_token
|
||||
end
|
||||
|
||||
def generate_token
|
||||
SecureRandom.hex
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Project < ApplicationRecord
|
||||
has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting'
|
||||
end
|
||||
|
||||
class ApplicationSetting < ApplicationRecord
|
||||
self.table_name = 'application_settings'
|
||||
|
||||
belongs_to :instance_administration_project, class_name: 'Project'
|
||||
|
||||
def self.current_without_cache
|
||||
last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup_alertmanager_token(project)
|
||||
return unless License.feature_available?(:prometheus_alerts)
|
||||
|
||||
project.create_alerting_setting!
|
||||
end
|
||||
|
||||
def up
|
||||
Gitlab.ee do
|
||||
project = Migratable::ApplicationSetting.current_without_cache&.instance_administration_project
|
||||
# no-op
|
||||
# Converted to no-op in https://gitlab.com/gitlab-org/gitlab/merge_requests/17049.
|
||||
|
||||
if project
|
||||
setup_alertmanager_token(project)
|
||||
end
|
||||
end
|
||||
# This migration has been made a no-op because the pre-requisite migration
|
||||
# which creates the self-monitoring project has already been removed in
|
||||
# https://gitlab.com/gitlab-org/gitlab/merge_requests/16864. As
|
||||
# such, this migration would do nothing.
|
||||
end
|
||||
|
||||
def down
|
||||
Gitlab.ee do
|
||||
Migratable::ApplicationSetting.current_without_cache
|
||||
&.instance_administration_project
|
||||
&.alerting_setting
|
||||
&.destroy!
|
||||
end
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
|
@ -83,6 +83,7 @@ This was originally implemented in: <https://gitlab.com/gitlab-org/gitlab-foss/m
|
|||
- In JS tests, shifting elements can cause Capybara to misclick when the element moves at the exact time Capybara sends the click
|
||||
- [Dropdowns rendering upward or downward due to window size and scroll position](https://gitlab.com/gitlab-org/gitlab/merge_requests/17660)
|
||||
- [Lazy loaded images can cause Capybara to misclick](https://gitlab.com/gitlab-org/gitlab/merge_requests/18713)
|
||||
- [Triggering JS events before the event handlers are set up](https://gitlab.com/gitlab-org/gitlab/merge_requests/18742)
|
||||
|
||||
#### Capybara viewport size related issues
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generic alerts integration **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/13203) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.3.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/13203) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.4.
|
||||
|
||||
GitLab can accept alerts from any source via a generic webhook receiver.
|
||||
When you set up the generic alerts integration, a unique endpoint will
|
||||
|
@ -16,7 +16,7 @@ authored by the GitLab Alert Bot.
|
|||
To set up the generic alerts integration:
|
||||
|
||||
1. Navigate to **Settings > Integrations** in a project.
|
||||
1. Click on **Alert endpoint**.
|
||||
1. Click on **Alerts endpoint**.
|
||||
1. Toggle the **Active** alert setting. The `URL` and `Authorization Key` for the webhook configuration can be found there.
|
||||
|
||||
## Customizing the payload
|
||||
|
|
|
@ -7,7 +7,6 @@ class Feature
|
|||
# Server feature flags should use '_' to separate words.
|
||||
SERVER_FEATURE_FLAGS =
|
||||
%w[
|
||||
cache_invalidator
|
||||
inforef_uploadpack_cache
|
||||
get_all_lfs_pointers_go
|
||||
].freeze
|
||||
|
|
|
@ -62,6 +62,9 @@ msgstr ""
|
|||
msgid " or references (e.g. path/to/project!merge_request_id)"
|
||||
msgstr ""
|
||||
|
||||
msgid "\"%{path}\" did not exist on \"%{ref}\""
|
||||
msgstr ""
|
||||
|
||||
msgid "%d comment"
|
||||
msgid_plural "%d comments"
|
||||
msgstr[0] ""
|
||||
|
|
|
@ -36,7 +36,6 @@ module QA
|
|||
runner.tags = tags
|
||||
runner.image = image
|
||||
runner.config = config if config
|
||||
runner.run_untagged = true
|
||||
runner.register!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe RedirectsForMissingPathOnTree, type: :controller do
|
||||
controller(ActionController::Base) do
|
||||
include Gitlab::Routing.url_helpers
|
||||
include RedirectsForMissingPathOnTree
|
||||
|
||||
def fake
|
||||
redirect_to_tree_root_for_missing_path(Project.find(params[:project_id]), params[:ref], params[:file_path])
|
||||
end
|
||||
end
|
||||
|
||||
let(:project) { create(:project) }
|
||||
|
||||
before do
|
||||
routes.draw { get 'fake' => 'anonymous#fake' }
|
||||
end
|
||||
|
||||
describe '#redirect_to_root_path' do
|
||||
it 'redirects to the tree path with a notice' do
|
||||
long_file_path = ('a/b/' * 30) + 'foo.txt'
|
||||
truncated_file_path = '...b/' + ('a/b/' * 12) + 'foo.txt'
|
||||
expected_message = "\"#{truncated_file_path}\" did not exist on \"theref\""
|
||||
|
||||
get :fake, params: { project_id: project.id, ref: 'theref', file_path: long_file_path }
|
||||
|
||||
expect(response).to redirect_to project_tree_path(project, 'theref')
|
||||
expect(response.flash[:notice]).to eq(expected_message)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,14 +25,25 @@ describe Projects::BlameController do
|
|||
})
|
||||
end
|
||||
|
||||
context "valid file" do
|
||||
context "valid branch, valid file" do
|
||||
let(:id) { 'master/files/ruby/popen.rb' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "invalid file" do
|
||||
let(:id) { 'master/files/ruby/missing_file.rb'}
|
||||
it { expect(response).to have_gitlab_http_status(404) }
|
||||
context "valid branch, invalid file" do
|
||||
let(:id) { 'master/files/ruby/invalid-path.rb' }
|
||||
|
||||
it 'redirects' do
|
||||
expect(subject)
|
||||
.to redirect_to("/#{project.full_path}/tree/master")
|
||||
end
|
||||
end
|
||||
|
||||
context "invalid branch, valid file" do
|
||||
let(:id) { 'invalid-branch/files/ruby/missing_file.rb'}
|
||||
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,26 +24,34 @@ describe Projects::BlobController do
|
|||
|
||||
context "valid branch, valid file" do
|
||||
let(:id) { 'master/README.md' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "valid branch, invalid file" do
|
||||
let(:id) { 'master/invalid-path.rb' }
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
|
||||
it 'redirects' do
|
||||
expect(subject)
|
||||
.to redirect_to("/#{project.full_path}/tree/master")
|
||||
end
|
||||
end
|
||||
|
||||
context "invalid branch, valid file" do
|
||||
let(:id) { 'invalid-branch/README.md' }
|
||||
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
end
|
||||
|
||||
context "binary file" do
|
||||
let(:id) { 'binary-encoding/encoding/binary-1.bin' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "Markdown file" do
|
||||
let(:id) { 'master/README.md' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
end
|
||||
|
@ -104,6 +112,7 @@ describe Projects::BlobController do
|
|||
|
||||
context 'redirect to tree' do
|
||||
let(:id) { 'markdown/doc' }
|
||||
|
||||
it 'redirects' do
|
||||
expect(subject)
|
||||
.to redirect_to("/#{project.full_path}/tree/markdown/doc")
|
||||
|
|
|
@ -30,46 +30,61 @@ describe Projects::TreeController do
|
|||
|
||||
context "valid branch, no path" do
|
||||
let(:id) { 'master' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "valid branch, valid path" do
|
||||
let(:id) { 'master/encoding/' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "valid branch, invalid path" do
|
||||
let(:id) { 'master/invalid-path/' }
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
|
||||
it 'redirects' do
|
||||
expect(subject)
|
||||
.to redirect_to("/#{project.full_path}/tree/master")
|
||||
end
|
||||
end
|
||||
|
||||
context "invalid branch, valid path" do
|
||||
let(:id) { 'invalid-branch/encoding/' }
|
||||
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
end
|
||||
|
||||
context "valid empty branch, invalid path" do
|
||||
let(:id) { 'empty-branch/invalid-path/' }
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
|
||||
it 'redirects' do
|
||||
expect(subject)
|
||||
.to redirect_to("/#{project.full_path}/tree/empty-branch")
|
||||
end
|
||||
end
|
||||
|
||||
context "valid empty branch" do
|
||||
let(:id) { 'empty-branch' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "invalid SHA commit ID" do
|
||||
let(:id) { 'ff39438/.gitignore' }
|
||||
|
||||
it { is_expected.to respond_with(:not_found) }
|
||||
end
|
||||
|
||||
context "valid SHA commit ID" do
|
||||
let(:id) { '6d39438' }
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
context "valid SHA commit ID with path" do
|
||||
let(:id) { '6d39438/.gitignore' }
|
||||
|
||||
it { expect(response).to have_gitlab_http_status(302) }
|
||||
end
|
||||
end
|
||||
|
@ -108,6 +123,7 @@ describe Projects::TreeController do
|
|||
|
||||
context 'redirect to blob' do
|
||||
let(:id) { 'master/README.md' }
|
||||
|
||||
it 'redirects' do
|
||||
redirect_url = "/#{project.full_path}/blob/master/README.md"
|
||||
expect(subject)
|
||||
|
|
|
@ -28,8 +28,7 @@ describe 'User uses header search field', :js do
|
|||
|
||||
context 'when clicking the search field' do
|
||||
before do
|
||||
page.find('#search').click
|
||||
wait_for_all_requests
|
||||
page.find('#search.js-autocomplete-disabled').click
|
||||
end
|
||||
|
||||
it 'shows category search dropdown' do
|
||||
|
|
|
@ -36,12 +36,20 @@ describe TodosFinder do
|
|||
expect(todos).to match_array([todo1, todo2])
|
||||
end
|
||||
|
||||
context 'when filtering by type' do
|
||||
it 'returns correct todos when filtered by a type' do
|
||||
todos = finder.new(user, { type: 'Issue' }).execute
|
||||
|
||||
expect(todos).to match_array([todo1])
|
||||
end
|
||||
|
||||
it 'returns the correct todos when filtering for multiple types' do
|
||||
todos = finder.new(user, { type: %w[Issue MergeRequest] }).execute
|
||||
|
||||
expect(todos).to match_array([todo1, todo2])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filtering for actions' do
|
||||
let!(:todo1) { create(:todo, user: user, project: project, target: issue, action: Todo::ASSIGNED) }
|
||||
let!(:todo2) { create(:todo, user: user, group: group, target: merge_request, action: Todo::DIRECTLY_ADDRESSED) }
|
||||
|
@ -53,14 +61,12 @@ describe TodosFinder do
|
|||
expect(todos).to match_array([todo2])
|
||||
end
|
||||
|
||||
context 'multiple actions' do
|
||||
it 'returns the expected todos' do
|
||||
it 'returns the expected todos when filtering for multiple action ids' do
|
||||
todos = finder.new(user, { action_id: [Todo::DIRECTLY_ADDRESSED, Todo::ASSIGNED] }).execute
|
||||
|
||||
expect(todos).to match_array([todo2, todo1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'by action names' do
|
||||
it 'returns the expected todos' do
|
||||
|
@ -69,15 +75,13 @@ describe TodosFinder do
|
|||
expect(todos).to match_array([todo2])
|
||||
end
|
||||
|
||||
context 'multiple actions' do
|
||||
it 'returns the expected todos' do
|
||||
it 'returns the expected todos when filtering for multiple action names' do
|
||||
todos = finder.new(user, { action: [:directly_addressed, :assigned] }).execute
|
||||
|
||||
expect(todos).to match_array([todo2, todo1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filtering by author' do
|
||||
let(:author1) { create(:user) }
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`IDE pipeline stage renders stage details & icon 1`] = `
|
||||
<div
|
||||
class="ide-stage card prepend-top-default"
|
||||
>
|
||||
<div
|
||||
class="card-header"
|
||||
>
|
||||
<ciicon-stub
|
||||
cssclasses=""
|
||||
size="24"
|
||||
status="[object Object]"
|
||||
/>
|
||||
|
||||
<strong
|
||||
class="prepend-left-8 ide-stage-title"
|
||||
data-container="body"
|
||||
data-original-title=""
|
||||
title=""
|
||||
>
|
||||
|
||||
build
|
||||
|
||||
</strong>
|
||||
|
||||
<div
|
||||
class="append-right-8 prepend-left-4"
|
||||
>
|
||||
<span
|
||||
class="badge badge-pill"
|
||||
>
|
||||
4
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<icon-stub
|
||||
class="ide-stage-collapse-icon"
|
||||
name="angle-down"
|
||||
size="16"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="card-body"
|
||||
>
|
||||
<item-stub
|
||||
job="[object Object]"
|
||||
/>
|
||||
<item-stub
|
||||
job="[object Object]"
|
||||
/>
|
||||
<item-stub
|
||||
job="[object Object]"
|
||||
/>
|
||||
<item-stub
|
||||
job="[object Object]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
86
spec/frontend/ide/components/jobs/stage_spec.js
Normal file
86
spec/frontend/ide/components/jobs/stage_spec.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import Stage from '~/ide/components/jobs/stage.vue';
|
||||
import Item from '~/ide/components/jobs/item.vue';
|
||||
import { stages, jobs } from '../../mock_data';
|
||||
|
||||
describe('IDE pipeline stage', () => {
|
||||
let wrapper;
|
||||
const defaultProps = {
|
||||
stage: {
|
||||
...stages[0],
|
||||
id: 0,
|
||||
dropdownPath: stages[0].dropdown_path,
|
||||
jobs: [...jobs],
|
||||
isLoading: false,
|
||||
isCollapsed: false,
|
||||
},
|
||||
};
|
||||
|
||||
const findHeader = () => wrapper.find({ ref: 'cardHeader' });
|
||||
const findJobList = () => wrapper.find({ ref: 'jobList' });
|
||||
|
||||
const createComponent = props => {
|
||||
wrapper = shallowMount(Stage, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it('emits fetch event when mounted', () => {
|
||||
createComponent();
|
||||
expect(wrapper.emitted().fetch).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders loading icon when no jobs and isLoading is true', () => {
|
||||
createComponent({
|
||||
stage: { ...defaultProps.stage, isLoading: true, jobs: [] },
|
||||
});
|
||||
|
||||
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('emits toggleCollaped event with stage id when clicking header', () => {
|
||||
const id = 5;
|
||||
createComponent({ stage: { ...defaultProps.stage, id } });
|
||||
findHeader().trigger('click');
|
||||
expect(wrapper.emitted().toggleCollapsed[0][0]).toBe(id);
|
||||
});
|
||||
|
||||
it('emits clickViewLog entity with job', () => {
|
||||
const [job] = defaultProps.stage.jobs;
|
||||
createComponent();
|
||||
wrapper
|
||||
.findAll(Item)
|
||||
.at(0)
|
||||
.vm.$emit('clickViewLog', job);
|
||||
expect(wrapper.emitted().clickViewLog[0][0]).toBe(job);
|
||||
});
|
||||
|
||||
it('renders stage details & icon', () => {
|
||||
createComponent();
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('when collapsed', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ stage: { ...defaultProps.stage, isCollapsed: true } });
|
||||
});
|
||||
|
||||
it('does not render job list', () => {
|
||||
expect(findJobList().isVisible()).toBe(false);
|
||||
});
|
||||
|
||||
it('sets border bottom class', () => {
|
||||
expect(findHeader().classes('border-bottom-0')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,95 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Stage from '~/ide/components/jobs/stage.vue';
|
||||
import { stages, jobs } from '../../mock_data';
|
||||
|
||||
describe('IDE pipeline stage', () => {
|
||||
const Component = Vue.extend(Stage);
|
||||
let vm;
|
||||
let stage;
|
||||
|
||||
beforeEach(() => {
|
||||
stage = {
|
||||
...stages[0],
|
||||
id: 0,
|
||||
dropdownPath: stages[0].dropdown_path,
|
||||
jobs: [...jobs],
|
||||
isLoading: false,
|
||||
isCollapsed: false,
|
||||
};
|
||||
|
||||
vm = new Component({
|
||||
propsData: { stage },
|
||||
});
|
||||
|
||||
spyOn(vm, '$emit');
|
||||
|
||||
vm.$mount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('emits fetch event when mounted', () => {
|
||||
expect(vm.$emit).toHaveBeenCalledWith('fetch', vm.stage);
|
||||
});
|
||||
|
||||
it('renders stages details', () => {
|
||||
expect(vm.$el.textContent).toContain(vm.stage.name);
|
||||
});
|
||||
|
||||
it('renders CI icon', () => {
|
||||
expect(vm.$el.querySelector('.ic-status_failed')).not.toBe(null);
|
||||
});
|
||||
|
||||
describe('collapsed', () => {
|
||||
it('emits event when clicking header', done => {
|
||||
vm.$el.querySelector('.card-header').click();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed', vm.stage.id);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('toggles collapse status when collapsed', done => {
|
||||
vm.stage.isCollapsed = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.card-body').style.display).toBe('none');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sets border bottom class when collapsed', done => {
|
||||
vm.stage.isCollapsed = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.card-header').classList).toContain('border-bottom-0');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders jobs count', () => {
|
||||
expect(vm.$el.querySelector('.badge').textContent).toContain('4');
|
||||
});
|
||||
|
||||
it('renders loading icon when no jobs and isLoading is true', done => {
|
||||
vm.stage.isLoading = true;
|
||||
vm.stage.jobs = [];
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders list of jobs', () => {
|
||||
expect(vm.$el.querySelectorAll('.ide-job-item').length).toBe(4);
|
||||
});
|
||||
});
|
|
@ -843,8 +843,8 @@ describe 'Git HTTP requests' do
|
|||
get "/#{project.full_path}/blob/master/info/refs"
|
||||
end
|
||||
|
||||
it "returns not found" do
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
it "redirects" do
|
||||
expect(response).to have_gitlab_http_status(302)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue