Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
83020ec58c
commit
5d3bcd82b5
|
@ -139,6 +139,7 @@ export const receiveJobLogsSuccess = ({ commit }, data) =>
|
|||
export const fetchJobLogs = ({ dispatch, state }) => {
|
||||
dispatch('requestJobLogs');
|
||||
|
||||
// update trace endpoint once BE compeletes trace re-naming in #340626
|
||||
return axios
|
||||
.get(`${state.detailJob.path}/trace`, { params: { format: 'json' } })
|
||||
.then(({ data }) => dispatch('receiveJobLogsSuccess', data))
|
||||
|
|
|
@ -80,13 +80,13 @@ export default {
|
|||
'isLoading',
|
||||
'job',
|
||||
'isSidebarOpen',
|
||||
'trace',
|
||||
'isTraceComplete',
|
||||
'traceSize',
|
||||
'isTraceSizeVisible',
|
||||
'jobLog',
|
||||
'isJobLogComplete',
|
||||
'jobLogSize',
|
||||
'isJobLogSizeVisible',
|
||||
'isScrollBottomDisabled',
|
||||
'isScrollTopDisabled',
|
||||
'isScrolledToBottomBeforeReceivingTrace',
|
||||
'isScrolledToBottomBeforeReceivingJobLog',
|
||||
'hasError',
|
||||
'selectedStage',
|
||||
]),
|
||||
|
@ -97,7 +97,7 @@ export default {
|
|||
'shouldRenderTriggeredLabel',
|
||||
'hasEnvironment',
|
||||
'shouldRenderSharedRunnerLimitWarning',
|
||||
'hasTrace',
|
||||
'hasJobLog',
|
||||
'emptyStateIllustration',
|
||||
'isScrollingDown',
|
||||
'emptyStateAction',
|
||||
|
@ -155,7 +155,7 @@ export default {
|
|||
this.updateSidebar();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.stopPollingTrace();
|
||||
this.stopPollingJobLog();
|
||||
this.stopPolling();
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
window.removeEventListener('scroll', this.updateScroll);
|
||||
|
@ -168,7 +168,7 @@ export default {
|
|||
'toggleSidebar',
|
||||
'scrollBottom',
|
||||
'scrollTop',
|
||||
'stopPollingTrace',
|
||||
'stopPollingJobLog',
|
||||
'stopPolling',
|
||||
'toggleScrollButtons',
|
||||
'toggleScrollAnimation',
|
||||
|
@ -270,7 +270,7 @@ export default {
|
|||
<div
|
||||
v-if="job.archived"
|
||||
class="gl-mt-3 gl-py-2 gl-px-3 gl-align-items-center gl-z-index-1 gl-m-auto archived-job"
|
||||
:class="{ 'sticky-top gl-border-bottom-0': hasTrace }"
|
||||
:class="{ 'sticky-top gl-border-bottom-0': hasJobLog }"
|
||||
data-testid="archived-job"
|
||||
>
|
||||
<gl-icon name="lock" class="gl-vertical-align-bottom" />
|
||||
|
@ -278,7 +278,7 @@ export default {
|
|||
</div>
|
||||
<!-- job log -->
|
||||
<div
|
||||
v-if="hasTrace"
|
||||
v-if="hasJobLog"
|
||||
class="build-trace-container gl-relative"
|
||||
:class="{ 'gl-mt-3': !job.archived }"
|
||||
>
|
||||
|
@ -289,22 +289,22 @@ export default {
|
|||
'has-archived-block': job.archived,
|
||||
}"
|
||||
:erase-path="job.erase_path"
|
||||
:size="traceSize"
|
||||
:size="jobLogSize"
|
||||
:raw-path="job.raw_path"
|
||||
:is-scroll-bottom-disabled="isScrollBottomDisabled"
|
||||
:is-scroll-top-disabled="isScrollTopDisabled"
|
||||
:is-trace-size-visible="isTraceSizeVisible"
|
||||
:is-job-log-size-visible="isJobLogSizeVisible"
|
||||
:is-scrolling-down="isScrollingDown"
|
||||
@scrollJobLogTop="scrollTop"
|
||||
@scrollJobLogBottom="scrollBottom"
|
||||
/>
|
||||
<log :trace="trace" :is-complete="isTraceComplete" />
|
||||
<log :job-log="jobLog" :is-complete="isJobLogComplete" />
|
||||
</div>
|
||||
<!-- EO job log -->
|
||||
|
||||
<!-- empty state -->
|
||||
<empty-state
|
||||
v-if="!hasTrace"
|
||||
v-if="!hasJobLog"
|
||||
:illustration-path="emptyStateIllustration.image"
|
||||
:illustration-size-class="emptyStateIllustration.size"
|
||||
:title="emptyStateTitle"
|
||||
|
|
|
@ -44,7 +44,7 @@ export default {
|
|||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isTraceSizeVisible: {
|
||||
isJobLogSizeVisible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
|
@ -73,7 +73,7 @@ export default {
|
|||
class="truncated-info gl-display-none gl-sm-display-block gl-float-left"
|
||||
data-testid="log-truncated-info"
|
||||
>
|
||||
<template v-if="isTraceSizeVisible">
|
||||
<template v-if="isJobLogSizeVisible">
|
||||
{{ jobLogSize }}
|
||||
<gl-link
|
||||
v-if="rawPath"
|
||||
|
|
|
@ -17,7 +17,7 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
traceEndpoint: {
|
||||
jobLogEndpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
@ -42,7 +42,7 @@ export default {
|
|||
<log-line-header
|
||||
:line="section.line"
|
||||
:duration="badgeDuration"
|
||||
:path="traceEndpoint"
|
||||
:path="jobLogEndpoint"
|
||||
:is-closed="section.isClosed"
|
||||
@toggleLine="handleOnClickCollapsibleLine(section)"
|
||||
/>
|
||||
|
@ -53,10 +53,10 @@ export default {
|
|||
v-if="line.isHeader"
|
||||
:key="line.line.offset"
|
||||
:section="line"
|
||||
:trace-endpoint="traceEndpoint"
|
||||
:job-log-endpoint="jobLogEndpoint"
|
||||
@onClickCollapsibleLine="handleOnClickCollapsibleLine"
|
||||
/>
|
||||
<log-line v-else :key="line.offset" :line="line" :path="traceEndpoint" />
|
||||
<log-line v-else :key="line.offset" :line="line" :path="jobLogEndpoint" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -64,7 +64,7 @@ export default {
|
|||
v-for="line in section.lines"
|
||||
:key="line.offset"
|
||||
:line="line"
|
||||
:path="traceEndpoint"
|
||||
:path="jobLogEndpoint"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -10,10 +10,10 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'traceEndpoint',
|
||||
'trace',
|
||||
'isTraceComplete',
|
||||
'isScrolledToBottomBeforeReceivingTrace',
|
||||
'jobLogEndpoint',
|
||||
'jobLog',
|
||||
'isJobLogComplete',
|
||||
'isScrolledToBottomBeforeReceivingJobLog',
|
||||
]),
|
||||
},
|
||||
updated() {
|
||||
|
@ -39,7 +39,7 @@ export default {
|
|||
* In order to scroll the page down after `v-html` has finished, we need to use setTimeout
|
||||
*/
|
||||
handleScrollDown() {
|
||||
if (this.isScrolledToBottomBeforeReceivingTrace) {
|
||||
if (this.isScrolledToBottomBeforeReceivingJobLog) {
|
||||
setTimeout(() => {
|
||||
this.scrollBottom();
|
||||
}, 0);
|
||||
|
@ -50,18 +50,18 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<code class="job-log d-block" data-qa-selector="job_log_content">
|
||||
<template v-for="(section, index) in trace">
|
||||
<template v-for="(section, index) in jobLog">
|
||||
<collapsible-log-section
|
||||
v-if="section.isHeader"
|
||||
:key="`collapsible-${index}`"
|
||||
:section="section"
|
||||
:trace-endpoint="traceEndpoint"
|
||||
:job-log-endpoint="jobLogEndpoint"
|
||||
@onClickCollapsibleLine="handleOnClickCollapsibleLine"
|
||||
/>
|
||||
<log-line v-else :key="section.offset" :line="section" :path="traceEndpoint" />
|
||||
<log-line v-else :key="section.offset" :line="section" :path="jobLogEndpoint" />
|
||||
</template>
|
||||
|
||||
<div v-if="!isTraceComplete" class="js-log-animation loader-animation pt-3 pl-3">
|
||||
<div v-if="!isJobLogComplete" class="js-log-animation loader-animation pt-3 pl-3">
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
|
|
|
@ -18,16 +18,16 @@ import * as types from './mutation_types';
|
|||
|
||||
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
|
||||
dispatch('setJobEndpoint', endpoint);
|
||||
dispatch('setTraceOptions', {
|
||||
dispatch('setJobLogOptions', {
|
||||
logState,
|
||||
pagePath,
|
||||
});
|
||||
|
||||
return Promise.all([dispatch('fetchJob'), dispatch('fetchTrace')]);
|
||||
return Promise.all([dispatch('fetchJob'), dispatch('fetchJobLog')]);
|
||||
};
|
||||
|
||||
export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
|
||||
export const setTraceOptions = ({ commit }, options) => commit(types.SET_TRACE_OPTIONS, options);
|
||||
export const setJobLogOptions = ({ commit }, options) => commit(types.SET_JOB_LOG_OPTIONS, options);
|
||||
|
||||
export const hideSidebar = ({ commit }) => commit(types.HIDE_SIDEBAR);
|
||||
export const showSidebar = ({ commit }) => commit(types.SHOW_SIDEBAR);
|
||||
|
@ -107,7 +107,7 @@ export const receiveJobError = ({ commit }) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Job's Trace
|
||||
* Job Log
|
||||
*/
|
||||
export const scrollTop = ({ dispatch }) => {
|
||||
scrollUp();
|
||||
|
@ -156,59 +156,62 @@ export const toggleScrollAnimation = ({ commit }, toggle) =>
|
|||
* Responsible to handle automatic scroll
|
||||
*/
|
||||
export const toggleScrollisInBottom = ({ commit }, toggle) => {
|
||||
commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE, toggle);
|
||||
commit(types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG, toggle);
|
||||
};
|
||||
|
||||
export const requestTrace = ({ commit }) => commit(types.REQUEST_TRACE);
|
||||
export const requestJobLog = ({ commit }) => commit(types.REQUEST_JOB_LOG);
|
||||
|
||||
export const fetchTrace = ({ dispatch, state }) =>
|
||||
export const fetchJobLog = ({ dispatch, state }) =>
|
||||
// update trace endpoint once BE compeletes trace re-naming in #340626
|
||||
axios
|
||||
.get(`${state.traceEndpoint}/trace.json`, {
|
||||
params: { state: state.traceState },
|
||||
.get(`${state.jobLogEndpoint}/trace.json`, {
|
||||
params: { state: state.jobLogState },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
dispatch('toggleScrollisInBottom', isScrolledToBottom());
|
||||
dispatch('receiveTraceSuccess', data);
|
||||
dispatch('receiveJobLogSuccess', data);
|
||||
|
||||
if (data.complete) {
|
||||
dispatch('stopPollingTrace');
|
||||
} else if (!state.traceTimeout) {
|
||||
dispatch('startPollingTrace');
|
||||
dispatch('stopPollingJobLog');
|
||||
} else if (!state.jobLogTimeout) {
|
||||
dispatch('startPollingJobLog');
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response.status === httpStatusCodes.FORBIDDEN) {
|
||||
dispatch('receiveTraceUnauthorizedError');
|
||||
dispatch('receiveJobLogUnauthorizedError');
|
||||
} else {
|
||||
reportToSentry('job_actions', e);
|
||||
dispatch('receiveTraceError');
|
||||
dispatch('receiveJobLogError');
|
||||
}
|
||||
});
|
||||
|
||||
export const startPollingTrace = ({ dispatch, commit }) => {
|
||||
const traceTimeout = setTimeout(() => {
|
||||
commit(types.SET_TRACE_TIMEOUT, 0);
|
||||
dispatch('fetchTrace');
|
||||
export const startPollingJobLog = ({ dispatch, commit }) => {
|
||||
const jobLogTimeout = setTimeout(() => {
|
||||
commit(types.SET_JOB_LOG_TIMEOUT, 0);
|
||||
dispatch('fetchJobLog');
|
||||
}, 4000);
|
||||
|
||||
commit(types.SET_TRACE_TIMEOUT, traceTimeout);
|
||||
commit(types.SET_JOB_LOG_TIMEOUT, jobLogTimeout);
|
||||
};
|
||||
|
||||
export const stopPollingTrace = ({ state, commit }) => {
|
||||
clearTimeout(state.traceTimeout);
|
||||
commit(types.SET_TRACE_TIMEOUT, 0);
|
||||
commit(types.STOP_POLLING_TRACE);
|
||||
export const stopPollingJobLog = ({ state, commit }) => {
|
||||
clearTimeout(state.jobLogTimeout);
|
||||
commit(types.SET_JOB_LOG_TIMEOUT, 0);
|
||||
commit(types.STOP_POLLING_JOB_LOG);
|
||||
};
|
||||
|
||||
export const receiveTraceSuccess = ({ commit }, log) => commit(types.RECEIVE_TRACE_SUCCESS, log);
|
||||
export const receiveTraceError = ({ dispatch }) => {
|
||||
dispatch('stopPollingTrace');
|
||||
export const receiveJobLogSuccess = ({ commit }, log) => commit(types.RECEIVE_JOB_LOG_SUCCESS, log);
|
||||
|
||||
export const receiveJobLogError = ({ dispatch }) => {
|
||||
dispatch('stopPollingJobLog');
|
||||
createFlash({
|
||||
message: __('An error occurred while fetching the job log.'),
|
||||
});
|
||||
};
|
||||
export const receiveTraceUnauthorizedError = ({ dispatch }) => {
|
||||
dispatch('stopPollingTrace');
|
||||
|
||||
export const receiveJobLogUnauthorizedError = ({ dispatch }) => {
|
||||
dispatch('stopPollingJobLog');
|
||||
createFlash({
|
||||
message: __('The current user is not authorized to access the job log.'),
|
||||
});
|
||||
|
@ -248,6 +251,7 @@ export const fetchJobsForStage = ({ dispatch }, stage = {}) => {
|
|||
};
|
||||
export const receiveJobsForStageSuccess = ({ commit }, data) =>
|
||||
commit(types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, data);
|
||||
|
||||
export const receiveJobsForStageError = ({ commit }) => {
|
||||
commit(types.RECEIVE_JOBS_FOR_STAGE_ERROR);
|
||||
createFlash({
|
||||
|
|
|
@ -21,11 +21,12 @@ export const shouldRenderTriggeredLabel = (state) => isString(state.job.started)
|
|||
export const hasEnvironment = (state) => !isEmpty(state.job.deployment_status);
|
||||
|
||||
/**
|
||||
* Checks if it the job has trace.
|
||||
* Checks if it the job has a log.
|
||||
* Used to check if it should render the job log or the empty state
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const hasTrace = (state) =>
|
||||
export const hasJobLog = (state) =>
|
||||
// update has_trace once BE compeletes trace re-naming in #340626
|
||||
state.job.has_trace || (!isEmpty(state.job.status) && state.job.status.group === 'running');
|
||||
|
||||
export const emptyStateIllustration = (state) => state?.job?.status?.illustration || {};
|
||||
|
@ -43,7 +44,7 @@ export const shouldRenderSharedRunnerLimitWarning = (state) =>
|
|||
!isEmpty(state.job.runners.quota) &&
|
||||
state.job.runners.quota.used >= state.job.runners.quota.limit;
|
||||
|
||||
export const isScrollingDown = (state) => isScrolledToBottom() && !state.isTraceComplete;
|
||||
export const isScrollingDown = (state) => isScrolledToBottom() && !state.isJobLogComplete;
|
||||
|
||||
export const hasRunnersForProject = (state) =>
|
||||
state?.job?.runners?.available && !state?.job?.runners?.online;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const SET_JOB_ENDPOINT = 'SET_JOB_ENDPOINT';
|
||||
export const SET_TRACE_OPTIONS = 'SET_TRACE_OPTIONS';
|
||||
export const SET_JOB_LOG_OPTIONS = 'SET_JOB_LOG_OPTIONS';
|
||||
|
||||
export const HIDE_SIDEBAR = 'HIDE_SIDEBAR';
|
||||
export const SHOW_SIDEBAR = 'SHOW_SIDEBAR';
|
||||
|
@ -12,17 +12,17 @@ export const ENABLE_SCROLL_BOTTOM = 'ENABLE_SCROLL_BOTTOM';
|
|||
export const ENABLE_SCROLL_TOP = 'ENABLE_SCROLL_TOP';
|
||||
export const TOGGLE_SCROLL_ANIMATION = 'TOGGLE_SCROLL_ANIMATION';
|
||||
|
||||
export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
|
||||
export const TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG = 'TOGGLE_IS_SCROLL_IN_BOTTOM';
|
||||
|
||||
export const REQUEST_JOB = 'REQUEST_JOB';
|
||||
export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS';
|
||||
export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR';
|
||||
|
||||
export const REQUEST_TRACE = 'REQUEST_TRACE';
|
||||
export const SET_TRACE_TIMEOUT = 'SET_TRACE_TIMEOUT';
|
||||
export const STOP_POLLING_TRACE = 'STOP_POLLING_TRACE';
|
||||
export const RECEIVE_TRACE_SUCCESS = 'RECEIVE_TRACE_SUCCESS';
|
||||
export const RECEIVE_TRACE_ERROR = 'RECEIVE_TRACE_ERROR';
|
||||
export const REQUEST_JOB_LOG = 'REQUEST_JOB_LOG';
|
||||
export const SET_JOB_LOG_TIMEOUT = 'SET_JOB_LOG_TIMEOUT';
|
||||
export const STOP_POLLING_JOB_LOG = 'STOP_POLLING_JOB_LOG';
|
||||
export const RECEIVE_JOB_LOG_SUCCESS = 'RECEIVE_JOB_LOG_SUCCESS';
|
||||
export const RECEIVE_JOB_LOG_ERROR = 'RECEIVE_JOB_LOG_ERROR';
|
||||
export const TOGGLE_COLLAPSIBLE_LINE = 'TOGGLE_COLLAPSIBLE_LINE';
|
||||
|
||||
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import Vue from 'vue';
|
||||
import { INFINITELY_NESTED_COLLAPSIBLE_SECTIONS_FF } from '../constants';
|
||||
import * as types from './mutation_types';
|
||||
import { logLinesParser, logLinesParserLegacy, updateIncrementalTrace } from './utils';
|
||||
import { logLinesParser, logLinesParserLegacy, updateIncrementalJobLog } from './utils';
|
||||
|
||||
export default {
|
||||
[types.SET_JOB_ENDPOINT](state, endpoint) {
|
||||
state.jobEndpoint = endpoint;
|
||||
},
|
||||
|
||||
[types.SET_TRACE_OPTIONS](state, options = {}) {
|
||||
state.traceEndpoint = options.pagePath;
|
||||
state.traceState = options.logState;
|
||||
[types.SET_JOB_LOG_OPTIONS](state, options = {}) {
|
||||
state.jobLogEndpoint = options.pagePath;
|
||||
state.jobLogState = options.logState;
|
||||
},
|
||||
|
||||
[types.HIDE_SIDEBAR](state) {
|
||||
|
@ -20,11 +20,11 @@ export default {
|
|||
state.isSidebarOpen = true;
|
||||
},
|
||||
|
||||
[types.RECEIVE_TRACE_SUCCESS](state, log = {}) {
|
||||
[types.RECEIVE_JOB_LOG_SUCCESS](state, log = {}) {
|
||||
const infinitelyCollapsibleSectionsFlag =
|
||||
gon.features?.[INFINITELY_NESTED_COLLAPSIBLE_SECTIONS_FF];
|
||||
if (log.state) {
|
||||
state.traceState = log.state;
|
||||
state.jobLogState = log.state;
|
||||
}
|
||||
|
||||
if (log.append) {
|
||||
|
@ -32,52 +32,52 @@ export default {
|
|||
if (log.lines) {
|
||||
const parsedResult = logLinesParser(
|
||||
log.lines,
|
||||
state.auxiliaryPartialTraceHelpers,
|
||||
state.trace,
|
||||
state.auxiliaryPartialJobLogHelpers,
|
||||
state.jobLog,
|
||||
);
|
||||
state.trace = parsedResult.parsedLines;
|
||||
state.auxiliaryPartialTraceHelpers = parsedResult.auxiliaryPartialTraceHelpers;
|
||||
state.jobLog = parsedResult.parsedLines;
|
||||
state.auxiliaryPartialJobLogHelpers = parsedResult.auxiliaryPartialJobLogHelpers;
|
||||
}
|
||||
} else {
|
||||
state.trace = log.lines ? updateIncrementalTrace(log.lines, state.trace) : state.trace;
|
||||
state.jobLog = log.lines ? updateIncrementalJobLog(log.lines, state.jobLog) : state.jobLog;
|
||||
}
|
||||
|
||||
state.traceSize += log.size;
|
||||
state.jobLogSize += log.size;
|
||||
} else {
|
||||
// When the job still does not have a trace
|
||||
// the trace response will not have a defined
|
||||
// When the job still does not have a log
|
||||
// the job log response will not have a defined
|
||||
// html or size. We keep the old value otherwise these
|
||||
// will be set to `null`
|
||||
|
||||
if (infinitelyCollapsibleSectionsFlag) {
|
||||
const parsedResult = logLinesParser(log.lines);
|
||||
state.trace = parsedResult.parsedLines;
|
||||
state.auxiliaryPartialTraceHelpers = parsedResult.auxiliaryPartialTraceHelpers;
|
||||
state.jobLog = parsedResult.parsedLines;
|
||||
state.auxiliaryPartialJobLogHelpers = parsedResult.auxiliaryPartialJobLogHelpers;
|
||||
} else {
|
||||
state.trace = log.lines ? logLinesParserLegacy(log.lines) : state.trace;
|
||||
state.jobLog = log.lines ? logLinesParserLegacy(log.lines) : state.jobLog;
|
||||
}
|
||||
|
||||
state.traceSize = log.size || state.traceSize;
|
||||
state.jobLogSize = log.size || state.jobLogSize;
|
||||
}
|
||||
|
||||
if (state.traceSize < log.total) {
|
||||
state.isTraceSizeVisible = true;
|
||||
if (state.jobLogSize < log.total) {
|
||||
state.isJobLogSizeVisible = true;
|
||||
} else {
|
||||
state.isTraceSizeVisible = false;
|
||||
state.isJobLogSizeVisible = false;
|
||||
}
|
||||
|
||||
state.isTraceComplete = log.complete || state.isTraceComplete;
|
||||
state.isJobLogComplete = log.complete || state.isJobLogComplete;
|
||||
},
|
||||
|
||||
[types.SET_TRACE_TIMEOUT](state, id) {
|
||||
state.traceTimeout = id;
|
||||
[types.SET_JOB_LOG_TIMEOUT](state, id) {
|
||||
state.jobLogTimeout = id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Will remove loading animation
|
||||
*/
|
||||
[types.STOP_POLLING_TRACE](state) {
|
||||
state.isTraceComplete = true;
|
||||
[types.STOP_POLLING_JOB_LOG](state) {
|
||||
state.isJobLogComplete = true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -137,8 +137,8 @@ export default {
|
|||
state.isScrollingDown = toggle;
|
||||
},
|
||||
|
||||
[types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_TRACE](state, toggle) {
|
||||
state.isScrolledToBottomBeforeReceivingTrace = toggle;
|
||||
[types.TOGGLE_IS_SCROLL_IN_BOTTOM_BEFORE_UPDATING_JOB_LOG](state, toggle) {
|
||||
state.isScrolledToBottomBeforeReceivingJobLog = toggle;
|
||||
},
|
||||
|
||||
[types.REQUEST_JOBS_FOR_STAGE](state, stage = {}) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export default () => ({
|
||||
jobEndpoint: null,
|
||||
traceEndpoint: null,
|
||||
jobLogEndpoint: null,
|
||||
|
||||
// sidebar
|
||||
isSidebarOpen: true,
|
||||
|
@ -14,16 +14,16 @@ export default () => ({
|
|||
isScrollTopDisabled: true,
|
||||
|
||||
// Used to check if we should keep the automatic scroll
|
||||
isScrolledToBottomBeforeReceivingTrace: true,
|
||||
isScrolledToBottomBeforeReceivingJobLog: true,
|
||||
|
||||
trace: [],
|
||||
isTraceComplete: false,
|
||||
traceSize: 0,
|
||||
isTraceSizeVisible: false,
|
||||
traceTimeout: 0,
|
||||
jobLog: [],
|
||||
isJobLogComplete: false,
|
||||
jobLogSize: 0,
|
||||
isJobLogSizeVisible: false,
|
||||
jobLogTimeout: 0,
|
||||
|
||||
// used as a query parameter to fetch the trace
|
||||
traceState: null,
|
||||
// used as a query parameter to fetch the job log
|
||||
jobLogState: null,
|
||||
|
||||
// sidebar dropdown & list of jobs
|
||||
isLoadingJobs: false,
|
||||
|
@ -32,5 +32,5 @@ export default () => ({
|
|||
jobs: [],
|
||||
|
||||
// to parse partial logs
|
||||
auxiliaryPartialTraceHelpers: {},
|
||||
auxiliaryPartialJobLogHelpers: {},
|
||||
});
|
||||
|
|
|
@ -131,17 +131,17 @@ export const logLinesParserLegacy = (lines = [], accumulator = []) =>
|
|||
[...accumulator],
|
||||
);
|
||||
|
||||
export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLines = []) => {
|
||||
let currentLineCount = previousTraceState?.prevLineCount ?? 0;
|
||||
let currentHeader = previousTraceState?.currentHeader;
|
||||
let isPreviousLineHeader = previousTraceState?.isPreviousLineHeader ?? false;
|
||||
export const logLinesParser = (lines = [], previousJobLogState = {}, prevParsedLines = []) => {
|
||||
let currentLineCount = previousJobLogState?.prevLineCount ?? 0;
|
||||
let currentHeader = previousJobLogState?.currentHeader;
|
||||
let isPreviousLineHeader = previousJobLogState?.isPreviousLineHeader ?? false;
|
||||
const parsedLines = prevParsedLines.length > 0 ? prevParsedLines : [];
|
||||
const sectionsQueue = previousTraceState?.sectionsQueue ?? [];
|
||||
const sectionsQueue = previousJobLogState?.sectionsQueue ?? [];
|
||||
|
||||
for (let i = 0; i < lines.length; i += 1) {
|
||||
const line = lines[i];
|
||||
// First run we can use the current index, later runs we have to retrieve the last number of lines
|
||||
currentLineCount = previousTraceState?.prevLineCount ? currentLineCount + 1 : i + 1;
|
||||
currentLineCount = previousJobLogState?.prevLineCount ? currentLineCount + 1 : i + 1;
|
||||
|
||||
if (line.section_header && !isPreviousLineHeader) {
|
||||
// If there's no previous line header that means we're at the root of the log
|
||||
|
@ -198,7 +198,7 @@ export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLi
|
|||
|
||||
return {
|
||||
parsedLines,
|
||||
auxiliaryPartialTraceHelpers: {
|
||||
auxiliaryPartialJobLogHelpers: {
|
||||
isPreviousLineHeader,
|
||||
currentHeader,
|
||||
sectionsQueue,
|
||||
|
@ -241,7 +241,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* When the trace is not complete, backend may send the last received line
|
||||
* When the job log is not complete, backend may send the last received line
|
||||
* in the new response.
|
||||
*
|
||||
* We need to check if that is the case by looking for the offset property
|
||||
|
@ -250,7 +250,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => {
|
|||
* @param array oldLog
|
||||
* @param array newLog
|
||||
*/
|
||||
export const updateIncrementalTrace = (newLog = [], oldParsed = []) => {
|
||||
export const updateIncrementalJobLog = (newLog = [], oldParsed = []) => {
|
||||
const parsedLog = findOffsetAndRemove(newLog, oldParsed);
|
||||
|
||||
return logLinesParserLegacy(newLog, parsedLog);
|
||||
|
|
|
@ -31,7 +31,7 @@ export default () => ({
|
|||
},
|
||||
|
||||
/**
|
||||
* Logs including trace
|
||||
* Jobs with logs
|
||||
*/
|
||||
logs: {
|
||||
lines: [],
|
||||
|
|
|
@ -70,7 +70,7 @@ class ProjectStatistics < ApplicationRecord
|
|||
end
|
||||
|
||||
def update_lfs_objects_size
|
||||
self.lfs_objects_size = project.lfs_objects.sum(:size)
|
||||
self.lfs_objects_size = LfsObject.joins(:lfs_objects_projects).where(lfs_objects_projects: { project_id: project.id }).sum(:size)
|
||||
end
|
||||
|
||||
def update_uploads_size
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
module Projects
|
||||
class Topic < ApplicationRecord
|
||||
include Avatarable
|
||||
|
||||
validates :name, presence: true, uniqueness: true, length: { maximum: 255 }
|
||||
validates :description, length: { maximum: 1024 }
|
||||
|
||||
has_many :project_topics, class_name: 'Projects::ProjectTopic'
|
||||
has_many :projects, through: :project_topics
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddAvatarAndDescriptionToTopic < Gitlab::Database::Migration[1.0]
|
||||
# rubocop:disable Migration/AddLimitToTextColumns
|
||||
def up
|
||||
add_column :topics, :avatar, :text
|
||||
add_column :topics, :description, :text
|
||||
end
|
||||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
|
||||
def down
|
||||
remove_column :topics, :avatar
|
||||
remove_column :topics, :description
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTextLimitToTopicsDescriptionAndAvatar < Gitlab::Database::Migration[1.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_text_limit :topics, :description, 1024
|
||||
add_text_limit :topics, :avatar, 255
|
||||
end
|
||||
|
||||
def down
|
||||
remove_text_limit :topics, :avatar
|
||||
remove_text_limit :topics, :description
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
f11b237fab6b4133c73f1d6105d85c8db1548b6d0753b2fd96f613c90a4fa3c1
|
|
@ -0,0 +1 @@
|
|||
e7e9b13874081a7df42d07ccc9a54fb81973210d1c175cd995300f6339d57495
|
|
@ -19593,6 +19593,10 @@ CREATE TABLE topics (
|
|||
name text NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
avatar text,
|
||||
description text,
|
||||
CONSTRAINT check_26753fb43a CHECK ((char_length(avatar) <= 255)),
|
||||
CONSTRAINT check_5d1a07c8c8 CHECK ((char_length(description) <= 1024)),
|
||||
CONSTRAINT check_7a90d4c757 CHECK ((char_length(name) <= 255))
|
||||
);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ LDAP service that can be configured with GitLab for authentication and group syn
|
|||
Secure LDAP requires a slightly different configuration than standard LDAP servers.
|
||||
The steps below cover:
|
||||
|
||||
- Configuring the Secure LDAP Client in the Google Admin console.
|
||||
- Configuring the Secure LDAP Client in the Google administrator console.
|
||||
- Required GitLab configuration.
|
||||
|
||||
## Configuring Google LDAP client
|
||||
|
|
|
@ -243,7 +243,7 @@ To mirror-push branches and tags only, and avoid attempting to mirror-push prote
|
|||
git push origin +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*
|
||||
```
|
||||
|
||||
Any other namespaces that the admin wants to push can be included there as well via additional patterns.
|
||||
Any other namespaces that the administrator wants to push can be included there as well via additional patterns.
|
||||
|
||||
### Command line tools cannot connect to Gitaly
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ To authenticate to the Helm repository, you need either:
|
|||
|
||||
## Publish a package
|
||||
|
||||
WARNING:
|
||||
The `helm-push` command is broken in Helm 3.7. For more information, see the [open issue](https://github.com/chartmuseum/helm-push/issues/109)
|
||||
in the Chart Museum project.
|
||||
|
||||
NOTE:
|
||||
You can publish Helm charts with duplicate names or versions. If duplicates exist, GitLab always
|
||||
returns the chart with the latest version.
|
||||
|
@ -123,3 +127,15 @@ Check the [Sidekiq log](../../../administration/logs.md#sidekiqlog)
|
|||
for any related errors. If you see `Validation failed: Version is invalid`, it means that the
|
||||
version in your `Chart.yaml` file does not follow [Helm Chart versioning specifications](https://helm.sh/docs/topics/charts/#charts-and-versioning).
|
||||
To fix the error, use the correct version syntax and upload the chart again.
|
||||
|
||||
### `helm push` results in an error
|
||||
|
||||
The `helm push` plugin is not yet supported in Helm 3.7. If you try to push a chart using
|
||||
`helm push`, it produces the following error:
|
||||
|
||||
```plaintext
|
||||
Error: this feature has been marked as experimental and is not enabled by default. Please set HELM_EXPERIMENTAL_OCI=1 in your environment to use this feature
|
||||
```
|
||||
|
||||
To continue to use the plugin, you can push an image using [curl](#use-cicd-to-publish-a-helm-package)
|
||||
or downgrade your version of Helm.
|
||||
|
|
|
@ -47,9 +47,9 @@ describe('Job App', () => {
|
|||
wrapper = mount(JobApp, { propsData: { ...props }, store });
|
||||
};
|
||||
|
||||
const setupAndMount = ({ jobData = {}, traceData = {} } = {}) => {
|
||||
const setupAndMount = ({ jobData = {}, jobLogData = {} } = {}) => {
|
||||
mock.onGet(initSettings.endpoint).replyOnce(200, { ...job, ...jobData });
|
||||
mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, traceData);
|
||||
mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, jobLogData);
|
||||
|
||||
const asyncInit = store.dispatch('init', initSettings);
|
||||
|
||||
|
@ -77,11 +77,10 @@ describe('Job App', () => {
|
|||
const findEmptyState = () => wrapper.find(EmptyState);
|
||||
const findJobNewIssueLink = () => wrapper.find('[data-testid="job-new-issue"]');
|
||||
const findJobEmptyStateTitle = () => wrapper.find('[data-testid="job-empty-state-title"]');
|
||||
const findJobTraceScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
|
||||
const findJobTraceScrollBottom = () =>
|
||||
wrapper.find('[data-testid="job-controller-scroll-bottom"]');
|
||||
const findJobTraceController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
|
||||
const findJobTraceEraseLink = () => wrapper.find('[data-testid="job-log-erase-link"]');
|
||||
const findJobLogScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
|
||||
const findJobLogScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
|
||||
const findJobLogController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
|
||||
const findJobLogEraseLink = () => wrapper.find('[data-testid="job-log-erase-link"]');
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
|
@ -315,7 +314,7 @@ describe('Job App', () => {
|
|||
});
|
||||
|
||||
describe('empty states block', () => {
|
||||
it('renders empty state when job does not have trace and is not running', () =>
|
||||
it('renders empty state when job does not have log and is not running', () =>
|
||||
setupAndMount({
|
||||
jobData: {
|
||||
has_trace: false,
|
||||
|
@ -342,7 +341,7 @@ describe('Job App', () => {
|
|||
expect(findEmptyState().exists()).toBe(true);
|
||||
}));
|
||||
|
||||
it('does not render empty state when job does not have trace but it is running', () =>
|
||||
it('does not render empty state when job does not have log but it is running', () =>
|
||||
setupAndMount({
|
||||
jobData: {
|
||||
has_trace: false,
|
||||
|
@ -358,7 +357,7 @@ describe('Job App', () => {
|
|||
expect(findEmptyState().exists()).toBe(false);
|
||||
}));
|
||||
|
||||
it('does not render empty state when job has trace but it is not running', () =>
|
||||
it('does not render empty state when job has log but it is not running', () =>
|
||||
setupAndMount({ jobData: { has_trace: true } }).then(() => {
|
||||
expect(findEmptyState().exists()).toBe(false);
|
||||
}));
|
||||
|
@ -424,10 +423,10 @@ describe('Job App', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('trace controls', () => {
|
||||
describe('job log controls', () => {
|
||||
beforeEach(() =>
|
||||
setupAndMount({
|
||||
traceData: {
|
||||
jobLogData: {
|
||||
html: '<span>Update</span>',
|
||||
status: 'success',
|
||||
append: false,
|
||||
|
@ -439,16 +438,16 @@ describe('Job App', () => {
|
|||
);
|
||||
|
||||
it('should render scroll buttons', () => {
|
||||
expect(findJobTraceScrollTop().exists()).toBe(true);
|
||||
expect(findJobTraceScrollBottom().exists()).toBe(true);
|
||||
expect(findJobLogScrollTop().exists()).toBe(true);
|
||||
expect(findJobLogScrollBottom().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should render link to raw ouput', () => {
|
||||
expect(findJobTraceController().exists()).toBe(true);
|
||||
expect(findJobLogController().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should render link to erase job', () => {
|
||||
expect(findJobTraceEraseLink().exists()).toBe(true);
|
||||
expect(findJobLogEraseLink().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ describe('Job log controllers', () => {
|
|||
isScrollTopDisabled: false,
|
||||
isScrollBottomDisabled: false,
|
||||
isScrollingDown: true,
|
||||
isTraceSizeVisible: true,
|
||||
isJobLogSizeVisible: true,
|
||||
};
|
||||
|
||||
const createWrapper = (props) => {
|
||||
|
@ -38,7 +38,7 @@ describe('Job log controllers', () => {
|
|||
const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
|
||||
|
||||
describe('Truncate information', () => {
|
||||
describe('with isTraceSizeVisible', () => {
|
||||
describe('with isJobLogSizeVisible', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
|
@ -47,31 +47,31 @@ describe('Job log controllers', () => {
|
|||
expect(findTruncatedInfo().text()).toMatch('499.95 KiB');
|
||||
});
|
||||
|
||||
it('renders link to raw trace', () => {
|
||||
it('renders link to raw job log', () => {
|
||||
expect(findRawLink().attributes('href')).toBe(defaultProps.rawPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('links section', () => {
|
||||
describe('with raw trace path', () => {
|
||||
describe('with raw job log path', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
|
||||
it('renders raw trace link', () => {
|
||||
it('renders raw job log link', () => {
|
||||
expect(findRawLinkController().attributes('href')).toBe(defaultProps.rawPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('without raw trace path', () => {
|
||||
describe('without raw job log path', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
rawPath: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render raw trace link', () => {
|
||||
it('does not render raw job log link', () => {
|
||||
expect(findRawLinkController().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ describe('Job Log Collapsible Section', () => {
|
|||
let wrapper;
|
||||
let origGon;
|
||||
|
||||
const traceEndpoint = 'jobs/335';
|
||||
const jobLogEndpoint = 'jobs/335';
|
||||
|
||||
const findCollapsibleLine = () => wrapper.find('.collapsible-line');
|
||||
const findCollapsibleLineSvg = () => wrapper.find('.collapsible-line svg');
|
||||
|
@ -35,7 +35,7 @@ describe('Job Log Collapsible Section', () => {
|
|||
beforeEach(() => {
|
||||
createComponent({
|
||||
section: collapsibleSectionClosed,
|
||||
traceEndpoint,
|
||||
jobLogEndpoint,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -52,7 +52,7 @@ describe('Job Log Collapsible Section', () => {
|
|||
beforeEach(() => {
|
||||
createComponent({
|
||||
section: collapsibleSectionOpened,
|
||||
traceEndpoint,
|
||||
jobLogEndpoint,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -72,7 +72,7 @@ describe('Job Log Collapsible Section', () => {
|
|||
it('emits onClickCollapsibleLine on click', () => {
|
||||
createComponent({
|
||||
section: collapsibleSectionOpened,
|
||||
traceEndpoint,
|
||||
jobLogEndpoint,
|
||||
});
|
||||
|
||||
findCollapsibleLine().trigger('click');
|
||||
|
|
|
@ -31,8 +31,8 @@ describe('Job Log', () => {
|
|||
window.gon = { features: { infinitelyCollapsibleSections: false } };
|
||||
|
||||
state = {
|
||||
trace: logLinesParserLegacy(jobLog),
|
||||
traceEndpoint: 'jobs/id',
|
||||
jobLog: logLinesParserLegacy(jobLog),
|
||||
jobLogEndpoint: 'jobs/id',
|
||||
};
|
||||
|
||||
store = new Vuex.Store({
|
||||
|
@ -59,7 +59,7 @@ describe('Job Log', () => {
|
|||
});
|
||||
|
||||
it('links to the provided path and correct line number', () => {
|
||||
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.traceEndpoint}#L1`);
|
||||
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.jobLogEndpoint}#L1`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -111,8 +111,8 @@ describe('Job Log, infinitelyCollapsibleSections feature flag enabled', () => {
|
|||
window.gon = { features: { infinitelyCollapsibleSections: true } };
|
||||
|
||||
state = {
|
||||
trace: logLinesParser(jobLog).parsedLines,
|
||||
traceEndpoint: 'jobs/id',
|
||||
jobLog: logLinesParser(jobLog).parsedLines,
|
||||
jobLogEndpoint: 'jobs/id',
|
||||
};
|
||||
|
||||
store = new Vuex.Store({
|
||||
|
@ -139,7 +139,7 @@ describe('Job Log, infinitelyCollapsibleSections feature flag enabled', () => {
|
|||
});
|
||||
|
||||
it('links to the provided path and correct line number', () => {
|
||||
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.traceEndpoint}#L1`);
|
||||
expect(wrapper.find('#L1').attributes('href')).toBe(`${state.jobLogEndpoint}#L1`);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { TEST_HOST } from 'helpers/test_constants';
|
|||
import testAction from 'helpers/vuex_action_helper';
|
||||
import {
|
||||
setJobEndpoint,
|
||||
setTraceOptions,
|
||||
setJobLogOptions,
|
||||
clearEtagPoll,
|
||||
stopPolling,
|
||||
requestJob,
|
||||
|
@ -12,12 +12,12 @@ import {
|
|||
receiveJobError,
|
||||
scrollTop,
|
||||
scrollBottom,
|
||||
requestTrace,
|
||||
fetchTrace,
|
||||
startPollingTrace,
|
||||
stopPollingTrace,
|
||||
receiveTraceSuccess,
|
||||
receiveTraceError,
|
||||
requestJobLog,
|
||||
fetchJobLog,
|
||||
startPollingJobLog,
|
||||
stopPollingJobLog,
|
||||
receiveJobLogSuccess,
|
||||
receiveJobLogError,
|
||||
toggleCollapsibleLine,
|
||||
requestJobsForStage,
|
||||
fetchJobsForStage,
|
||||
|
@ -51,13 +51,13 @@ describe('Job State actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setTraceOptions', () => {
|
||||
it('should commit SET_TRACE_OPTIONS mutation', (done) => {
|
||||
describe('setJobLogOptions', () => {
|
||||
it('should commit SET_JOB_LOG_OPTIONS mutation', (done) => {
|
||||
testAction(
|
||||
setTraceOptions,
|
||||
setJobLogOptions,
|
||||
{ pagePath: 'job/872324/trace.json' },
|
||||
mockedState,
|
||||
[{ type: types.SET_TRACE_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }],
|
||||
[{ type: types.SET_JOB_LOG_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
|
@ -191,17 +191,17 @@ describe('Job State actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('requestTrace', () => {
|
||||
it('should commit REQUEST_TRACE mutation', (done) => {
|
||||
testAction(requestTrace, null, mockedState, [{ type: types.REQUEST_TRACE }], [], done);
|
||||
describe('requestJobLog', () => {
|
||||
it('should commit REQUEST_JOB_LOG mutation', (done) => {
|
||||
testAction(requestJobLog, null, mockedState, [{ type: types.REQUEST_JOB_LOG }], [], done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchTrace', () => {
|
||||
describe('fetchJobLog', () => {
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockedState.traceEndpoint = `${TEST_HOST}/endpoint`;
|
||||
mockedState.jobLogEndpoint = `${TEST_HOST}/endpoint`;
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
|
@ -212,14 +212,14 @@ describe('Job State actions', () => {
|
|||
});
|
||||
|
||||
describe('success', () => {
|
||||
it('dispatches requestTrace, receiveTraceSuccess and stopPollingTrace when job is complete', (done) => {
|
||||
it('dispatches requestJobLog, receiveJobLogSuccess and stopPollingJobLog when job is complete', (done) => {
|
||||
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, {
|
||||
html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
|
||||
complete: true,
|
||||
});
|
||||
|
||||
testAction(
|
||||
fetchTrace,
|
||||
fetchJobLog,
|
||||
null,
|
||||
mockedState,
|
||||
[],
|
||||
|
@ -233,10 +233,10 @@ describe('Job State actions', () => {
|
|||
html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
|
||||
complete: true,
|
||||
},
|
||||
type: 'receiveTraceSuccess',
|
||||
type: 'receiveJobLogSuccess',
|
||||
},
|
||||
{
|
||||
type: 'stopPollingTrace',
|
||||
type: 'stopPollingJobLog',
|
||||
},
|
||||
],
|
||||
done,
|
||||
|
@ -244,43 +244,43 @@ describe('Job State actions', () => {
|
|||
});
|
||||
|
||||
describe('when job is incomplete', () => {
|
||||
let tracePayload;
|
||||
let jobLogPayload;
|
||||
|
||||
beforeEach(() => {
|
||||
tracePayload = {
|
||||
jobLogPayload = {
|
||||
html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
|
||||
complete: false,
|
||||
};
|
||||
|
||||
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, tracePayload);
|
||||
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, jobLogPayload);
|
||||
});
|
||||
|
||||
it('dispatches startPollingTrace', (done) => {
|
||||
it('dispatches startPollingJobLog', (done) => {
|
||||
testAction(
|
||||
fetchTrace,
|
||||
fetchJobLog,
|
||||
null,
|
||||
mockedState,
|
||||
[],
|
||||
[
|
||||
{ type: 'toggleScrollisInBottom', payload: true },
|
||||
{ type: 'receiveTraceSuccess', payload: tracePayload },
|
||||
{ type: 'startPollingTrace' },
|
||||
{ type: 'receiveJobLogSuccess', payload: jobLogPayload },
|
||||
{ type: 'startPollingJobLog' },
|
||||
],
|
||||
done,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not dispatch startPollingTrace when timeout is non-empty', (done) => {
|
||||
mockedState.traceTimeout = 1;
|
||||
it('does not dispatch startPollingJobLog when timeout is non-empty', (done) => {
|
||||
mockedState.jobLogTimeout = 1;
|
||||
|
||||
testAction(
|
||||
fetchTrace,
|
||||
fetchJobLog,
|
||||
null,
|
||||
mockedState,
|
||||
[],
|
||||
[
|
||||
{ type: 'toggleScrollisInBottom', payload: true },
|
||||
{ type: 'receiveTraceSuccess', payload: tracePayload },
|
||||
{ type: 'receiveJobLogSuccess', payload: jobLogPayload },
|
||||
],
|
||||
done,
|
||||
);
|
||||
|
@ -293,15 +293,15 @@ describe('Job State actions', () => {
|
|||
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).reply(500);
|
||||
});
|
||||
|
||||
it('dispatches requestTrace and receiveTraceError ', (done) => {
|
||||
it('dispatches requestJobLog and receiveJobLogError ', (done) => {
|
||||
testAction(
|
||||
fetchTrace,
|
||||
fetchJobLog,
|
||||
null,
|
||||
mockedState,
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'receiveTraceError',
|
||||
type: 'receiveJobLogError',
|
||||
},
|
||||
],
|
||||
done,
|
||||
|
@ -310,7 +310,7 @@ describe('Job State actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('startPollingTrace', () => {
|
||||
describe('startPollingJobLog', () => {
|
||||
let dispatch;
|
||||
let commit;
|
||||
|
||||
|
@ -318,18 +318,18 @@ describe('Job State actions', () => {
|
|||
dispatch = jest.fn();
|
||||
commit = jest.fn();
|
||||
|
||||
startPollingTrace({ dispatch, commit });
|
||||
startPollingJobLog({ dispatch, commit });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
it('should save the timeout id but not call fetchTrace', () => {
|
||||
expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, expect.any(Number));
|
||||
it('should save the timeout id but not call fetchJobLog', () => {
|
||||
expect(commit).toHaveBeenCalledWith(types.SET_JOB_LOG_TIMEOUT, expect.any(Number));
|
||||
expect(commit.mock.calls[0][1]).toBeGreaterThan(0);
|
||||
|
||||
expect(dispatch).not.toHaveBeenCalledWith('fetchTrace');
|
||||
expect(dispatch).not.toHaveBeenCalledWith('fetchJobLog');
|
||||
});
|
||||
|
||||
describe('after timeout has passed', () => {
|
||||
|
@ -337,14 +337,14 @@ describe('Job State actions', () => {
|
|||
jest.advanceTimersByTime(4000);
|
||||
});
|
||||
|
||||
it('should clear the timeout id and fetchTrace', () => {
|
||||
expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, 0);
|
||||
expect(dispatch).toHaveBeenCalledWith('fetchTrace');
|
||||
it('should clear the timeout id and fetchJobLog', () => {
|
||||
expect(commit).toHaveBeenCalledWith(types.SET_JOB_LOG_TIMEOUT, 0);
|
||||
expect(dispatch).toHaveBeenCalledWith('fetchJobLog');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('stopPollingTrace', () => {
|
||||
describe('stopPollingJobLog', () => {
|
||||
let origTimeout;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -358,40 +358,40 @@ describe('Job State actions', () => {
|
|||
window.clearTimeout = origTimeout;
|
||||
});
|
||||
|
||||
it('should commit STOP_POLLING_TRACE mutation ', (done) => {
|
||||
const traceTimeout = 7;
|
||||
it('should commit STOP_POLLING_JOB_LOG mutation ', (done) => {
|
||||
const jobLogTimeout = 7;
|
||||
|
||||
testAction(
|
||||
stopPollingTrace,
|
||||
stopPollingJobLog,
|
||||
null,
|
||||
{ ...mockedState, traceTimeout },
|
||||
[{ type: types.SET_TRACE_TIMEOUT, payload: 0 }, { type: types.STOP_POLLING_TRACE }],
|
||||
{ ...mockedState, jobLogTimeout },
|
||||
[{ type: types.SET_JOB_LOG_TIMEOUT, payload: 0 }, { type: types.STOP_POLLING_JOB_LOG }],
|
||||
[],
|
||||
)
|
||||
.then(() => {
|
||||
expect(window.clearTimeout).toHaveBeenCalledWith(traceTimeout);
|
||||
expect(window.clearTimeout).toHaveBeenCalledWith(jobLogTimeout);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveTraceSuccess', () => {
|
||||
it('should commit RECEIVE_TRACE_SUCCESS mutation ', (done) => {
|
||||
describe('receiveJobLogSuccess', () => {
|
||||
it('should commit RECEIVE_JOB_LOG_SUCCESS mutation ', (done) => {
|
||||
testAction(
|
||||
receiveTraceSuccess,
|
||||
receiveJobLogSuccess,
|
||||
'hello world',
|
||||
mockedState,
|
||||
[{ type: types.RECEIVE_TRACE_SUCCESS, payload: 'hello world' }],
|
||||
[{ type: types.RECEIVE_JOB_LOG_SUCCESS, payload: 'hello world' }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveTraceError', () => {
|
||||
it('should commit stop polling trace', (done) => {
|
||||
testAction(receiveTraceError, null, mockedState, [], [{ type: 'stopPollingTrace' }], done);
|
||||
describe('receiveJobLogError', () => {
|
||||
it('should commit stop polling job log', (done) => {
|
||||
testAction(receiveJobLogError, null, mockedState, [], [{ type: 'stopPollingJobLog' }], done);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -102,13 +102,13 @@ describe('Job Store Getters', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('hasTrace', () => {
|
||||
describe('hasJobLog', () => {
|
||||
describe('when has_trace is true', () => {
|
||||
it('returns true', () => {
|
||||
localState.job.has_trace = true;
|
||||
localState.job.status = {};
|
||||
|
||||
expect(getters.hasTrace(localState)).toEqual(true);
|
||||
expect(getters.hasJobLog(localState)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -117,7 +117,7 @@ describe('Job Store Getters', () => {
|
|||
localState.job.has_trace = false;
|
||||
localState.job.status = { group: 'running' };
|
||||
|
||||
expect(getters.hasTrace(localState)).toEqual(true);
|
||||
expect(getters.hasJobLog(localState)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -126,7 +126,7 @@ describe('Job Store Getters', () => {
|
|||
localState.job.has_trace = false;
|
||||
localState.job.status = { group: 'pending' };
|
||||
|
||||
expect(getters.hasTrace(localState)).toEqual(false);
|
||||
expect(getters.hasJobLog(localState)).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,39 +45,39 @@ describe('Jobs Store Mutations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_TRACE_SUCCESS', () => {
|
||||
describe('when trace has state', () => {
|
||||
it('sets traceState', () => {
|
||||
describe('RECEIVE_JOB_LOG_SUCCESS', () => {
|
||||
describe('when job log has state', () => {
|
||||
it('sets jobLogState', () => {
|
||||
const stateLog =
|
||||
'eyJvZmZzZXQiOjczNDQ1MSwibl9vcGVuX3RhZ3MiOjAsImZnX2NvbG9yIjpudWxsLCJiZ19jb2xvciI6bnVsbCwic3R5bGVfbWFzayI6MH0=';
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
state: stateLog,
|
||||
});
|
||||
|
||||
expect(stateCopy.traceState).toEqual(stateLog);
|
||||
expect(stateCopy.jobLogState).toEqual(stateLog);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when traceSize is smaller than the total size', () => {
|
||||
it('sets isTraceSizeVisible to true', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, { total: 51184600, size: 1231 });
|
||||
describe('when jobLogSize is smaller than the total size', () => {
|
||||
it('sets isJobLogSizeVisible to true', () => {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, { total: 51184600, size: 1231 });
|
||||
|
||||
expect(stateCopy.isTraceSizeVisible).toEqual(true);
|
||||
expect(stateCopy.isJobLogSizeVisible).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when traceSize is bigger than the total size', () => {
|
||||
it('sets isTraceSizeVisible to false', () => {
|
||||
const copy = { ...stateCopy, traceSize: 5118460, size: 2321312 };
|
||||
describe('when jobLogSize is bigger than the total size', () => {
|
||||
it('sets isJobLogSizeVisible to false', () => {
|
||||
const copy = { ...stateCopy, jobLogSize: 5118460, size: 2321312 };
|
||||
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](copy, { total: 511846 });
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](copy, { total: 511846 });
|
||||
|
||||
expect(copy.isTraceSizeVisible).toEqual(false);
|
||||
expect(copy.isJobLogSizeVisible).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets trace, trace size and isTraceComplete', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
it('sets job log size and isJobLogComplete', () => {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: true,
|
||||
html,
|
||||
size: 511846,
|
||||
|
@ -85,15 +85,15 @@ describe('Jobs Store Mutations', () => {
|
|||
lines: [],
|
||||
});
|
||||
|
||||
expect(stateCopy.traceSize).toEqual(511846);
|
||||
expect(stateCopy.isTraceComplete).toEqual(true);
|
||||
expect(stateCopy.jobLogSize).toEqual(511846);
|
||||
expect(stateCopy.isJobLogComplete).toEqual(true);
|
||||
});
|
||||
|
||||
describe('with new job log', () => {
|
||||
describe('log.lines', () => {
|
||||
describe('when append is true', () => {
|
||||
it('sets the parsed log ', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: true,
|
||||
size: 511846,
|
||||
complete: true,
|
||||
|
@ -105,7 +105,7 @@ describe('Jobs Store Mutations', () => {
|
|||
],
|
||||
});
|
||||
|
||||
expect(stateCopy.trace).toEqual([
|
||||
expect(stateCopy.jobLog).toEqual([
|
||||
{
|
||||
offset: 1,
|
||||
content: [{ text: 'Running with gitlab-runner 11.12.1 (5a147c92)' }],
|
||||
|
@ -117,7 +117,7 @@ describe('Jobs Store Mutations', () => {
|
|||
|
||||
describe('when it is defined', () => {
|
||||
it('sets the parsed log ', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: false,
|
||||
size: 511846,
|
||||
complete: true,
|
||||
|
@ -126,7 +126,7 @@ describe('Jobs Store Mutations', () => {
|
|||
],
|
||||
});
|
||||
|
||||
expect(stateCopy.trace).toEqual([
|
||||
expect(stateCopy.jobLog).toEqual([
|
||||
{
|
||||
offset: 0,
|
||||
content: [{ text: 'Running with gitlab-runner 11.11.1 (5a147c92)' }],
|
||||
|
@ -138,7 +138,7 @@ describe('Jobs Store Mutations', () => {
|
|||
|
||||
describe('when it is null', () => {
|
||||
it('sets the default value', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: true,
|
||||
html,
|
||||
size: 511846,
|
||||
|
@ -146,30 +146,30 @@ describe('Jobs Store Mutations', () => {
|
|||
lines: null,
|
||||
});
|
||||
|
||||
expect(stateCopy.trace).toEqual([]);
|
||||
expect(stateCopy.jobLog).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_TRACE_TIMEOUT', () => {
|
||||
it('sets the traceTimeout id', () => {
|
||||
describe('SET_JOB_LOG_TIMEOUT', () => {
|
||||
it('sets the jobLogTimeout id', () => {
|
||||
const id = 7;
|
||||
|
||||
expect(stateCopy.traceTimeout).not.toEqual(id);
|
||||
expect(stateCopy.jobLogTimeout).not.toEqual(id);
|
||||
|
||||
mutations[types.SET_TRACE_TIMEOUT](stateCopy, id);
|
||||
mutations[types.SET_JOB_LOG_TIMEOUT](stateCopy, id);
|
||||
|
||||
expect(stateCopy.traceTimeout).toEqual(id);
|
||||
expect(stateCopy.jobLogTimeout).toEqual(id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('STOP_POLLING_TRACE', () => {
|
||||
it('sets isTraceComplete to true', () => {
|
||||
mutations[types.STOP_POLLING_TRACE](stateCopy);
|
||||
describe('STOP_POLLING_JOB_LOG', () => {
|
||||
it('sets isJobLogComplete to true', () => {
|
||||
mutations[types.STOP_POLLING_JOB_LOG](stateCopy);
|
||||
|
||||
expect(stateCopy.isTraceComplete).toEqual(true);
|
||||
expect(stateCopy.isJobLogComplete).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -296,12 +296,12 @@ describe('Job Store mutations, feature flag ON', () => {
|
|||
window.gon = origGon;
|
||||
});
|
||||
|
||||
describe('RECEIVE_TRACE_SUCCESS', () => {
|
||||
describe('RECEIVE_JOB_LOG_SUCCESS', () => {
|
||||
describe('with new job log', () => {
|
||||
describe('log.lines', () => {
|
||||
describe('when append is true', () => {
|
||||
it('sets the parsed log ', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: true,
|
||||
size: 511846,
|
||||
complete: true,
|
||||
|
@ -313,7 +313,7 @@ describe('Job Store mutations, feature flag ON', () => {
|
|||
],
|
||||
});
|
||||
|
||||
expect(stateCopy.trace).toEqual([
|
||||
expect(stateCopy.jobLog).toEqual([
|
||||
{
|
||||
offset: 1,
|
||||
content: [{ text: 'Running with gitlab-runner 11.12.1 (5a147c92)' }],
|
||||
|
@ -325,7 +325,7 @@ describe('Job Store mutations, feature flag ON', () => {
|
|||
|
||||
describe('when lines are defined', () => {
|
||||
it('sets the parsed log ', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: false,
|
||||
size: 511846,
|
||||
complete: true,
|
||||
|
@ -334,7 +334,7 @@ describe('Job Store mutations, feature flag ON', () => {
|
|||
],
|
||||
});
|
||||
|
||||
expect(stateCopy.trace).toEqual([
|
||||
expect(stateCopy.jobLog).toEqual([
|
||||
{
|
||||
offset: 0,
|
||||
content: [{ text: 'Running with gitlab-runner 11.11.1 (5a147c92)' }],
|
||||
|
@ -346,7 +346,7 @@ describe('Job Store mutations, feature flag ON', () => {
|
|||
|
||||
describe('when lines are null', () => {
|
||||
it('sets the default value', () => {
|
||||
mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, {
|
||||
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
|
||||
append: true,
|
||||
html,
|
||||
size: 511846,
|
||||
|
@ -354,7 +354,7 @@ describe('Job Store mutations, feature flag ON', () => {
|
|||
lines: null,
|
||||
});
|
||||
|
||||
expect(stateCopy.trace).toEqual([]);
|
||||
expect(stateCopy.jobLog).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
logLinesParser,
|
||||
logLinesParserLegacy,
|
||||
updateIncrementalTrace,
|
||||
updateIncrementalJobLog,
|
||||
parseHeaderLine,
|
||||
parseLine,
|
||||
addDurationToHeader,
|
||||
|
@ -487,11 +487,11 @@ describe('Jobs Store Utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('updateIncrementalTrace', () => {
|
||||
describe('updateIncrementalJobLog', () => {
|
||||
describe('without repeated section', () => {
|
||||
it('concats and parses both arrays', () => {
|
||||
const oldLog = logLinesParserLegacy(originalTrace);
|
||||
const result = updateIncrementalTrace(regularIncremental, oldLog);
|
||||
const result = updateIncrementalJobLog(regularIncremental, oldLog);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
|
@ -519,7 +519,7 @@ describe('Jobs Store Utils', () => {
|
|||
describe('with regular line repeated offset', () => {
|
||||
it('updates the last line and formats with the incremental part', () => {
|
||||
const oldLog = logLinesParserLegacy(originalTrace);
|
||||
const result = updateIncrementalTrace(regularIncrementalRepeated, oldLog);
|
||||
const result = updateIncrementalJobLog(regularIncrementalRepeated, oldLog);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
|
@ -538,7 +538,7 @@ describe('Jobs Store Utils', () => {
|
|||
describe('with header line repeated', () => {
|
||||
it('updates the header line and formats with the incremental part', () => {
|
||||
const oldLog = logLinesParserLegacy(headerTrace);
|
||||
const result = updateIncrementalTrace(headerTraceIncremental, oldLog);
|
||||
const result = updateIncrementalJobLog(headerTraceIncremental, oldLog);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
|
@ -564,7 +564,7 @@ describe('Jobs Store Utils', () => {
|
|||
describe('with collapsible line repeated', () => {
|
||||
it('updates the collapsible line and formats with the incremental part', () => {
|
||||
const oldLog = logLinesParserLegacy(collapsibleTrace);
|
||||
const result = updateIncrementalTrace(collapsibleTraceIncremental, oldLog);
|
||||
const result = updateIncrementalJobLog(collapsibleTraceIncremental, oldLog);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
|
|
|
@ -294,15 +294,17 @@ RSpec.describe ProjectStatistics do
|
|||
describe '#update_lfs_objects_size' do
|
||||
let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) }
|
||||
let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) }
|
||||
let!(:lfs_object3) { create(:lfs_object, size: 34.megabytes) }
|
||||
let!(:lfs_objects_project1) { create(:lfs_objects_project, project: project, lfs_object: lfs_object1) }
|
||||
let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) }
|
||||
let!(:lfs_objects_project3) { create(:lfs_objects_project, project: project, lfs_object: lfs_object3) }
|
||||
|
||||
before do
|
||||
statistics.update_lfs_objects_size
|
||||
end
|
||||
|
||||
it "stores the size of related LFS objects" do
|
||||
expect(statistics.lfs_objects_size).to eq 57.megabytes
|
||||
expect(statistics.lfs_objects_size).to eq 91.megabytes
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,5 +18,6 @@ RSpec.describe Projects::Topic do
|
|||
it { is_expected.to validate_presence_of(:name) }
|
||||
it { is_expected.to validate_uniqueness_of(:name) }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:description).is_at_most(1024) }
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue