Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2019-12-12 09:07:48 +00:00
parent 8e22ef10e4
commit fc53ce8e6c
39 changed files with 203 additions and 48 deletions

2
.nvmrc
View File

@ -1 +1 @@
12.4.0 12.10.0

View File

@ -35,10 +35,10 @@ export default {
title() { title() {
const timeago = getTimeago(); const timeago = getTimeago();
const { timeDifference, standardDateFormat } = this; const { timeDifference, standardDateFormat } = this;
const formatedDate = standardDateFormat; const formattedDate = standardDateFormat;
if (timeDifference >= -1 && timeDifference < 7) { if (timeDifference >= -1 && timeDifference < 7) {
return `${timeago.format(this.issueDueDate)} (${formatedDate})`; return `${timeago.format(this.issueDueDate)} (${formattedDate})`;
} }
return timeago.format(this.issueDueDate); return timeago.format(this.issueDueDate);

View File

@ -159,7 +159,7 @@ export default {
<div role="rowheader" class="table-mobile-header">{{ __('Created') }}</div> <div role="rowheader" class="table-mobile-header">{{ __('Created') }}</div>
<div class="table-mobile-content text-secondary key-created-at"> <div class="table-mobile-content text-secondary key-created-at">
<span v-tooltip :title="tooltipTitle(deployKey.created_at)"> <span v-tooltip :title="tooltipTitle(deployKey.created_at)">
<icon name="calendar" /> <span>{{ timeFormated(deployKey.created_at) }}</span> <icon name="calendar" /> <span>{{ timeFormatted(deployKey.created_at) }}</span>
</span> </span>
</div> </div>
</div> </div>

View File

@ -177,6 +177,7 @@ export default {
projectPath: this.projectPath, projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint, dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover, showSuggestPopover: this.showSuggestPopover,
useSingleDiffStyle: this.glFeatures.singleMrDiffView,
}); });
if (this.shouldShow) { if (this.shouldShow) {

View File

@ -46,6 +46,7 @@ export const setBaseConfig = ({ commit }, options) => {
projectPath, projectPath,
dismissEndpoint, dismissEndpoint,
showSuggestPopover, showSuggestPopover,
useSingleDiffStyle,
} = options; } = options;
commit(types.SET_BASE_CONFIG, { commit(types.SET_BASE_CONFIG, {
endpoint, endpoint,
@ -54,11 +55,15 @@ export const setBaseConfig = ({ commit }, options) => {
projectPath, projectPath,
dismissEndpoint, dismissEndpoint,
showSuggestPopover, showSuggestPopover,
useSingleDiffStyle,
}); });
}; };
export const fetchDiffFiles = ({ state, commit }) => { export const fetchDiffFiles = ({ state, commit }) => {
const worker = new TreeWorker(); const worker = new TreeWorker();
const urlParams = {
w: state.showWhitespace ? '0' : '1',
};
commit(types.SET_LOADING, true); commit(types.SET_LOADING, true);
@ -69,9 +74,10 @@ export const fetchDiffFiles = ({ state, commit }) => {
}); });
return axios return axios
.get(mergeUrlParams({ w: state.showWhitespace ? '0' : '1' }, state.endpoint)) .get(mergeUrlParams(urlParams, state.endpoint))
.then(res => { .then(res => {
commit(types.SET_LOADING, false); commit(types.SET_LOADING, false);
commit(types.SET_MERGE_REQUEST_DIFFS, res.data.merge_request_diffs || []); commit(types.SET_MERGE_REQUEST_DIFFS, res.data.merge_request_diffs || []);
commit(types.SET_DIFF_DATA, res.data); commit(types.SET_DIFF_DATA, res.data);

View File

@ -31,4 +31,5 @@ export default () => ({
fileFinderVisible: false, fileFinderVisible: false,
dismissEndpoint: '', dismissEndpoint: '',
showSuggestPopover: true, showSuggestPopover: true,
useSingleDiffStyle: false,
}); });

View File

@ -19,6 +19,7 @@ export default {
projectPath, projectPath,
dismissEndpoint, dismissEndpoint,
showSuggestPopover, showSuggestPopover,
useSingleDiffStyle,
} = options; } = options;
Object.assign(state, { Object.assign(state, {
endpoint, endpoint,
@ -27,6 +28,7 @@ export default {
projectPath, projectPath,
dismissEndpoint, dismissEndpoint,
showSuggestPopover, showSuggestPopover,
useSingleDiffStyle,
}); });
}, },

