Use Vue component for job artifacts, Commit and Trigger Variables
This commit is contained in:
parent
765686f26a
commit
32ccde8ccd
|
|
@ -1,10 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default function handleRevealVariables() {
|
||||
$('.js-reveal-variables')
|
||||
.off('click')
|
||||
.on('click', function click() {
|
||||
$('.js-build-variables').toggle();
|
||||
$(this).hide();
|
||||
});
|
||||
}
|
||||
|
|
@ -1,40 +1,27 @@
|
|||
<script>
|
||||
import TimeagoTooltiop from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TimeagoTooltiop,
|
||||
TimeagoTooltip,
|
||||
},
|
||||
mixins: [
|
||||
timeagoMixin,
|
||||
],
|
||||
props: {
|
||||
// @build.artifacts_expired?
|
||||
haveArtifactsExpired: {
|
||||
type: Boolean,
|
||||
artifact: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
// @build.has_expiring_artifacts?
|
||||
willArtifactsExpire: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
computed: {
|
||||
isExpired() {
|
||||
return this.artifact.expired;
|
||||
},
|
||||
expireAt: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
keepArtifactsPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
downloadArtifactsPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
browseArtifactsPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
// Only when the key is `false` we can render this block
|
||||
willExpire() {
|
||||
return this.artifact.expired === false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -46,21 +33,22 @@
|
|||
</div>
|
||||
|
||||
<p
|
||||
v-if="haveArtifactsExpired"
|
||||
v-if="isExpired"
|
||||
class="js-artifacts-removed build-detail-row"
|
||||
>
|
||||
{{ s__('Job|The artifacts were removed') }}
|
||||
</p>
|
||||
|
||||
<p
|
||||
v-else-if="willArtifactsExpire"
|
||||
v-else-if="willExpire"
|
||||
class="js-artifacts-will-be-removed build-detail-row"
|
||||
>
|
||||
{{ s__('Job|The artifacts will be removed') }}
|
||||
{{ s__('Job|The artifacts will be removed in') }}
|
||||
</p>
|
||||
|
||||
<timeago-tooltiop
|
||||
v-if="expireAt"
|
||||
:time="expireAt"
|
||||
<timeago-tooltip
|
||||
v-if="artifact.expire_at"
|
||||
:time="artifact.expire_at"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
|
@ -68,8 +56,8 @@
|
|||
role="group"
|
||||
>
|
||||
<a
|
||||
v-if="keepArtifactsPath"
|
||||
:href="keepArtifactsPath"
|
||||
v-if="artifact.keep_path"
|
||||
:href="artifact.keep_path"
|
||||
class="js-keep-artifacts btn btn-sm btn-default"
|
||||
data-method="post"
|
||||
>
|
||||
|
|
@ -77,8 +65,8 @@
|
|||
</a>
|
||||
|
||||
<a
|
||||
v-if="downloadArtifactsPath"
|
||||
:href="downloadArtifactsPath"
|
||||
v-if="artifact.download_path"
|
||||
:href="artifact.download_path"
|
||||
class="js-download-artifacts btn btn-sm btn-default"
|
||||
download
|
||||
rel="nofollow"
|
||||
|
|
@ -87,8 +75,8 @@
|
|||
</a>
|
||||
|
||||
<a
|
||||
v-if="browseArtifactsPath"
|
||||
:href="browseArtifactsPath"
|
||||
v-if="artifact.browse_path"
|
||||
:href="artifact.browse_path"
|
||||
class="js-browse-artifacts btn btn-sm btn-default"
|
||||
>
|
||||
{{ s__('Job|Browse') }}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,56 @@
|
|||
<script>
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ClipboardButton,
|
||||
},
|
||||
props: {
|
||||
pipelineShortSha: {
|
||||
type: String,
|
||||
required: true,
|
||||
export default {
|
||||
components: {
|
||||
ClipboardButton,
|
||||
},
|
||||
pipelineShaPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
props: {
|
||||
commit: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
mergeRequest: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
isLastBlock: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
mergeRequestReference: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
mergeRequestPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
gitCommitTitlte: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="block">
|
||||
<div
|
||||
:class="{
|
||||
'block-last': isLastBlock,
|
||||
block: !isLastBlock
|
||||
}">
|
||||
<p>
|
||||
{{ __('Commit') }}
|
||||
|
||||
<a
|
||||
:href="pipelineShaPath"
|
||||
:href="commit.commit_path"
|
||||
class="js-commit-sha commit-sha link-commit"
|
||||
>
|
||||
{{ pipelineShortSha }}
|
||||
</a>
|
||||
>{{ commit.short_id }}</a>
|
||||
|
||||
<clipboard-button
|
||||
:text="pipelineShortSha"
|
||||
:text="commit.short_id"
|
||||
:title="__('Copy commit SHA to clipboard')"
|
||||
css-class="btn btn-clipboard btn-transparent"
|
||||
/>
|
||||
|
||||
<a
|
||||
v-if="mergeRequestPath && mergeRequestReference"
|
||||
:href="mergeRequestPath"
|
||||
v-if="mergeRequest"
|
||||
:href="mergeRequest.path"
|
||||
class="js-link-commit link-commit"
|
||||
>
|
||||
{{ mergeRequestReference }}
|
||||
</a>
|
||||
>{{ mergeRequest.iid }}</a>
|
||||
</p>
|
||||
|
||||
<p class="build-light-text append-bottom-0">
|
||||
{{ gitCommitTitlte }}
|
||||
{{ commit.title }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,89 +1,113 @@
|
|||
<script>
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import DetailRow from './sidebar_detail_row.vue';
|
||||
import _ from 'underscore';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import DetailRow from './sidebar_detail_row.vue';
|
||||
import ArtifactsBlock from './artifacts_block.vue';
|
||||
import TriggerBlock from './trigger_block.vue';
|
||||
import CommitBlock from './commit_block.vue';
|
||||
|
||||
export default {
|
||||
name: 'SidebarDetailsBlock',
|
||||
components: {
|
||||
DetailRow,
|
||||
Icon,
|
||||
},
|
||||
mixins: [timeagoMixin],
|
||||
props: {
|
||||
job: {
|
||||
type: Object,
|
||||
required: true,
|
||||
export default {
|
||||
name: 'SidebarDetailsBlock',
|
||||
components: {
|
||||
ArtifactsBlock,
|
||||
CommitBlock,
|
||||
DetailRow,
|
||||
Icon,
|
||||
TriggerBlock,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
mixins: [timeagoMixin],
|
||||
props: {
|
||||
job: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
runnerHelpUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
terminalPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
runnerHelpUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
terminalPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
shouldRenderContent() {
|
||||
return !this.isLoading && Object.keys(this.job).length > 0;
|
||||
},
|
||||
coverage() {
|
||||
return `${this.job.coverage}%`;
|
||||
},
|
||||
duration() {
|
||||
return timeIntervalInWords(this.job.duration);
|
||||
},
|
||||
queued() {
|
||||
return timeIntervalInWords(this.job.queued);
|
||||
},
|
||||
runnerId() {
|
||||
return `${this.job.runner.description} (#${this.job.runner.id})`;
|
||||
},
|
||||
retryButtonClass() {
|
||||
let className =
|
||||
'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
|
||||
className +=
|
||||
this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
|
||||
return className;
|
||||
},
|
||||
hasTimeout() {
|
||||
return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
|
||||
},
|
||||
timeout() {
|
||||
if (this.job.metadata == null) {
|
||||
return '';
|
||||
}
|
||||
computed: {
|
||||
shouldRenderContent() {
|
||||
return !this.isLoading && Object.keys(this.job).length > 0;
|
||||
},
|
||||
coverage() {
|
||||
return `${this.job.coverage}%`;
|
||||
},
|
||||
duration() {
|
||||
return timeIntervalInWords(this.job.duration);
|
||||
},
|
||||
queued() {
|
||||
return timeIntervalInWords(this.job.queued);
|
||||
},
|
||||
runnerId() {
|
||||
return `${this.job.runner.description} (#${this.job.runner.id})`;
|
||||
},
|
||||
retryButtonClass() {
|
||||
let className =
|
||||
'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
|
||||
className +=
|
||||
this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
|
||||
return className;
|
||||
},
|
||||
hasTimeout() {
|
||||
return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
|
||||
},
|
||||
timeout() {
|
||||
if (this.job.metadata == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let t = this.job.metadata.timeout_human_readable;
|
||||
if (this.job.metadata.timeout_source !== '') {
|
||||
t += ` (from ${this.job.metadata.timeout_source})`;
|
||||
}
|
||||
let t = this.job.metadata.timeout_human_readable;
|
||||
if (this.job.metadata.timeout_source !== '') {
|
||||
t += ` (from ${this.job.metadata.timeout_source})`;
|
||||
}
|
||||
|
||||
return t;
|
||||
return t;
|
||||
},
|
||||
renderBlock() {
|
||||
return (
|
||||
this.job.merge_request ||
|
||||
this.job.duration ||
|
||||
this.job.finished_data ||
|
||||
this.job.erased_at ||
|
||||
this.job.queued ||
|
||||
this.job.runner ||
|
||||
this.job.coverage ||
|
||||
this.job.tags.length ||
|
||||
this.job.cancel_path
|
||||
);
|
||||
},
|
||||
hasArtifact() {
|
||||
return !_.isEmpty(this.job.artifact);
|
||||
},
|
||||
hasTriggers() {
|
||||
return !_.isEmpty(this.job.trigger);
|
||||
},
|
||||
hasStages() {
|
||||
return (
|
||||
this.job &&
|
||||
this.job.pipeline &&
|
||||
this.job.pipeline.stages &&
|
||||
this.job.pipeline.stages.length > 0
|
||||
) || false;
|
||||
},
|
||||
commit() {
|
||||
return this.job.pipeline.commit || {};
|
||||
},
|
||||
},
|
||||
renderBlock() {
|
||||
return (
|
||||
this.job.merge_request ||
|
||||
this.job.duration ||
|
||||
this.job.finished_data ||
|
||||
this.job.erased_at ||
|
||||
this.job.queued ||
|
||||
this.job.runner ||
|
||||
this.job.coverage ||
|
||||
this.job.tags.length ||
|
||||
this.job.cancel_path
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
|
|
@ -229,6 +253,19 @@ export default {
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<artifacts-block
|
||||
v-if="hasArtifact"
|
||||
:artifact="job.artifact"
|
||||
/>
|
||||
<trigger-block
|
||||
v-if="hasTriggers"
|
||||
:trigger="job.trigger"
|
||||
/>
|
||||
<commit-block
|
||||
:is-last-block="hasStages"
|
||||
:commit="commit"
|
||||
:merge-request="job.merge_request"
|
||||
/>
|
||||
</template>
|
||||
<gl-loading-icon
|
||||
v-if="isLoading"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
shortToken: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
variables: {
|
||||
trigger: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
@ -20,7 +13,7 @@
|
|||
},
|
||||
computed: {
|
||||
hasVariables() {
|
||||
return Object.keys(this.variables).length > 0;
|
||||
return this.trigger.variables && this.trigger.variables.length > 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -38,17 +31,18 @@
|
|||
</h4>
|
||||
|
||||
<p
|
||||
v-if="shortToken"
|
||||
v-if="trigger.short_token"
|
||||
class="js-short-token"
|
||||
>
|
||||
<span class="build-light-text">
|
||||
{{ __('Token') }}
|
||||
</span>
|
||||
{{ shortToken }}
|
||||
{{ trigger.short_token }}
|
||||
</p>
|
||||
|
||||
<p v-if="hasVariables">
|
||||
<button
|
||||
v-if="!areVariablesVisible"
|
||||
type="button"
|
||||
class="btn btn-default group js-reveal-variables"
|
||||
@click="revealVariables"
|
||||
|
|
@ -63,20 +57,20 @@
|
|||
class="js-build-variables trigger-build-variables"
|
||||
>
|
||||
<template
|
||||
v-for="(value, key) in variables"
|
||||
v-for="variable in trigger.variables"
|
||||
>
|
||||
<dt
|
||||
:key="`${key}-variable`"
|
||||
:key="`${variable.key}-variable`"
|
||||
class="js-build-variable trigger-build-variable"
|
||||
>
|
||||
{{ key }}
|
||||
{{ variable.key }}
|
||||
</dt>
|
||||
|
||||
<dd
|
||||
:key="`${key}-value`"
|
||||
:key="`${variable.key}-value`"
|
||||
class="js-build-value trigger-build-value"
|
||||
>
|
||||
{{ value }}
|
||||
{{ variable.value }}
|
||||
</dd>
|
||||
</template>
|
||||
</dl>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import Poll from '../lib/utils/poll';
|
|||
import JobStore from './stores/job_store';
|
||||
import JobService from './services/job_service';
|
||||
import Job from '../job';
|
||||
import handleRevealVariables from '../build_variables';
|
||||
|
||||
export default class JobMediator {
|
||||
constructor(options = {}) {
|
||||
|
|
@ -20,7 +19,6 @@ export default class JobMediator {
|
|||
|
||||
initBuildClass() {
|
||||
this.build = new Job();
|
||||
handleRevealVariables();
|
||||
}
|
||||
|
||||
fetchJob() {
|
||||
|
|
|
|||
|
|
@ -3,63 +3,6 @@
|
|||
.blocks-container
|
||||
#js-details-block-vue{ data: { terminal_path: can?(current_user, :create_build_terminal, @build) && @build.has_terminal? ? terminal_project_job_path(@project, @build) : nil } }
|
||||
|
||||
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
|
||||
.block
|
||||
.title
|
||||
Job artifacts
|
||||
- if @build.artifacts_expired?
|
||||
%p.build-detail-row
|
||||
The artifacts were removed
|
||||
#{time_ago_with_tooltip(@build.artifacts_expire_at)}
|
||||
- elsif @build.has_expiring_artifacts?
|
||||
%p.build-detail-row
|
||||
The artifacts will be removed
|
||||
#{time_ago_with_tooltip(@build.artifacts_expire_at)}
|
||||
|
||||
- if @build.artifacts?
|
||||
.btn-group.d-flex{ role: :group }
|
||||
- if @build.has_expiring_artifacts? && can?(current_user, :update_build, @build)
|
||||
= link_to keep_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default', method: :post do
|
||||
Keep
|
||||
|
||||
= link_to download_project_job_artifacts_path(@project, @build), rel: 'nofollow', download: '', class: 'btn btn-sm btn-default' do
|
||||
Download
|
||||
|
||||
- if @build.browsable_artifacts?
|
||||
= link_to browse_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default' do
|
||||
Browse
|
||||
|
||||
- if @build.trigger_request
|
||||
.build-widget.block
|
||||
%h4.title
|
||||
Trigger
|
||||
|
||||
- if @build.trigger_request&.trigger&.short_token
|
||||
%p
|
||||
%span.build-light-text Token:
|
||||
#{@build.trigger_request.trigger.short_token}
|
||||
|
||||
- if @build.trigger_variables.any?
|
||||
%p
|
||||
%button.btn.group.js-reveal-variables Reveal Variables
|
||||
|
||||
%dl.js-build-variables.trigger-build-variables.hide
|
||||
- @build.trigger_variables.each do |trigger_variable|
|
||||
%dt.js-build-variable.trigger-build-variable= trigger_variable[:key]
|
||||
%dd.js-build-value.trigger-build-value= trigger_variable[:value]
|
||||
|
||||
%div{ class: (@build.pipeline.stages_count > 1 ? "block" : "block-last") }
|
||||
%p
|
||||
Commit
|
||||
= link_to @build.pipeline.short_sha, project_commit_path(@project, @build.pipeline.sha), class: 'commit-sha link-commit'
|
||||
= clipboard_button(text: @build.pipeline.short_sha, title: "Copy commit SHA to clipboard")
|
||||
- if @build.merge_request
|
||||
in
|
||||
= link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request), class: 'link-commit'
|
||||
|
||||
%p.build-light-text.append-bottom-0
|
||||
#{@build.pipeline.git_commit_title}
|
||||
|
||||
- if @build.pipeline.stages_count > 1
|
||||
.block-last.dropdown.build-dropdown
|
||||
%div
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use Vue components and new API to render Artifacts, Trigger Variables and Commit blocks on Job page
|
||||
merge_request: 21777
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -35,11 +35,15 @@ module Gitlab
|
|||
request_headers = env_http_headers(env)
|
||||
status, headers, body = @app.call(env)
|
||||
|
||||
full_body = ''
|
||||
body.each { |b| full_body << b }
|
||||
|
||||
request = OpenStruct.new(
|
||||
url: url,
|
||||
status_code: status,
|
||||
request_headers: request_headers,
|
||||
response_headers: headers
|
||||
response_headers: headers,
|
||||
body: full_body
|
||||
)
|
||||
log_request request
|
||||
|
||||
|
|
|
|||
|
|
@ -3365,7 +3365,7 @@ msgstr ""
|
|||
msgid "Job|The artifacts were removed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Job|The artifacts will be removed"
|
||||
msgid "Job|The artifacts will be removed in"
|
||||
msgstr ""
|
||||
|
||||
msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
|
||||
|
|
|
|||
|
|
@ -20,23 +20,13 @@ describe "User downloads artifacts" do
|
|||
end
|
||||
|
||||
context "via job id" do
|
||||
set(:url) { download_project_job_artifacts_path(project, job) }
|
||||
let(:url) { download_project_job_artifacts_path(project, job) }
|
||||
|
||||
it_behaves_like "downloading"
|
||||
end
|
||||
|
||||
context "via branch name and job name" do
|
||||
set(:url) { latest_succeeded_project_artifacts_path(project, "#{pipeline.ref}/download", job: job.name) }
|
||||
|
||||
it_behaves_like "downloading"
|
||||
end
|
||||
|
||||
context "via clicking the `Download` button" do
|
||||
set(:url) { project_job_path(project, job) }
|
||||
|
||||
before do
|
||||
click_link("Download")
|
||||
end
|
||||
let(:url) { latest_succeeded_project_artifacts_path(project, "#{pipeline.ref}/download", job: job.name) }
|
||||
|
||||
it_behaves_like "downloading"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
let(:user) { create(:user) }
|
||||
let(:user_access_level) { :developer }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
|
||||
|
||||
let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
|
||||
let(:job2) { create(:ci_build) }
|
||||
|
|
@ -20,7 +20,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
|
||||
describe "GET /:project/jobs" do
|
||||
let!(:job) { create(:ci_build, pipeline: pipeline) }
|
||||
let!(:job) { create(:ci_build, pipeline: pipeline) }
|
||||
|
||||
context "Pending scope" do
|
||||
before do
|
||||
|
|
@ -115,22 +115,28 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
context "Job from project" do
|
||||
let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline) }
|
||||
|
||||
before do
|
||||
visit project_job_path(project, job)
|
||||
end
|
||||
|
||||
it 'shows status name', :js do
|
||||
visit project_job_path(project, job)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css('.ci-status.ci-success', text: 'passed')
|
||||
end
|
||||
|
||||
it 'shows commit`s data' do
|
||||
expect(page.status_code).to eq(200)
|
||||
it 'shows commit`s data', :js do
|
||||
requests = inspect_requests() do
|
||||
visit project_job_path(project, job)
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
expect(requests.first.status_code).to eq(200)
|
||||
expect(page).to have_content pipeline.sha[0..7]
|
||||
expect(page).to have_content pipeline.git_commit_message
|
||||
expect(page).to have_content pipeline.git_author_name
|
||||
expect(page).to have_content pipeline.commit.title
|
||||
end
|
||||
|
||||
it 'shows active job' do
|
||||
visit project_job_path(project, job)
|
||||
|
||||
expect(page).to have_selector('.build-job.active')
|
||||
end
|
||||
end
|
||||
|
|
@ -199,7 +205,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
it { expect(page.status_code).to eq(404) }
|
||||
end
|
||||
|
||||
context "Download artifacts" do
|
||||
context "Download artifacts", :js do
|
||||
before do
|
||||
job.update(legacy_artifacts_file: artifacts_file)
|
||||
visit project_job_path(project, job)
|
||||
|
|
@ -208,9 +214,22 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
it 'has button to download artifacts' do
|
||||
expect(page).to have_content 'Download'
|
||||
end
|
||||
|
||||
it 'downloads the zip file when user clicks the download button' do
|
||||
requests = inspect_requests() do
|
||||
click_link 'Download'
|
||||
end
|
||||
|
||||
artifact_request = requests.find { |req| req.url.match(%r{artifacts/download}) }
|
||||
|
||||
expect(artifact_request.response_headers["Content-Disposition"]).to eq(%Q{attachment; filename="#{job.artifacts_file.filename}"})
|
||||
expect(artifact_request.response_headers['Content-Transfer-Encoding']).to eq("binary")
|
||||
expect(artifact_request.response_headers['Content-Type']).to eq("image/gif")
|
||||
expect(artifact_request.body).to eq(job.artifacts_file.file.read.b)
|
||||
end
|
||||
end
|
||||
|
||||
context 'Artifacts expire date' do
|
||||
context 'Artifacts expire date', :js do
|
||||
before do
|
||||
job.update(legacy_artifacts_file: artifacts_file,
|
||||
artifacts_expire_at: expire_at)
|
||||
|
|
@ -231,12 +250,12 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
|
||||
context 'when user has ability to update job' do
|
||||
it 'keeps artifacts when keep button is clicked' do
|
||||
expect(page).to have_content 'The artifacts will be removed'
|
||||
expect(page).to have_content 'The artifacts will be removed in'
|
||||
|
||||
click_link 'Keep'
|
||||
|
||||
expect(page).to have_no_link 'Keep'
|
||||
expect(page).to have_no_content 'The artifacts will be removed'
|
||||
expect(page).to have_no_content 'The artifacts will be removed in'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -314,6 +333,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
|
||||
shared_examples 'expected variables behavior' do
|
||||
it 'shows variable key and value after click', :js do
|
||||
expect(page).to have_content('Token')
|
||||
expect(page).to have_css('.js-reveal-variables')
|
||||
expect(page).not_to have_css('.js-build-variable')
|
||||
expect(page).not_to have_css('.js-build-value')
|
||||
|
|
@ -542,20 +562,26 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /:project/jobs/:id/download" do
|
||||
describe "GET /:project/jobs/:id/download", :js do
|
||||
before do
|
||||
job.update(legacy_artifacts_file: artifacts_file)
|
||||
visit project_job_path(project, job)
|
||||
|
||||
click_link 'Download'
|
||||
end
|
||||
|
||||
context "Build from other project" do
|
||||
before do
|
||||
job2.update(legacy_artifacts_file: artifacts_file)
|
||||
visit download_project_job_artifacts_path(project, job2)
|
||||
end
|
||||
|
||||
it { expect(page.status_code).to eq(404) }
|
||||
it do
|
||||
requests = inspect_requests() do
|
||||
visit download_project_job_artifacts_path(project, job2)
|
||||
end
|
||||
|
||||
expect(requests.first.status_code).to eq(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,19 @@ describe('Artifacts block', () => {
|
|||
const timeago = getTimeago();
|
||||
const formatedDate = timeago.format(expireAt);
|
||||
|
||||
const expiredArtifact = {
|
||||
expire_at: expireAt,
|
||||
expired: true,
|
||||
};
|
||||
|
||||
const nonExpiredArtifact = {
|
||||
download_path: '/gitlab-org/gitlab-ce/-/jobs/98314558/artifacts/download',
|
||||
browse_path: '/gitlab-org/gitlab-ce/-/jobs/98314558/artifacts/browse',
|
||||
keep_path: '/gitlab-org/gitlab-ce/-/jobs/98314558/artifacts/keep',
|
||||
expire_at: expireAt,
|
||||
expired: false,
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
|
@ -18,100 +31,87 @@ describe('Artifacts block', () => {
|
|||
describe('with expired artifacts', () => {
|
||||
it('renders expired artifact date and info', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
artifact: expiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-artifacts-removed')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).toBeNull();
|
||||
expect(vm.$el.textContent).toContain(formatedDate);
|
||||
expect(vm.$el.querySelector('.js-artifacts-removed').textContent.trim()).toEqual(
|
||||
'The artifacts were removed',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with artifacts that will expire', () => {
|
||||
it('renders will expire artifact date and info', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: false,
|
||||
willArtifactsExpire: true,
|
||||
expireAt,
|
||||
artifact: nonExpiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-artifacts-removed')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).not.toBeNull();
|
||||
expect(vm.$el.textContent).toContain(formatedDate);
|
||||
expect(vm.$el.querySelector('.js-artifacts-will-be-removed').textContent.trim()).toEqual(
|
||||
'The artifacts will be removed in',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can keep the artifacts', () => {
|
||||
describe('with keep path', () => {
|
||||
it('renders the keep button', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
keepArtifactsPath: '/keep',
|
||||
artifact: nonExpiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-keep-artifacts')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can not keep the artifacts', () => {
|
||||
describe('without keep path', () => {
|
||||
it('does not render the keep button', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
artifact: expiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-keep-artifacts')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can download the artifacts', () => {
|
||||
describe('with download path', () => {
|
||||
it('renders the download button', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
downloadArtifactsPath: '/download',
|
||||
artifact: nonExpiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-download-artifacts')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can not download the artifacts', () => {
|
||||
describe('without download path', () => {
|
||||
it('does not render the keep button', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
artifact: expiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-download-artifacts')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can browse the artifacts', () => {
|
||||
describe('with browse path', () => {
|
||||
it('does not render the browse button', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
browseArtifactsPath: '/browse',
|
||||
artifact: nonExpiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-browse-artifacts')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can not browse the artifacts', () => {
|
||||
describe('without browse path', () => {
|
||||
it('does not render the browse button', () => {
|
||||
vm = mountComponent(Component, {
|
||||
haveArtifactsExpired: true,
|
||||
willArtifactsExpire: false,
|
||||
expireAt,
|
||||
artifact: expiredArtifact,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-browse-artifacts')).toBeNull();
|
||||
|
|
|
|||
|
|
@ -7,11 +7,16 @@ describe('Commit block', () => {
|
|||
let vm;
|
||||
|
||||
const props = {
|
||||
pipelineShortSha: '1f0fb84f',
|
||||
pipelineShaPath: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
|
||||
mergeRequestReference: '!21244',
|
||||
mergeRequestPath: 'merge_requests/21244',
|
||||
gitCommitTitlte: 'Regenerate pot files',
|
||||
commit: {
|
||||
short_id: '1f0fb84f',
|
||||
commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
|
||||
title: 'Update README.md',
|
||||
},
|
||||
mergeRequest: {
|
||||
iid: '!21244',
|
||||
path: 'merge_requests/21244',
|
||||
},
|
||||
isLastBlock: true,
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -26,12 +31,18 @@ describe('Commit block', () => {
|
|||
});
|
||||
|
||||
it('renders pipeline short sha link', () => {
|
||||
expect(vm.$el.querySelector('.js-commit-sha').getAttribute('href')).toEqual(props.pipelineShaPath);
|
||||
expect(vm.$el.querySelector('.js-commit-sha').textContent.trim()).toEqual(props.pipelineShortSha);
|
||||
expect(vm.$el.querySelector('.js-commit-sha').getAttribute('href')).toEqual(
|
||||
props.commit.commit_path,
|
||||
);
|
||||
expect(vm.$el.querySelector('.js-commit-sha').textContent.trim()).toEqual(
|
||||
props.commit.short_id,
|
||||
);
|
||||
});
|
||||
|
||||
it('renders clipboard button', () => {
|
||||
expect(vm.$el.querySelector('button').getAttribute('data-clipboard-text')).toEqual(props.pipelineShortSha);
|
||||
expect(vm.$el.querySelector('button').getAttribute('data-clipboard-text')).toEqual(
|
||||
props.commit.short_id,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -41,17 +52,19 @@ describe('Commit block', () => {
|
|||
...props,
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-link-commit').getAttribute('href')).toEqual(props.mergeRequestPath);
|
||||
expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual(props.mergeRequestReference);
|
||||
|
||||
expect(vm.$el.querySelector('.js-link-commit').getAttribute('href')).toEqual(
|
||||
props.mergeRequest.path,
|
||||
);
|
||||
expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual(
|
||||
props.mergeRequest.iid,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('without merge request', () => {
|
||||
it('does not render merge request', () => {
|
||||
const copyProps = Object.assign({}, props);
|
||||
delete copyProps.mergeRequestPath;
|
||||
delete copyProps.mergeRequestReference;
|
||||
delete copyProps.mergeRequest;
|
||||
|
||||
vm = mountComponent(Component, {
|
||||
...copyProps,
|
||||
|
|
@ -67,7 +80,7 @@ describe('Commit block', () => {
|
|||
...props,
|
||||
});
|
||||
|
||||
expect(vm.$el.textContent).toContain(props.gitCommitTitlte);
|
||||
expect(vm.$el.textContent).toContain(props.commit.title);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ describe('Trigger block', () => {
|
|||
describe('with short token', () => {
|
||||
it('renders short token', () => {
|
||||
vm = mountComponent(Component, {
|
||||
shortToken: '0a666b2',
|
||||
trigger: {
|
||||
short_token: '0a666b2',
|
||||
},
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.js-short-token').textContent).toContain('0a666b2');
|
||||
|
|
@ -22,7 +24,7 @@ describe('Trigger block', () => {
|
|||
|
||||
describe('without short token', () => {
|
||||
it('does not render short token', () => {
|
||||
vm = mountComponent(Component, {});
|
||||
vm = mountComponent(Component, { trigger: {} });
|
||||
|
||||
expect(vm.$el.querySelector('.js-short-token')).toBeNull();
|
||||
});
|
||||
|
|
@ -32,9 +34,12 @@ describe('Trigger block', () => {
|
|||
describe('reveal variables', () => {
|
||||
it('reveals variables on click', done => {
|
||||
vm = mountComponent(Component, {
|
||||
variables: {
|
||||
key: 'value',
|
||||
variable: 'foo',
|
||||
trigger: {
|
||||
short_token: 'bd7e',
|
||||
variables: [
|
||||
{ key: 'UPLOAD_TO_GCS', value: 'false', public: false },
|
||||
{ key: 'UPLOAD_TO_S3', value: 'true', public: false },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -44,10 +49,10 @@ describe('Trigger block', () => {
|
|||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('key');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('value');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('variable');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('foo');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('UPLOAD_TO_GCS');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('false');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('UPLOAD_TO_S3');
|
||||
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
|
@ -57,7 +62,7 @@ describe('Trigger block', () => {
|
|||
|
||||
describe('without variables', () => {
|
||||
it('does not render variables', () => {
|
||||
vm = mountComponent(Component);
|
||||
vm = mountComponent(Component, { trigger: {} });
|
||||
|
||||
expect(vm.$el.querySelector('.js-reveal-variables')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-build-variables')).toBeNull();
|
||||
|
|
@ -188,40 +188,4 @@ describe 'projects/jobs/show' do
|
|||
expect(rendered).not_to have_link('New issue')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when incomplete trigger_request is used' do
|
||||
before do
|
||||
build.trigger_request = FactoryBot.build(:ci_trigger_request, trigger: nil)
|
||||
end
|
||||
|
||||
it 'test should not render token block' do
|
||||
render
|
||||
|
||||
expect(rendered).not_to have_content('Token')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when complete trigger_request is used' do
|
||||
before do
|
||||
build.trigger_request = FactoryBot.build(:ci_trigger_request)
|
||||
end
|
||||
|
||||
it 'should render token' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_content('Token')
|
||||
expect(rendered).to have_content(build.trigger_request.trigger.short_token)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'commit title in sidebar' do
|
||||
let(:commit_title) { project.commit.title }
|
||||
|
||||
it 'shows commit title and not show commit message' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_css('p.build-light-text.append-bottom-0',
|
||||
text: /\A\n#{Regexp.escape(commit_title)}\n\Z/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue