Commit 18ffa5e8 authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 66bd1f0f
......@@ -59,6 +59,7 @@ code_quality:
SAST_ANALYZER_IMAGE_TAG: 2
SAST_BRAKEMAN_LEVEL: 2 # GitLab-specific
SAST_EXCLUDED_PATHS: qa,spec,doc,ee/spec # GitLab-specific
SAST_DISABLE_BABEL: "true"
script:
- /analyzer run
......@@ -72,11 +73,10 @@ eslint-sast:
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/eslint:$SAST_ANALYZER_IMAGE_TAG"
# Temporary disabled as it's constantly failing. See https://gitlab.com/gitlab-org/gitlab/-/issues/213769.
# nodejs-scan-sast:
# extends: .sast
# image:
# name: "$SAST_ANALYZER_IMAGE_PREFIX/nodejs-scan:$SAST_ANALYZER_IMAGE_TAG"
nodejs-scan-sast:
extends: .sast
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/nodejs-scan:$SAST_ANALYZER_IMAGE_TAG"
secrets-sast:
extends: .sast
......
// Browser polyfills
/**
* Polyfill: fetch
* @what https://fetch.spec.whatwg.org/
* @why Because Apollo GraphQL client relies on fetch
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=fetch
*/
import 'unfetch/polyfill/index';
/**
* Polyfill: FormData APIs
* @what delete(), get(), getAll(), has(), set(), entries(), keys(), values(),
* and support for for...of
* @why Because Apollo GraphQL client relies on fetch
* @browsers Internet Explorer 11, Edge < 18
* @see https://caniuse.com/#feat=mdn-api_formdata and subfeatures
* Polyfill
* @what requestIdleCallback
* @why To align browser features
* @browsers Safari (all versions)
* @see https://caniuse.com/#feat=requestidlecallback
*/
import 'formdata-polyfill';
window.requestIdleCallback =
window.requestIdleCallback ||
function requestShim(cb) {
const start = Date.now();
return setTimeout(() => {
cb({
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),
});
}, 1);
};
import './polyfills/custom_event';
import './polyfills/element';
import './polyfills/event';
import './polyfills/nodelist';
import './polyfills/request_idle_callback';
import './polyfills/svg';
window.cancelIdleCallback =
window.cancelIdleCallback ||
function cancelShim(id) {
clearTimeout(id);
};
/**
* Polyfill: CustomEvent constructor
* @what new CustomEvent()
* @why Certain features, e.g. notes utilize this
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=customevent
*/
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function CustomEvent(event, params) {
const evt = document.createEvent('CustomEvent');
const evtParams = {
bubbles: false,
cancelable: false,
detail: undefined,
...params,
};
evt.initCustomEvent(event, evtParams.bubbles, evtParams.cancelable, evtParams.detail);
return evt;
};
window.CustomEvent.prototype = Event;
}
/**
* Polyfill
* @what Element.classList
* @why In order to align browser features
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=classlist
*/
import 'classlist-polyfill';
/**
* Polyfill
* @what Element.closest
* @why In order to align browser features
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=element-closest
*/
Element.prototype.closest =
Element.prototype.closest ||
function closest(selector, selectedElement = this) {
if (!selectedElement) return null;
return selectedElement.matches(selector)
? selectedElement
: Element.prototype.closest(selector, selectedElement.parentElement);
};
/**
* Polyfill
* @what Element.matches
* @why In order to align browser features
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=mdn-api_element_matches
*/
Element.prototype.matches =
Element.prototype.matches ||
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function matches(selector) {
const elms = (this.document || this.ownerDocument).querySelectorAll(selector);
let i = elms.length - 1;
while (i >= 0 && elms.item(i) !== this) {
i -= 1;
}
return i > -1;
};
/**
* Polyfill
* @what ChildNode.remove, Element.remove, CharacterData.remove, DocumentType.remove
* @why In order to align browser features
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=childnode-remove
*
* From the polyfill on MDN, https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove#Polyfill
*/
(arr => {
arr.forEach(item => {
if (Object.prototype.hasOwnProperty.call(item, 'remove')) {
return;
}
Object.defineProperty(item, 'remove', {
configurable: true,
enumerable: true,
writable: true,
value: function remove() {
if (this.parentNode !== null) {
this.parentNode.removeChild(this);
}
},
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
/**
* Polyfill: Event constructor
* @what new Event()
* @why To align browser support
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=mdn-api_event_event
*
* Although `initEvent` is deprecated for modern browsers it is the one supported by IE
*/
if (typeof window.Event !== 'function') {
window.Event = function Event(event, params) {
const evt = document.createEvent('Event');
const evtParams = {
bubbles: false,
cancelable: false,
...params,
};
evt.initEvent(event, evtParams.bubbles, evtParams.cancelable);
return evt;
};
window.Event.prototype = Event;
}
/**
* Polyfill
* @what NodeList.forEach
* @why To align browser support
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=mdn-api_nodelist_foreach
*/
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = function forEach(callback, thisArg = window) {
for (let i = 0; i < this.length; i += 1) {
callback.call(thisArg, this[i], i, this);
}
};
}
/**
* Polyfill
* @what requestIdleCallback
* @why To align browser features
* @browsers Safari (all versions), Internet Explorer 11
* @see https://caniuse.com/#feat=requestidlecallback
*/
window.requestIdleCallback =
window.requestIdleCallback ||
function requestShim(cb) {
const start = Date.now();
return setTimeout(() => {
cb({
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),
});
}, 1);
};
window.cancelIdleCallback =
window.cancelIdleCallback ||
function cancelShim(id) {
clearTimeout(id);
};
/**
* polyfill support for external SVG file references via <use xlink:href>
* @what polyfill support for external SVG file references via <use xlink:href>
* @why This is used in our GitLab SVG icon library
* @browsers Internet Explorer 11
* @see https://caniuse.com/#feat=mdn-svg_elements_use_external_uri
* @see https//css-tricks.com/svg-use-external-source/
*/
import svg4everybody from 'svg4everybody';
svg4everybody();
......@@ -106,10 +106,11 @@ export default {
return {
id: this.fieldId,
name: this.fieldName,
state: this.valid,
};
},
valid() {
return !this.required || !isEmpty(this.model) || !this.validated;
return !this.required || !isEmpty(this.model) || this.isNonEmptyPassword || !this.validated;
},
},
created() {
......
<script>
import { GlFormGroup, GlFormCheckbox, GlFormInput, GlSprintf, GlLink } from '@gitlab/ui';
import eventHub from '../event_hub';
import {
GlFormGroup,
GlFormCheckbox,
GlFormInput,
GlSprintf,
GlLink,
GlButton,
GlCard,
} from '@gitlab/ui';
export default {
name: 'JiraIssuesFields',
......@@ -9,17 +18,30 @@ export default {
GlFormInput,
GlSprintf,
GlLink,
GlButton,
GlCard,
},
props: {
showJiraIssuesIntegration: {
type: Boolean,
required: false,
default: false,
},
initialEnableJiraIssues: {
type: Boolean,
required: false,
default: null,
},
initialProjectKey: {
type: String,
required: false,
default: null,
},
upgradePlanPath: {
type: String,
required: false,
default: null,
},
editProjectPath: {
type: String,
required: false,
......@@ -30,8 +52,25 @@ export default {
return {
enableJiraIssues: this.initialEnableJiraIssues,
projectKey: this.initialProjectKey,
validated: false,
};
},
computed: {
validProjectKey() {
return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated;
},
},
created() {
eventHub.$on('validateForm', this.validateForm);
},
beforeDestroy() {
eventHub.$off('validateForm', this.validateForm);
},
methods: {
validateForm() {
this.validated = true;
},
},
};
</script>
......@@ -45,44 +84,66 @@ export default {
<p>
{{
s__(
'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of issues and view any issue as read-only.',
'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only.',
)
}}
</p>
<input name="service[issues_enabled]" type="hidden" value="false" />
<gl-form-checkbox v-model="enableJiraIssues" name="service[issues_enabled]">
{{ s__('JiraService|Enable Jira issues') }}
<template #help>
{{
s__(
'JiraService|Warning: All GitLab users that have access to this GitLab project will be able to view all issues from the Jira project specified below.',
)
}}
</template>
</gl-form-checkbox>
<template v-if="showJiraIssuesIntegration">
<input name="service[issues_enabled]" type="hidden" value="false" />
<gl-form-checkbox v-model="enableJiraIssues" name="service[issues_enabled]">
{{ s__('JiraService|Enable Jira issues') }}
<template #help>
{{
s__(
'JiraService|Warning: All GitLab users that have access to this GitLab project will be able to view all issues from the Jira project specified below.',
)
}}
</template>
</gl-form-checkbox>
</template>
<gl-card v-else class="gl-mt-7">
<strong>{{ __('This is a Premium feature') }}</strong>
<p>{{ __('Upgrade your plan to enable this feature of the Jira Integration.') }}</p>
<gl-button
v-if="upgradePlanPath"
category="primary"
variant="info"
:href="upgradePlanPath"
target="_blank"
>
{{ __('Upgrade your plan') }}
</gl-button>
</gl-card>
</div>
</gl-form-group>
<gl-form-group :label="s__('JiraService|Jira project key')">
<gl-form-input
v-model="projectKey"
type="text"
name="service[project_key]"
:placeholder="s__('JiraService|e.g. AB')"
:disabled="!enableJiraIssues"
/>
</gl-form-group>
<p>
<gl-sprintf
:message="
s__(
'JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used.',
)
"
<template v-if="showJiraIssuesIntegration">
<gl-form-group
:label="s__('JiraService|Jira project key')"
:invalid-feedback="__('This field is required.')"
:state="validProjectKey"
>
<template #link="{ content }">
<gl-link :href="editProjectPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
<gl-form-input
v-model="projectKey"
name="service[project_key]"
:placeholder="s__('JiraService|e.g. AB')"
:required="enableJiraIssues"
:state="validProjectKey"
:disabled="!enableJiraIssues"
/>
</gl-form-group>
<p>
<gl-sprintf
:message="
s__(
'JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used.',
)
"
>
<template #link="{ content }">
<gl-link :href="editProjectPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</template>
</div>
</template>
......@@ -19,6 +19,7 @@ export default el => {
type,
commentDetail,
projectKey,
upgradePlanPath,
editProjectPath,
triggerEvents,
fields,
......@@ -30,6 +31,7 @@ export default el => {
commitEvents,
mergeRequestEvents,
enableComments,
showJiraIssuesIntegration,
enableJiraIssues,
} = parseBooleanInData(booleanAttributes);
......@@ -50,8 +52,10 @@ export default el => {
initialCommentDetail: commentDetail,
},
jiraIssuesProps: {
showJiraIssuesIntegration,
initialEnableJiraIssues: enableJiraIssues,
initialProjectKey: projectKey,
upgradePlanPath,
editProjectPath,
},
triggerEvents: JSON.parse(triggerEvents),
......
......@@ -187,6 +187,10 @@ export default {
return isScopedLabel({ title: name }) && this.scopedLabelsAvailable;
},
labelHref({ name }) {
if (this.isJiraIssue) {
return this.issuableLink({ 'labels[]': name });
}
return this.issuableLink({ 'label_name[]': name });
},
onSelect(ev) {
......
......@@ -14,7 +14,7 @@ export default {
TestSummaryTable,
},
computed: {
...mapState(['isLoading', 'selectedSuiteIndex', 'testReports']),
...mapState(['hasFullReport', 'isLoading', 'selectedSuiteIndex', 'testReports']),
...mapGetters(['getSelectedSuite']),
showSuite() {
return this.selectedSuiteIndex !== null;
......@@ -28,12 +28,22 @@ export default {
this.fetchSummary();
},
methods: {
...mapActions(['fetchSummary', 'setSelectedSuiteIndex', 'removeSelectedSuiteIndex']),
...mapActions([
'fetchFullReport',
'fetchSummary',
'setSelectedSuiteIndex',
'removeSelectedSuiteIndex',
]),
summaryBackClick() {
this.removeSelectedSuiteIndex();
},
summaryTableRowClick(index) {
this.setSelectedSuiteIndex(index);
// Fetch the full report when the user clicks to see more details
if (!this.hasFullReport) {
this.fetchFullReport();
}
},
beforeEnterTransition() {
document.documentElement.style.overflowX = 'hidden';
......
......@@ -122,13 +122,17 @@ const createTestDetails = () => {
}
const el = document.querySelector('#js-pipeline-tests-detail');
const { fullReportEndpoint, countEndpoint } = el?.dataset || {};
const { fullReportEndpoint, summaryEndpoint, countEndpoint } = el?.dataset || {};
const testReportsStore = createTestReportsStore({
fullReportEndpoint,
summaryEndpoint: countEndpoint,
summaryEndpoint: summaryEndpoint || countEndpoint,
useBuildSummaryReport: window.gon?.features?.buildReportSummary,
});
createPipelinesTabs(testReportsStore);
if (!window.gon?.features?.buildReportSummary) {
createPipelinesTabs(testReportsStore);
}
// eslint-disable-next-line no-new
new Vue({
......
......@@ -3,21 +3,33 @@ import * as types from './mutation_types';
import createFlash from '~/flash';
import { s__ } from '~/locale';
export const fetchSummary = ({ state, commit }) => {
export const fetchSummary = ({ state, commit, dispatch }) => {
// If we do this without the build_report_summary feature flag enabled
// it causes a race condition for toggleLoading and ruins the loading
// state in the application
if (state.useBuildSummaryReport) {
dispatch('toggleLoading');
}
return axios
.get(state.summaryEndpoint)
.then(({ data }) => {
commit(types.SET_SUMMARY, data);
// Set the tab counter badge to total_count
// This is temporary until we can server-side render that count number
// (see https://gitlab.com/gitlab-org/gitlab/-/issues/223134)
if (data.total_count !== undefined) {
document.querySelector('.js-test-report-badge-counter').innerHTML = data.total_count;
if (!state.useBuildSummaryReport) {
// Set the tab counter badge to total_count
// This is temporary until we can server-side render that count number
// (see https://gitlab.com/gitlab-org/gitlab/-/issues/223134)
document.querySelector('.js-test-report-badge-counter').innerHTML = data.total_count || 0;
}
})
.catch(() => {
createFlash(s__('TestReports|There was an error fetching the summary.'));
})
.finally(() => {
if (state