View File

@ -56,7 +56,7 @@ export default {
__('Reported %{timeAgo} by %{reportedBy}'), __('Reported %{timeAgo} by %{reportedBy}'),
{ {
reportedBy: `<strong>${this.error.culprit}</strong>`, reportedBy: `<strong>${this.error.culprit}</strong>`,
timeAgo: this.timeFormated(this.stacktraceData.date_received), timeAgo: this.timeFormatted(this.stacktraceData.date_received),
}, },
false, false,
); );
@ -107,7 +107,7 @@ export default {
this.$refs.sentryIssueForm.submit(); this.$refs.sentryIssueForm.submit();
}, },
formatDate(date) { formatDate(date) {
return `${this.timeFormated(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`; return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
}, },
}, },
}; };

View File

@ -22,7 +22,7 @@ export default {
mixins: [timeAgoMixin], mixins: [timeAgoMixin],
data() { data() {
return { return {
lastCommitFormatedAge: null, lastCommitFormattedAge: null,
}; };
}, },
computed: { computed: {
@ -62,7 +62,7 @@ export default {
}, },
commitAgeUpdate() { commitAgeUpdate() {
if (this.lastCommit) { if (this.lastCommit) {
this.lastCommitFormatedAge = this.timeFormated(this.lastCommit.committed_date); this.lastCommitFormattedAge = this.timeFormatted(this.lastCommit.committed_date);
} }
}, },
getCommitPath(shortSha) { getCommitPath(shortSha) {
@ -118,7 +118,7 @@ export default {
:title="tooltipTitle(lastCommit.committed_date)" :title="tooltipTitle(lastCommit.committed_date)"
data-placement="top" data-placement="top"
data-container="body" data-container="body"
>{{ lastCommitFormatedAge }}</time >{{ lastCommitFormattedAge }}</time
> >
</div> </div>
<ide-status-list class="ml-auto" /> <ide-status-list class="ml-auto" />

View File

@ -91,7 +91,7 @@ export default {
/> />
<gl-tooltip :target="() => $refs.state" placement="bottom"> <gl-tooltip :target="() => $refs.state" placement="bottom">
<span class="d-block"> <span class="d-block">
<span class="bold"> {{ stateTitle }} </span> {{ timeFormated(closedOrCreatedDate) }} <span class="bold"> {{ stateTitle }} </span> {{ timeFormatted(closedOrCreatedDate) }}
</span> </span>
<span class="text-tertiary">{{ tooltipTitle(closedOrCreatedDate) }}</span> <span class="text-tertiary">{{ tooltipTitle(closedOrCreatedDate) }}</span>
</gl-tooltip> </gl-tooltip>

View File

@ -168,13 +168,13 @@ export default {
/> />
<detail-row <detail-row
v-if="job.finished_at" v-if="job.finished_at"
:value="timeFormated(job.finished_at)" :value="timeFormatted(job.finished_at)"
class="js-job-finished" class="js-job-finished"
title="Finished" title="Finished"
/> />
<detail-row <detail-row
v-if="job.erased_at" v-if="job.erased_at"
:value="timeFormated(job.erased_at)" :value="timeFormatted(job.erased_at)"
class="js-job-erased" class="js-job-erased"
title="Erased" title="Erased"
/> />

View File

@ -114,7 +114,7 @@ export const logLinesParser = (lines = [], accumulator = []) =>
acc.push(parseHeaderLine(line, lineNumber)); acc.push(parseHeaderLine(line, lineNumber));
} else if (isCollapsibleSection(acc, last, line)) { } else if (isCollapsibleSection(acc, last, line)) {
// if the object belongs to a nested section, we append it to the new `lines` array of the // if the object belongs to a nested section, we append it to the new `lines` array of the
// previously formated header // previously formatted header
last.lines.push(parseLine(line, lineNumber)); last.lines.push(parseLine(line, lineNumber));
} else if (line.section_duration) { } else if (line.section_duration) {
// if the line has section_duration, we look for the correct header to add it // if the line has section_duration, we look for the correct header to add it

View File

@ -5,7 +5,7 @@
import $ from 'jquery'; import $ from 'jquery';
import axios from './axios_utils'; import axios from './axios_utils';
import { getLocationHash } from './url_utility'; import { getLocationHash } from './url_utility';
import { convertToCamelCase } from './text_utility'; import { convertToCamelCase, convertToSnakeCase } from './text_utility';
import { isObject } from './type_utility'; import { isObject } from './type_utility';
import breakpointInstance from '../../breakpoints'; import breakpointInstance from '../../breakpoints';
@ -697,6 +697,22 @@ export const convertObjectPropsToCamelCase = (obj = {}, options = {}) => {
}, initial); }, initial);
}; };
/**
* Converts all the object keys to snake case
*
* @param {Object} obj Object to transform
* @returns {Object}
*/
// Follow up to add additional options param:
// https://gitlab.com/gitlab-org/gitlab/issues/39173
export const convertObjectPropsToSnakeCase = (obj = {}) =>
obj
? Object.entries(obj).reduce(
(acc, [key, value]) => ({ ...acc, [convertToSnakeCase(key)]: value }),
{},
)
: {};
export const imagePath = imgUrl => export const imagePath = imgUrl =>
`${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`; `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;

View File

@ -447,7 +447,7 @@ export const parsePikadayDate = dateString => {
/** /**
* Used `onSelect` method in pickaday * Used `onSelect` method in pickaday
* @param {Date} date UTC format * @param {Date} date UTC format
* @return {String} Date formated in yyyy-mm-dd * @return {String} Date formatted in yyyy-mm-dd
*/ */
export const pikadayToString = date => { export const pikadayToString = date => {
const day = pad(date.getDate()); const day = pad(date.getDate());
@ -513,8 +513,8 @@ export const stringifyTime = (timeObject, fullNameFormat = false) => {
if (fullNameFormat && isNonZero) { if (fullNameFormat && isNonZero) {
// Remove traling 's' if unit value is singular // Remove traling 's' if unit value is singular
const formatedUnitName = unitValue > 1 ? unitName : unitName.replace(/s$/, ''); const formattedUnitName = unitValue > 1 ? unitName : unitName.replace(/s$/, '');
return `${memo} ${unitValue} ${formatedUnitName}`; return `${memo} ${unitValue} ${formattedUnitName}`;
} }
return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo; return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo;

View File

@ -45,7 +45,7 @@ export default {
return this.mergeRequest.headPipeline && this.mergeRequest.headPipeline.detailedStatus; return this.mergeRequest.headPipeline && this.mergeRequest.headPipeline.detailedStatus;
}, },
formattedTime() { formattedTime() {
return this.timeFormated(this.mergeRequest.createdAt); return this.timeFormatted(this.mergeRequest.createdAt);
}, },
statusBoxClass() { statusBoxClass() {
switch (this.mergeRequest.state) { switch (this.mergeRequest.state) {

View File

@ -31,7 +31,7 @@ export default {
hasFinishedTime() { hasFinishedTime() {
return this.finishedTime !== ''; return this.finishedTime !== '';
}, },
durationFormated() { durationFormatted() {
const date = new Date(this.duration * 1000); const date = new Date(this.duration * 1000);
let hh = date.getUTCHours(); let hh = date.getUTCHours();
@ -59,7 +59,7 @@ export default {
<div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Duration') }}</div> <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Duration') }}</div>
<div class="table-mobile-content"> <div class="table-mobile-content">
<p v-if="hasDuration" class="duration"> <p v-if="hasDuration" class="duration">
<span v-html="iconTimerSvg"> </span> {{ durationFormated }} <span v-html="iconTimerSvg"> </span> {{ durationFormatted }}
</p> </p>
<p v-if="hasFinishedTime" class="finished-at d-none d-sm-none d-md-block"> <p v-if="hasFinishedTime" class="finished-at d-none d-sm-none d-md-block">
@ -71,7 +71,7 @@ export default {
data-placement="top" data-placement="top"
data-container="body" data-container="body"
> >
{{ timeFormated(finishedTime) }} {{ timeFormatted(finishedTime) }}
</time> </time>
</p> </p>
</div> </div>

View File

@ -247,7 +247,7 @@ export default {
<td> <td>
<span v-gl-tooltip.bottom :title="tooltipTitle(item.createdAt)">{{ <span v-gl-tooltip.bottom :title="tooltipTitle(item.createdAt)">{{
timeFormated(item.createdAt) timeFormatted(item.createdAt)
}}</span> }}</span>
</td> </td>

View File

@ -48,7 +48,7 @@ export default {
}, },
releasedTimeAgo() { releasedTimeAgo() {
return sprintf(__('released %{time}'), { return sprintf(__('released %{time}'), {
time: this.timeFormated(this.release.released_at), time: this.timeFormatted(this.release.released_at),
}); });
}, },
userImageAltDescription() { userImageAltDescription() {

View File

@ -50,7 +50,7 @@ export default {
}, },
computed: { computed: {
releasedAtTimeAgo() { releasedAtTimeAgo() {
return this.timeFormated(this.releasedAt); return this.timeFormatted(this.releasedAt);
}, },
userImageAltDescription() { userImageAltDescription() {
return this.author && this.author.username return this.author && this.author.username

View File

@ -41,7 +41,7 @@ export default {
}, },
computed: { computed: {
deployTimeago() { deployTimeago() {
return this.timeFormated(this.deployment.deployed_at); return this.timeFormatted(this.deployment.deployed_at);
}, },
deployedText() { deployedText() {
return this.$options.deployedTextMap[this.computedDeploymentStatus]; return this.$options.deployedTextMap[this.computedDeploymentStatus];

View File

@ -54,7 +54,7 @@ export default {
return timeFor( return timeFor(
this.milestoneDue, this.milestoneDue,
sprintf(__('Expired %{expiredOn}'), { sprintf(__('Expired %{expiredOn}'), {
expiredOn: this.timeFormated(this.milestoneDue), expiredOn: this.timeFormatted(this.milestoneDue),
}), }),
); );
} }
@ -62,7 +62,7 @@ export default {
return sprintf( return sprintf(
this.isMilestoneStarted ? __('Started %{startsIn}') : __('Starts %{startsIn}'), this.isMilestoneStarted ? __('Started %{startsIn}') : __('Starts %{startsIn}'),
{ {
startsIn: this.timeFormated(this.milestoneStart), startsIn: this.timeFormatted(this.milestoneStart),
}, },
); );
} }

View File

@ -64,7 +64,7 @@ export default {
tooltipText(dateType = 'min') { tooltipText(dateType = 'min') {
const defaultText = dateType === 'min' ? __('Start date') : __('Due date'); const defaultText = dateType === 'min' ? __('Start date') : __('Due date');
const date = this[`${dateType}Date`]; const date = this[`${dateType}Date`];
const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date); const timeAgo = dateType === 'min' ? this.timeFormatted(date) : timeFor(date);
const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : ''; const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : '';
if (date) { if (date) {

View File

@ -35,7 +35,7 @@ export default {
v-gl-tooltip.viewport="{ placement: tooltipPlacement }" v-gl-tooltip.viewport="{ placement: tooltipPlacement }"
:class="cssClass" :class="cssClass"
:title="tooltipTitle(time)" :title="tooltipTitle(time)"
v-text="timeFormated(time)" v-text="timeFormatted(time)"
> >
</time> </time>
</template> </template>

View File

@ -159,7 +159,7 @@ const mixins = {
return this.displayReference.split(this.pathIdSeparator).pop(); return this.displayReference.split(this.pathIdSeparator).pop();
}, },
createdAtInWords() { createdAtInWords() {
return this.createdAt ? this.timeFormated(this.createdAt) : ''; return this.createdAt ? this.timeFormatted(this.createdAt) : '';
}, },
createdAtTimestamp() { createdAtTimestamp() {
return this.createdAt ? formatDate(new Date(this.createdAt)) : ''; return this.createdAt ? formatDate(new Date(this.createdAt)) : '';
@ -168,10 +168,10 @@ const mixins = {
return this.mergedAt ? formatDate(new Date(this.mergedAt)) : ''; return this.mergedAt ? formatDate(new Date(this.mergedAt)) : '';
}, },
mergedAtInWords() { mergedAtInWords() {
return this.mergedAt ? this.timeFormated(this.mergedAt) : ''; return this.mergedAt ? this.timeFormatted(this.mergedAt) : '';
}, },
closedAtInWords() { closedAtInWords() {
return this.closedAt ? this.timeFormated(this.closedAt) : ''; return this.closedAt ? this.timeFormatted(this.closedAt) : '';
}, },
closedAtTimestamp() { closedAtTimestamp() {
return this.closedAt ? formatDate(new Date(this.closedAt)) : ''; return this.closedAt ? formatDate(new Date(this.closedAt)) : '';

View File

@ -5,7 +5,7 @@ import { formatDate, getTimeago } from '../../lib/utils/datetime_utility';
*/ */
export default { export default {
methods: { methods: {
timeFormated(time) { timeFormatted(time) {
const timeago = getTimeago(); const timeago = getTimeago();
return timeago.format(time); return timeago.format(time);

View File

@ -20,6 +20,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :check_user_can_push_to_source_branch!, only: [:rebase] before_action :check_user_can_push_to_source_branch!, only: [:rebase]
before_action only: [:show] do before_action only: [:show] do
push_frontend_feature_flag(:diffs_batch_load, @project) push_frontend_feature_flag(:diffs_batch_load, @project)
push_frontend_feature_flag(:single_mr_diff_view, @project)
end end
before_action do before_action do

View File

@ -0,0 +1,5 @@
---
title: Show merge immediately dialog even if the MR's pipeline hasn't finished
merge_request: 21556
author:
type: changed

View File

@ -219,6 +219,94 @@ build_latest_vulnerabilities:
The above template will work for a GitLab Docker registry running on a local installation, however, if you're using a non-GitLab Docker registry, you'll need to change the `$CI_REGISTRY` value and the `docker login` credentials to match the details of your local registry. The above template will work for a GitLab Docker registry running on a local installation, however, if you're using a non-GitLab Docker registry, you'll need to change the `$CI_REGISTRY` value and the `docker login` credentials to match the details of your local registry.
## Reports JSON format
CAUTION: **Caution:**
The JSON report artifacts are not a public API of Container Scanning and their format may change in the future.
The Container Scanning tool emits a JSON report file. Here is an example of the report structure with all important parts of
it highlighted:
```json-doc
{
"version": "2.3",
"vulnerabilities": [
{
"category": "container_scanning",
"message": "CVE-2019-3462 in apt",
"description": "Incorrect sanitation of the 302 redirect field in HTTP transport method of apt versions 1.4.8 and earlier can lead to content injection by a MITM attacker, potentially leading to remote code execution on the target machine.",
"cve": "debian:9:apt:CVE-2019-3462",
"severity": "High",
"confidence": "Unknown",
"solution": "Upgrade apt from 1.4.8 to 1.4.9",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "apt"
},
"version": "1.4.8"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2019-3462",
"value": "CVE-2019-3462",
"url": "https://security-tracker.debian.org/tracker/CVE-2019-3462"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2019-3462"
}
]
}
],
"remediations": [
]
}
```
Here is the description of the report file structure nodes and their meaning. All fields are mandatory to be present in
the report JSON unless stated otherwise. Presence of optional fields depends on the underlying analyzers being used.
| Report JSON node | Description |
|------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `version` | Report syntax version used to generate this JSON. |
| `vulnerabilities` | Array of vulnerability objects. |
| `vulnerabilities[].category` | Where this vulnerability belongs (SAST, Container Scanning etc.). For Container Scanning, it will always be `container_scanning`. |
| `vulnerabilities[].message` | A short text that describes the vulnerability, it may include occurrence's specific information. Optional. |
| `vulnerabilities[].description` | A long text that describes the vulnerability. Optional. |
| `vulnerabilities[].cve` | A fingerprint string value that represents a concrete occurrence of the vulnerability. It's used to determine whether two vulnerability occurrences are same or different. May not be 100% accurate. **This is NOT a [CVE](https://cve.mitre.org/)**. |
| `vulnerabilities[].severity` | How much the vulnerability impacts the software. Possible values: `Undefined` (an analyzer has not provided this info), `Info`, `Unknown`, `Low`, `Medium`, `High`, `Critical`. **Note:** Our current container scanning tool based on [klar](https://github.com/optiopay/klar) only provides the following levels: `Unknown`, `Low`, `Medium`, `High`, `Critical`. |
| `vulnerabilities[].confidence` | How reliable the vulnerability's assessment is. Possible values: `Undefined` (an analyzer has not provided this info), `Ignore`, `Unknown`, `Experimental`, `Low`, `Medium`, `High`, `Confirmed`. **Note:** Our current container scanning tool based on [klar](https://github.com/optiopay/klar) does not provide a confidence level, so this value is currently hardcoded to `Unknown`. |
| `vulnerabilities[].solution` | Explanation of how to fix the vulnerability. Optional. |
| `vulnerabilities[].scanner` | A node that describes the analyzer used to find this vulnerability. |
| `vulnerabilities[].scanner.id` | Id of the scanner as a snake_case string. |
| `vulnerabilities[].scanner.name` | Name of the scanner, for display purposes. |
| `vulnerabilities[].location` | A node that tells where the vulnerability is located. |
| `vulnerabilities[].location.dependency` | A node that describes the dependency of a project where the vulnerability is located. |
| `vulnerabilities[].location.dependency.package` | A node that provides the information on the package where the vulnerability is located. |
| `vulnerabilities[].location.dependency.package.name` | Name of the package where the vulnerability is located. |
| `vulnerabilities[].location.dependency.version` | Version of the vulnerable package. Optional. |
| `vulnerabilities[].location.operating_system` | The operating system that contains the vulnerable package. |
| `vulnerabilities[].location.image` | The Docker image that was analyzed. Optional. |
| `vulnerabilities[].identifiers` | An ordered array of references that identify a vulnerability on internal or external DBs. |
| `vulnerabilities[].identifiers[].type` | Type of the identifier. Possible values: common identifier types (among `cve`, `cwe`, `osvdb`, and `usn`). |
| `vulnerabilities[].identifiers[].name` | Name of the identifier for display purpose. |
| `vulnerabilities[].identifiers[].value` | Value of the identifier for matching purpose. |
| `vulnerabilities[].identifiers[].url` | URL to identifier's documentation. Optional. |
| `vulnerabilities[].links` | An array of references to external documentation pieces or articles that describe the vulnerability further. Optional. |
| `vulnerabilities[].links[].name` | Name of the vulnerability details link. Optional. |
| `vulnerabilities[].links[].url` | URL of the vulnerability details document. Optional. |
| `remediations` | Not supported yet. |
## Troubleshooting ## Troubleshooting
### docker: Error response from daemon: failed to copy xattrs ### docker: Error response from daemon: failed to copy xattrs

View File

@ -5202,6 +5202,9 @@ msgstr ""
msgid "CustomCycleAnalytics|Add stage" msgid "CustomCycleAnalytics|Add stage"
msgstr "" msgstr ""
msgid "CustomCycleAnalytics|Editing stage"
msgstr ""
msgid "CustomCycleAnalytics|Enter a name for the stage" msgid "CustomCycleAnalytics|Enter a name for the stage"
msgstr "" msgstr ""
@ -5235,6 +5238,9 @@ msgstr ""
msgid "CustomCycleAnalytics|Stop event label" msgid "CustomCycleAnalytics|Stop event label"
msgstr "" msgstr ""
msgid "CustomCycleAnalytics|Update stage"
msgstr ""
msgid "Customize colors" msgid "Customize colors"
msgstr "" msgstr ""

View File

@ -45,9 +45,9 @@ describe('Environment item', () => {
}); });
it('should render last deployment date', () => { it('should render last deployment date', () => {
const formatedDate = format(environment.last_deployment.deployed_at); const formattedDate = format(environment.last_deployment.deployed_at);
expect(wrapper.find('.environment-created-date-timeago').text()).toContain(formatedDate); expect(wrapper.find('.environment-created-date-timeago').text()).toContain(formattedDate);
}); });
describe('With user information', () => { describe('With user information', () => {

View File

@ -8,7 +8,7 @@ describe('Erased block', () => {
const erasedAt = '2016-11-07T11:11:16.525Z'; const erasedAt = '2016-11-07T11:11:16.525Z';
const timeago = getTimeago(); const timeago = getTimeago();
const formatedDate = timeago.format(erasedAt); const formattedDate = timeago.format(erasedAt);
const createComponent = props => { const createComponent = props => {
wrapper = mount(ErasedBlock, { wrapper = mount(ErasedBlock, {
@ -41,7 +41,7 @@ describe('Erased block', () => {
}); });
it('renders erasedAt', () => { it('renders erasedAt', () => {
expect(wrapper.text().trim()).toContain(formatedDate); expect(wrapper.text().trim()).toContain(formattedDate);
}); });
}); });
@ -57,7 +57,7 @@ describe('Erased block', () => {
}); });
it('renders erasedAt', () => { it('renders erasedAt', () => {
expect(wrapper.text().trim()).toContain(formatedDate); expect(wrapper.text().trim()).toContain(formattedDate);
}); });
}); });
}); });

View File

@ -68,7 +68,7 @@ describe('table registry', () => {
expect(tds.at(2).html()).toContain(repoPropsData.list[0].shortRevision); expect(tds.at(2).html()).toContain(repoPropsData.list[0].shortRevision);
expect(tds.at(3).html()).toContain(repoPropsData.list[0].layers); expect(tds.at(3).html()).toContain(repoPropsData.list[0].layers);
expect(tds.at(3).html()).toContain(repoPropsData.list[0].size); expect(tds.at(3).html()).toContain(repoPropsData.list[0].size);
expect(tds.at(4).html()).toContain(wrapper.vm.timeFormated(repoPropsData.list[0].createdAt)); expect(tds.at(4).html()).toContain(wrapper.vm.timeFormatted(repoPropsData.list[0].createdAt));
}); });
it('should have a label called Image ID', () => { it('should have a label called Image ID', () => {

View File

@ -8,8 +8,8 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
jest.mock('~/vue_shared/mixins/timeago', () => ({ jest.mock('~/vue_shared/mixins/timeago', () => ({
methods: { methods: {
timeFormated() { timeFormatted() {
return '7 fortnightes ago'; return '7 fortnights ago';
}, },
tooltipTitle() { tooltipTitle() {
return 'February 30, 2401'; return 'February 30, 2401';
@ -82,7 +82,7 @@ describe('Release block footer', () => {
it('renders the author and creation time info', () => { it('renders the author and creation time info', () => {
expect(trimText(authorDateInfoSection().text())).toBe( expect(trimText(authorDateInfoSection().text())).toBe(
`Created 7 fortnightes ago by ${releaseClone.author.username}`, `Created 7 fortnights ago by ${releaseClone.author.username}`,
); );
}); });
@ -139,7 +139,7 @@ describe('Release block footer', () => {
beforeEach(() => factory({ author: undefined })); beforeEach(() => factory({ author: undefined }));
it('renders the release date without the author name', () => { it('renders the release date without the author name', () => {
expect(trimText(authorDateInfoSection().text())).toBe('Created 7 fortnightes ago'); expect(trimText(authorDateInfoSection().text())).toBe('Created 7 fortnights ago');
}); });
}); });

View File

@ -68,7 +68,7 @@ describe('Release block', () => {
}); });
it('renders release date', () => { it('renders release date', () => {
expect(wrapper.text()).toContain(timeagoMixin.methods.timeFormated(release.released_at)); expect(wrapper.text()).toContain(timeagoMixin.methods.timeFormatted(release.released_at));
}); });
it('renders number of assets provided', () => { it('renders number of assets provided', () => {

View File

@ -90,11 +90,11 @@ describe('RelatedIssuableItem', () => {
it('renders state title', () => { it('renders state title', () => {
const stateTitle = tokenState.attributes('title'); const stateTitle = tokenState.attributes('title');
const formatedCreateDate = formatDate(props.createdAt); const formattedCreateDate = formatDate(props.createdAt);
expect(stateTitle).toContain('<span class="bold">Opened</span>'); expect(stateTitle).toContain('<span class="bold">Opened</span>');
expect(stateTitle).toContain(`<span class="text-tertiary">${formatedCreateDate}</span>`); expect(stateTitle).toContain(`<span class="text-tertiary">${formattedCreateDate}</span>`);
}); });
it('renders aria label', () => { it('renders aria label', () => {

View File

@ -41,6 +41,7 @@ describe('diffs/components/app', () => {
changesEmptyStateIllustration: '', changesEmptyStateIllustration: '',
dismissEndpoint: '', dismissEndpoint: '',
showSuggestPopover: true, showSuggestPopover: true,
useSingleDiffStyle: false,
...props, ...props,
}, },
store, store,

View File

@ -75,6 +75,7 @@ describe('DiffsStoreActions', () => {
const projectPath = '/root/project'; const projectPath = '/root/project';
const dismissEndpoint = '/-/user_callouts'; const dismissEndpoint = '/-/user_callouts';
const showSuggestPopover = false; const showSuggestPopover = false;
const useSingleDiffStyle = false;
testAction( testAction(
setBaseConfig, setBaseConfig,
@ -85,6 +86,7 @@ describe('DiffsStoreActions', () => {
projectPath, projectPath,
dismissEndpoint, dismissEndpoint,
showSuggestPopover, showSuggestPopover,
useSingleDiffStyle,
}, },
{ {
endpoint: '', endpoint: '',
@ -93,6 +95,7 @@ describe('DiffsStoreActions', () => {
projectPath: '', projectPath: '',
dismissEndpoint: '', dismissEndpoint: '',
showSuggestPopover: true, showSuggestPopover: true,
useSingleDiffStyle: true,
}, },
[ [
{ {
@ -104,6 +107,7 @@ describe('DiffsStoreActions', () => {
projectPath, projectPath,
dismissEndpoint, dismissEndpoint,
showSuggestPopover, showSuggestPopover,
useSingleDiffStyle,
}, },
}, },
], ],

View File

@ -10,11 +10,13 @@ describe('DiffsStoreMutations', () => {
const state = {}; const state = {};
const endpoint = '/diffs/endpoint'; const endpoint = '/diffs/endpoint';
const projectPath = '/root/project'; const projectPath = '/root/project';
const useSingleDiffStyle = false;
mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath }); mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath, useSingleDiffStyle });
expect(state.endpoint).toEqual(endpoint); expect(state.endpoint).toEqual(endpoint);
expect(state.projectPath).toEqual(projectPath); expect(state.projectPath).toEqual(projectPath);
expect(state.useSingleDiffStyle).toEqual(useSingleDiffStyle);
}); });
}); });

View File

@ -721,6 +721,28 @@ describe('common_utils', () => {
}); });
}); });
describe('convertObjectPropsToSnakeCase', () => {
it('converts each object key to snake case', () => {
const obj = {
some: 'some',
'cool object': 'cool object',
likeThisLongOne: 'likeThisLongOne',
};
expect(commonUtils.convertObjectPropsToSnakeCase(obj)).toEqual({
some: 'some',
cool_object: 'cool object',
like_this_long_one: 'likeThisLongOne',
});
});
it('returns an empty object if there are no keys', () => {
['', {}, [], null].forEach(badObj => {
expect(commonUtils.convertObjectPropsToSnakeCase(badObj)).toEqual({});
});
});
});
describe('with options', () => { describe('with options', () => {
const objWithoutChildren = { const objWithoutChildren = {
project_name: 'GitLab CE', project_name: 'GitLab CE',