Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-07-22 12:10:03 +00:00
parent 2763994b8f
commit 74b99c4264
24 changed files with 360 additions and 332 deletions

View File

@ -217,52 +217,50 @@ export default {
</script>
<template>
<div class="diff-grid-row diff-grid-row-full diff-tr line_holder match expansion">
<div :class="{ parallel: !inline }" class="diff-grid-left diff-grid-2-col left-side">
<div
class="diff-td diff-line-num gl-text-center! gl-p-0! gl-w-full! gl-display-flex gl-flex-direction-column"
<div>
<div
class="diff-td diff-line-num gl-text-center! gl-p-0! gl-w-full! gl-display-flex gl-flex-direction-column"
>
<button
v-if="showExpandDown"
v-gl-tooltip.left
:title="s__('Diffs|Next 20 lines')"
:disabled="loading.down"
type="button"
class="js-unfold-down gl-rounded-0 gl-border-0 diff-line-expand-button"
@click="handleExpandLines($options.EXPAND_DOWN)"
>
<button
v-if="showExpandDown"
v-gl-tooltip.left
:title="s__('Diffs|Next 20 lines')"
:disabled="loading.down"
type="button"
class="js-unfold-down gl-rounded-0 gl-border-0 diff-line-expand-button"
@click="handleExpandLines($options.EXPAND_DOWN)"
>
<gl-loading-icon v-if="loading.down" size="sm" color="dark" inline />
<gl-icon v-else name="expand-down" />
</button>
<button
v-if="lineCountBetween !== -1 && lineCountBetween < 20"
v-gl-tooltip.left
:title="s__('Diffs|Expand all lines')"
:disabled="loading.all"
type="button"
class="js-unfold-all gl-rounded-0 gl-border-0 diff-line-expand-button"
@click="handleExpandLines()"
>
<gl-loading-icon v-if="loading.all" size="sm" color="dark" inline />
<gl-icon v-else name="expand" />
</button>
<button
v-if="showExpandUp"
v-gl-tooltip.left
:title="s__('Diffs|Previous 20 lines')"
:disabled="loading.up"
type="button"
class="js-unfold gl-rounded-0 gl-border-0 diff-line-expand-button"
@click="handleExpandLines($options.EXPAND_UP)"
>
<gl-loading-icon v-if="loading.up" size="sm" color="dark" inline />
<gl-icon v-else name="expand-up" />
</button>
</div>
<div
v-safe-html="line.rich_text"
class="gl-display-flex! gl-flex-direction-column gl-justify-content-center diff-td line_content left-side gl-white-space-normal!"
></div>
<gl-loading-icon v-if="loading.down" size="sm" color="dark" inline />
<gl-icon v-else name="expand-down" />
</button>
<button
v-if="lineCountBetween !== -1 && lineCountBetween < 20"
v-gl-tooltip.left
:title="s__('Diffs|Expand all lines')"
:disabled="loading.all"
type="button"
class="js-unfold-all gl-rounded-0 gl-border-0 diff-line-expand-button"
@click="handleExpandLines()"
>
<gl-loading-icon v-if="loading.all" size="sm" color="dark" inline />
<gl-icon v-else name="expand" />
</button>
<button
v-if="showExpandUp"
v-gl-tooltip.left
:title="s__('Diffs|Previous 20 lines')"
:disabled="loading.up"
type="button"
class="js-unfold gl-rounded-0 gl-border-0 diff-line-expand-button"
@click="handleExpandLines($options.EXPAND_UP)"
>
<gl-loading-icon v-if="loading.up" size="sm" color="dark" inline />
<gl-icon v-else name="expand-up" />
</button>
</div>
<div
v-safe-html="line.rich_text"
class="gl-display-flex! gl-flex-direction-column gl-justify-content-center diff-td line_content left-side gl-white-space-normal!"
></div>
</div>
</template>

View File

@ -197,17 +197,33 @@ export default {
@mousedown="handleParallelLineMouseDown"
>
<template v-for="(line, index) in diffLines">
<template v-if="line.isMatchLineLeft || line.isMatchLineRight">
<div
v-if="line.isMatchLineLeft || line.isMatchLineRight"
:key="`expand-${index}`"
class="diff-grid-row diff-tr line_holder match expansion"
>
<diff-expansion-cell
:key="`expand-${index}`"
:file="diffFile"
:line="line.left"
:is-top="index === 0"
:is-bottom="index + 1 === diffLinesLength"
:inline="inline"
:line-count-between="getCountBetweenIndex(index)"
:class="{ parallel: !inline }"
class="diff-grid-left diff-grid-2-col left-side"
/>
</template>
<diff-expansion-cell
v-if="!inline"
:file="diffFile"
:line="line.left"
:is-top="index === 0"
:is-bottom="index + 1 === diffLinesLength"
:inline="inline"
:line-count-between="getCountBetweenIndex(index)"
:class="{ parallel: !inline }"
class="diff-grid-right diff-grid-2-col right-side"
/>
</div>
<diff-row
v-if="!line.isMatchLineLeft && !line.isMatchLineRight"
:key="line.line_code"

View File

@ -1,17 +1,26 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import ResourceLinksBlock from 'ee_component/linked_resources/components/resource_links_block.vue';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
Vue.use(VueApollo);
export default function initLinkedResources() {
const linkedResourcesRootElement = document.querySelector('.js-linked-resources-root');
if (linkedResourcesRootElement) {
const { issuableId, canAddResourceLinks, helpPath } = linkedResourcesRootElement.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
// eslint-disable-next-line no-new
new Vue({
el: linkedResourcesRootElement,
name: 'LinkedResourcesRoot',
apolloProvider,
components: {
resourceLinksBlock: ResourceLinksBlock,
},

View File

@ -206,7 +206,6 @@ export default {
<gl-button
v-if="isCollapsible"
class="js-collapse-btn"
data-testid="report-section-expand-button"
data-qa-selector="expand_report_button"
@click="toggleCollapsed"

View File

@ -189,15 +189,10 @@ export default {
axios
.get(`${this.blobInfo.webPath}?format=json&viewer=${type}`)
.then(async ({ data: { html, binary } }) => {
this.isRenderingLegacyTextViewer = true;
if (type === SIMPLE_BLOB_VIEWER) {
this.isRenderingLegacyTextViewer = true;
this.legacySimpleViewer = html;
window.requestIdleCallback(() => {
this.isRenderingLegacyTextViewer = false;
new LineHighlighter(); // eslint-disable-line no-new
});
} else {
this.legacyRichViewer = html;
}
@ -205,6 +200,14 @@ export default {
this.isBinary = binary;
this.isLoadingLegacyViewer = false;
window.requestIdleCallback(() => {
this.isRenderingLegacyTextViewer = false;
if (type === SIMPLE_BLOB_VIEWER) {
new LineHighlighter(); // eslint-disable-line no-new
}
});
await this.$nextTick();
handleLocationHash(); // Ensures that we scroll to the hash when async content is loaded
})

View File

@ -575,3 +575,11 @@ span.idiff {
@include gl-text-center;
}
}
// *:nth-of-type(1n+5) - makes sure we do not render elements 5+ right away when
// viewing a file. Even though the HTML is injected in the DOM, as long as we do
// not render those elements, the browser doesn't need to spend resources
// calculating and repainting what's hidden.
.file-holder [data-loading] .file-content *:nth-of-type(1n+5) {
@include gl-display-none;
}

View File

@ -109,6 +109,7 @@ module Participable
when User
participants << source
when Participable
next if skippable_system_notes?(source, participants)
next unless !verify_access || source_visible_to_user?(source, current_user)
source.class.participant_attrs.each do |attr|
@ -133,6 +134,13 @@ module Participable
participants.merge(extractor.users)
end
def skippable_system_notes?(source, participants)
source.is_a?(Note) &&
source.system? &&
source.author.in?(participants) &&
!source.note.match?(User.reference_pattern)
end
def use_internal_notes_extractor_for?(source)
source.is_a?(Note) && source.confidential?
end

View File

@ -74,13 +74,11 @@ module Ci
end
def runner_version_with_updated_status(runner_version)
version = runner_version['version']
suggestion = upgrade_check.check_runner_upgrade_status(version)
new_status = suggestion.each_key.first
_, new_status = upgrade_check.check_runner_upgrade_suggestion(runner_version.version)
if new_status != :error && new_status != runner_version['status'].to_sym
if new_status != :error && new_status != runner_version.status.to_sym
{
version: version,
version: runner_version.version,
status: Ci::RunnerVersion.statuses[new_status]
}
end

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338165
milestone: '14.5'
type: development
group: group::pipeline insights
default_enabled: false
default_enabled: true

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347144
milestone: '14.10'
type: development
group: group::pipeline execution
default_enabled: false
default_enabled: true

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343465
milestone: '14.5'
type: development
group: group::pipeline insights
default_enabled: false
default_enabled: true

View File

@ -268,7 +268,6 @@ gitlab_rails['ldap_servers'] = {
'port' => 636,
...
}
}
```
@ -444,15 +443,15 @@ If initially your LDAP configuration looked like:
1. In `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['ldap_servers'] = {
'main' => {
# snip...
'bind_dn' => 'admin',
'password' => '123'
}
}
```
```ruby
gitlab_rails['ldap_servers'] = {
'main' => {
# snip...
'bind_dn' => 'admin',
'password' => '123'
}
}
```
1. Edit the encrypted secret:

View File

@ -20399,8 +20399,6 @@ Vulnerability sort values.
| <a id="vulnerabilitysortreport_type_desc"></a>`report_type_desc` | Report Type in descending order. |
| <a id="vulnerabilitysortseverity_asc"></a>`severity_asc` | Severity in ascending order. |
| <a id="vulnerabilitysortseverity_desc"></a>`severity_desc` | Severity in descending order. |
| <a id="vulnerabilitysortstate_asc"></a>`state_asc` | State in ascending order. |
| <a id="vulnerabilitysortstate_desc"></a>`state_desc` | State in descending order. |
### `VulnerabilityState`

View File

@ -5,44 +5,12 @@ module Gitlab
class RunnerUpgradeCheck
include Singleton
def check_runner_upgrade_status(runner_version)
runner_version = ::Gitlab::VersionInfo.parse(runner_version, parse_suffix: true)
return { invalid_version: runner_version } unless runner_version.valid?
return { error: runner_version } unless runner_releases_store.releases
# Recommend update if outside of backport window
recommended_version = recommendation_if_outside_backport_window(runner_version)
return { recommended: recommended_version } if recommended_version
# Recommend patch update if there's a newer release in a same minor branch as runner
recommended_version = recommended_runner_release_update(runner_version)
return { recommended: recommended_version } if recommended_version
# Consider update if there's a newer release within the currently deployed GitLab version
available_version = available_runner_release(runner_version)
return { available: available_version } if available_version
{ not_available: runner_version }
def check_runner_upgrade_suggestion(runner_version)
check_runner_upgrade_suggestions(runner_version).first
end
private
def recommended_runner_release_update(runner_version)
recommended_release = runner_releases_store.releases_by_minor[runner_version.without_patch]
return recommended_release if recommended_release && recommended_release > runner_version
# Consider the edge case of pre-release runner versions that get registered, but are never published.
# In this case, suggest the latest compatible runner version
latest_release = runner_releases_store.releases_by_minor.values.select { |v| v < gitlab_version }.max
latest_release if latest_release && latest_release > runner_version
end
def available_runner_release(runner_version)
available_release = runner_releases_store.releases_by_minor[gitlab_version.without_patch]
available_release if available_release && available_release > runner_version
end
def gitlab_version
@gitlab_version ||= ::Gitlab::VersionInfo.parse(::Gitlab::VERSION, parse_suffix: true)
end
@ -51,9 +19,55 @@ module Gitlab
RunnerReleases.instance
end
def recommendation_if_outside_backport_window(runner_version)
return if runner_releases_store.releases.empty?
return if runner_version >= runner_releases_store.releases.last # return early if runner version is too new
def add_suggestion(suggestions, runner_version, version, status)
return false unless version && version > runner_version
suggestions[version] = status
true
end
def check_runner_upgrade_suggestions(runner_version)
runner_version = ::Gitlab::VersionInfo.parse(runner_version, parse_suffix: true)
return { runner_version => :invalid_version } unless runner_version.valid?
return { runner_version => :error } unless runner_releases_store.releases
suggestions = {}
# Recommend update if outside of backport window
unless add_recommendation_if_outside_backport_window(runner_version, suggestions)
# Recommend patch update if there's a newer release in a same minor branch as runner
add_recommended_runner_release_update(runner_version, suggestions)
end
# Consider update if there's a newer release within the currently deployed GitLab version
add_available_runner_release(runner_version, suggestions)
suggestions[runner_version] = :not_available if suggestions.empty?
suggestions
end
def add_recommended_runner_release_update(runner_version, suggestions)
recommended_release = runner_releases_store.releases_by_minor[runner_version.without_patch]
return true if add_suggestion(suggestions, runner_version, recommended_release, :recommended)
# Consider the edge case of pre-release runner versions that get registered, but are never published.
# In this case, suggest the latest compatible runner version
latest_release = runner_releases_store.releases_by_minor.values.select { |v| v < gitlab_version }.max
add_suggestion(suggestions, runner_version, latest_release, :recommended)
end
def add_available_runner_release(runner_version, suggestions)
available_version = runner_releases_store.releases_by_minor[gitlab_version.without_patch]
unless suggestions.include?(available_version)
add_suggestion(suggestions, runner_version, available_version, :available)
end
end
def add_recommendation_if_outside_backport_window(runner_version, suggestions)
return false if runner_releases_store.releases.empty?
return false if runner_version >= runner_releases_store.releases.last # return early if runner version is too new
minor_releases_with_index = runner_releases_store.releases_by_minor.keys.each_with_index.to_h
runner_minor_version_index = minor_releases_with_index[runner_version.without_patch]
@ -62,14 +76,15 @@ module Gitlab
outside_window = minor_releases_with_index.count - runner_minor_version_index > 3
if outside_window
recommended_release = runner_releases_store.releases_by_minor[gitlab_version.without_patch]
recommended_release if recommended_release && recommended_release > runner_version
recommended_version = runner_releases_store.releases_by_minor[gitlab_version.without_patch]
return add_suggestion(suggestions, runner_version, recommended_version, :recommended)
end
else
# If unknown runner version, then recommend the latest version for the GitLab instance
recommended_runner_release_update(gitlab_version)
return add_recommended_runner_release_update(gitlab_version, suggestions)
end
false
end
end
end

View File

@ -23477,6 +23477,9 @@ msgstr ""
msgid "LinkedResources|Cancel"
msgstr ""
msgid "LinkedResources|Fetching linked resources"
msgstr ""
msgid "LinkedResources|Link"
msgstr ""
@ -23486,6 +23489,12 @@ msgstr ""
msgid "LinkedResources|Read more about linked resources"
msgstr ""
msgid "LinkedResources|Remove"
msgstr ""
msgid "LinkedResources|Something went wrong while fetching linked resources for the incident."
msgstr ""
msgid "LinkedResources|Text (Optional)"
msgstr ""

View File

@ -1,5 +1,3 @@
import Vue from 'vue';
/**
* Deprecated. Please do not use.
* Please see https://gitlab.com/groups/gitlab-org/-/epics/2445
@ -29,33 +27,6 @@ export const mountComponentWithStore = (Component, { el, props, store }) =>
propsData: props || {},
}).$mount(el);
/**
* Deprecated. Please do not use.
* Please see https://gitlab.com/groups/gitlab-org/-/epics/2445
*/
export const mountComponentWithSlots = (Component, { props, slots }) => {
const component = new Component({
propsData: props || {},
});
component.$slots = slots;
return component.$mount();
};
/**
* Mount a component with the given render method.
*
* -----------------------------
* Deprecated. Please do not use.
* Please see https://gitlab.com/groups/gitlab-org/-/epics/2445
* -----------------------------
*
* This helps with inserting slots that need to be compiled.
*/
export const mountComponentWithRender = (render, el = null) =>
mountComponent(Vue.extend({ render }), {}, el);
/**
* Deprecated. Please do not use.
* Please see https://gitlab.com/groups/gitlab-org/-/epics/2445

View File

@ -28,8 +28,8 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
before do
allow(Gitlab::Ci::RunnerUpgradeCheck.instance)
.to receive(:check_runner_upgrade_status)
.and_return({ not_available: nil })
.to receive(:check_runner_upgrade_suggestion)
.and_return([nil, :not_available])
end
describe do

View File

@ -1,16 +1,15 @@
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import mountComponent, { mountComponentWithSlots } from 'helpers/vue_mount_component_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { GlButton } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import reportSection from '~/reports/components/report_section.vue';
import ReportItem from '~/reports/components/report_item.vue';
import ReportSection from '~/reports/components/report_section.vue';
describe('Report section', () => {
let vm;
describe('ReportSection component', () => {
let wrapper;
const ReportSection = Vue.extend(reportSection);
const findCollapseButton = () => wrapper.findByTestId('report-section-expand-button');
const findButton = () => wrapper.findComponent(GlButton);
const findPopover = () => wrapper.findComponent(HelpPopover);
const findReportSection = () => wrapper.find('.js-report-section-container');
const resolvedIssues = [
{
@ -33,34 +32,24 @@ describe('Report section', () => {
alwaysOpen: false,
};
const createComponent = (props) => {
wrapper = extendedWrapper(
mount(reportSection, {
propsData: {
...defaultProps,
...props,
},
}),
);
return wrapper;
const createComponent = ({ props = {}, data = {}, slots = {} } = {}) => {
wrapper = mountExtended(ReportSection, {
propsData: {
...defaultProps,
...props,
},
data() {
return data;
},
slots,
});
};
afterEach(() => {
if (vm) {
vm.$destroy();
vm = null;
}
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
wrapper.destroy();
});
describe('computed', () => {
beforeEach(() => {
vm = mountComponent(ReportSection, defaultProps);
});
describe('isCollapsible', () => {
const testMatrix = [
{ hasIssues: false, alwaysOpen: false, isCollapsible: false },
@ -73,12 +62,10 @@ describe('Report section', () => {
const issues = hasIssues ? 'has issues' : 'has no issues';
const open = alwaysOpen ? 'is always open' : 'is not always open';
it(`is ${isCollapsible}, if the report ${issues} and ${open}`, async () => {
vm.hasIssues = hasIssues;
vm.alwaysOpen = alwaysOpen;
it(`is ${isCollapsible}, if the report ${issues} and ${open}`, () => {
createComponent({ props: { hasIssues, alwaysOpen } });
await nextTick();
expect(vm.isCollapsible).toBe(isCollapsible);
expect(wrapper.vm.isCollapsible).toBe(isCollapsible);
});
});
});
@ -95,12 +82,10 @@ describe('Report section', () => {
const issues = isCollapsed ? 'is collapsed' : 'is not collapsed';
const open = alwaysOpen ? 'is always open' : 'is not always open';
it(`is ${isExpanded}, if the report ${issues} and ${open}`, async () => {
vm.isCollapsed = isCollapsed;
vm.alwaysOpen = alwaysOpen;
it(`is ${isExpanded}, if the report ${issues} and ${open}`, () => {
createComponent({ props: { alwaysOpen }, data: { isCollapsed } });
await nextTick();
expect(vm.isExpanded).toBe(isExpanded);
expect(wrapper.vm.isExpanded).toBe(isExpanded);
});
});
});
@ -108,110 +93,105 @@ describe('Report section', () => {
describe('when it is loading', () => {
it('should render loading indicator', () => {
vm = mountComponent(ReportSection, {
component: '',
status: 'LOADING',
loadingText: 'Loading Code Quality report',
errorText: 'foo',
successText: 'Code quality improved on 1 point and degraded on 1 point',
hasIssues: false,
createComponent({
props: {
component: '',
status: 'LOADING',
loadingText: 'Loading Code Quality report',
errorText: 'foo',
successText: 'Code quality improved on 1 point and degraded on 1 point',
hasIssues: false,
},
});
expect(vm.$el.textContent.trim()).toEqual('Loading Code Quality report');
expect(wrapper.text()).toBe('Loading Code Quality report');
});
});
describe('with success status', () => {
beforeEach(() => {
vm = mountComponent(ReportSection, {
...defaultProps,
hasIssues: true,
});
});
it('should render provided data', () => {
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
createComponent({ props: { hasIssues: true } });
expect(wrapper.find('.js-code-text').text()).toBe(
'Code quality improved on 1 point and degraded on 1 point',
);
expect(vm.$el.querySelectorAll('.report-block-container li').length).toEqual(
resolvedIssues.length,
);
expect(wrapper.findAllComponents(ReportItem)).toHaveLength(resolvedIssues.length);
});
describe('toggleCollapsed', () => {
const hiddenCss = { display: 'none' };
it('toggles issues', async () => {
vm.$el.querySelector('button').click();
createComponent({ props: { hasIssues: true } });
await nextTick();
expect(vm.$el.querySelector('.js-report-section-container')).not.toHaveCss(hiddenCss);
expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Collapse');
await findButton().trigger('click');
vm.$el.querySelector('button').click();
expect(findReportSection().isVisible()).toBe(true);
expect(findButton().text()).toBe('Collapse');
await nextTick();
expect(vm.$el.querySelector('.js-report-section-container')).toHaveCss(hiddenCss);
expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Expand');
await findButton().trigger('click');
expect(findReportSection().isVisible()).toBe(false);
expect(findButton().text()).toBe('Expand');
});
it('is always expanded, if always-open is set to true', async () => {
vm.alwaysOpen = true;
await nextTick();
expect(vm.$el.querySelector('.js-report-section-container')).not.toHaveCss(hiddenCss);
expect(vm.$el.querySelector('button')).toBeNull();
it('is always expanded, if always-open is set to true', () => {
createComponent({ props: { hasIssues: true, alwaysOpen: true } });
expect(findReportSection().isVisible()).toBe(true);
expect(findButton().exists()).toBe(false);
});
});
});
describe('snowplow events', () => {
it('does emit an event on issue toggle if the shouldEmitToggleEvent prop does exist', async () => {
createComponent({ hasIssues: true, shouldEmitToggleEvent: true });
it('does emit an event on issue toggle if the shouldEmitToggleEvent prop does exist', () => {
createComponent({ props: { hasIssues: true, shouldEmitToggleEvent: true } });
expect(wrapper.emitted().toggleEvent).toBeUndefined();
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
findCollapseButton().trigger('click');
await nextTick();
expect(wrapper.emitted().toggleEvent).toHaveLength(1);
findButton().trigger('click');
expect(wrapper.emitted('toggleEvent')).toEqual([[]]);
});
it('does not emit an event on issue toggle if the shouldEmitToggleEvent prop does not exist', async () => {
createComponent({ hasIssues: true });
it('does not emit an event on issue toggle if the shouldEmitToggleEvent prop does not exist', () => {
createComponent({ props: { hasIssues: true } });
expect(wrapper.emitted().toggleEvent).toBeUndefined();
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
findCollapseButton().trigger('click');
await nextTick();
expect(wrapper.emitted().toggleEvent).toBeUndefined();
findButton().trigger('click');
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
});
it('does not emit an event if always-open is set to true', async () => {
createComponent({ alwaysOpen: true, hasIssues: true, shouldEmitToggleEvent: true });
it('does not emit an event if always-open is set to true', () => {
createComponent({
props: { alwaysOpen: true, hasIssues: true, shouldEmitToggleEvent: true },
});
await nextTick();
expect(wrapper.emitted().toggleEvent).toBeUndefined();
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
});
});
describe('with failed request', () => {
it('should render error indicator', () => {
vm = mountComponent(ReportSection, {
component: '',
status: 'ERROR',
loadingText: 'Loading Code Quality report',
errorText: 'Failed to load Code Quality report',
successText: 'Code quality improved on 1 point and degraded on 1 point',
hasIssues: false,
createComponent({
props: {
component: '',
status: 'ERROR',
loadingText: 'Loading Code Quality report',
errorText: 'Failed to load Code Quality report',
successText: 'Code quality improved on 1 point and degraded on 1 point',
hasIssues: false,
},
});
expect(vm.$el.textContent.trim()).toEqual('Failed to load Code Quality report');
expect(wrapper.text()).toBe('Failed to load Code Quality report');
});
});
describe('with action buttons passed to the slot', () => {
beforeEach(() => {
vm = mountComponentWithSlots(ReportSection, {
createComponent({
props: {
status: 'SUCCESS',
successText: 'success',
@ -224,17 +204,17 @@ describe('Report section', () => {
});
it('should render the passed button', () => {
expect(vm.$el.textContent.trim()).toContain('Action!');
expect(wrapper.text()).toContain('Action!');
});
it('should still render the expand/collapse button', () => {
expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
expect(findButton().text()).toBe('Expand');
});
});
describe('Success and Error slots', () => {
const createComponentWithSlots = (status) => {
vm = mountComponentWithSlots(ReportSection, {
createComponent({
props: {
status,
hasIssues: true,
@ -250,25 +230,25 @@ describe('Report section', () => {
it('only renders success slot when status is "SUCCESS"', () => {
createComponentWithSlots('SUCCESS');
expect(vm.$el.textContent.trim()).toContain('This is a success');
expect(vm.$el.textContent.trim()).not.toContain('This is an error');
expect(vm.$el.textContent.trim()).not.toContain('This is loading');
expect(wrapper.text()).toContain('This is a success');
expect(wrapper.text()).not.toContain('This is an error');
expect(wrapper.text()).not.toContain('This is loading');
});
it('only renders error slot when status is "ERROR"', () => {
createComponentWithSlots('ERROR');
expect(vm.$el.textContent.trim()).toContain('This is an error');
expect(vm.$el.textContent.trim()).not.toContain('This is a success');
expect(vm.$el.textContent.trim()).not.toContain('This is loading');
expect(wrapper.text()).toContain('This is an error');
expect(wrapper.text()).not.toContain('This is a success');
expect(wrapper.text()).not.toContain('This is loading');
});
it('only renders loading slot when status is "LOADING"', () => {
createComponentWithSlots('LOADING');
expect(vm.$el.textContent.trim()).toContain('This is loading');
expect(vm.$el.textContent.trim()).not.toContain('This is an error');
expect(vm.$el.textContent.trim()).not.toContain('This is a success');
expect(wrapper.text()).toContain('This is loading');
expect(wrapper.text()).not.toContain('This is an error');
expect(wrapper.text()).not.toContain('This is a success');
});
});
@ -280,9 +260,7 @@ describe('Report section', () => {
};
beforeEach(() => {
createComponent({
popoverOptions: options,
});
createComponent({ props: { popoverOptions: options } });
});
it('popover is shown with options', () => {
@ -292,7 +270,7 @@ describe('Report section', () => {
describe('when popover options are not defined', () => {
beforeEach(() => {
createComponent({ popoverOptions: {} });
createComponent({ props: { popoverOptions: {} } });
});
it('popover is not shown', () => {

View File

@ -229,6 +229,12 @@ describe('Blob content viewer component', () => {
expect(LineHighlighter).toHaveBeenCalled();
});
it('does not load the LineHighlighter for RichViewers', async () => {
mockAxios.onGet(legacyViewerUrl).replyOnce(httpStatusCodes.OK, 'test');
await createComponent({ blob: { ...richViewerMock, fileType, highlightJs } });
expect(LineHighlighter).not.toHaveBeenCalled();
});
it('scrolls to the hash', async () => {
mockAxios.onGet(legacyViewerUrl).replyOnce(httpStatusCodes.OK, 'test');
await createComponent({ blob: { ...simpleViewerMock, fileType, highlightJs } });

View File

@ -1,80 +1,71 @@
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
import { mountComponentWithSlots } from 'helpers/vue_mount_component_helper';
import dropdownButtonComponent from '~/vue_shared/components/dropdown/dropdown_button.vue';
describe('DropdownButton component', () => {
let wrapper;
const defaultLabel = 'Select';
const customLabel = 'Select project';
const defaultLabel = 'Select';
const customLabel = 'Select project';
const createComponent = (props, slots = {}) => {
const Component = Vue.extend(dropdownButtonComponent);
return mountComponentWithSlots(Component, { props, slots });
};
describe('DropdownButtonComponent', () => {
let vm;
beforeEach(() => {
vm = createComponent();
});
const createComponent = (props, slots = {}) => {
wrapper = mount(DropdownButton, { propsData: props, slots });
};
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
describe('computed', () => {
describe('dropdownToggleText', () => {
it('returns default toggle text', () => {
expect(vm.toggleText).toBe(defaultLabel);
createComponent();
expect(wrapper.vm.toggleText).toBe(defaultLabel);
});
it('returns custom toggle text when provided via props', () => {
const vmEmptyLabels = createComponent({ toggleText: customLabel });
createComponent({ toggleText: customLabel });
expect(vmEmptyLabels.toggleText).toBe(customLabel);
vmEmptyLabels.$destroy();
expect(wrapper.vm.toggleText).toBe(customLabel);
});
});
});
describe('template', () => {
it('renders component container element of type `button`', () => {
expect(vm.$el.nodeName).toBe('BUTTON');
createComponent();
expect(wrapper.element.nodeName).toBe('BUTTON');
});
it('renders component container element with required data attributes', () => {
expect(vm.$el.dataset.abilityName).toBe(vm.abilityName);
expect(vm.$el.dataset.fieldName).toBe(vm.fieldName);
expect(vm.$el.dataset.issueUpdate).toBe(vm.updatePath);
expect(vm.$el.dataset.labels).toBe(vm.labelsPath);
expect(vm.$el.dataset.namespacePath).toBe(vm.namespace);
expect(vm.$el.dataset.showAny).not.toBeDefined();
createComponent();
expect(wrapper.element.dataset.abilityName).toBe(wrapper.vm.abilityName);
expect(wrapper.element.dataset.fieldName).toBe(wrapper.vm.fieldName);
expect(wrapper.element.dataset.issueUpdate).toBe(wrapper.vm.updatePath);
expect(wrapper.element.dataset.labels).toBe(wrapper.vm.labelsPath);
expect(wrapper.element.dataset.namespacePath).toBe(wrapper.vm.namespace);
expect(wrapper.element.dataset.showAny).toBeUndefined();
});
it('renders dropdown toggle text element', () => {
const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text');
createComponent();
expect(dropdownToggleTextEl).not.toBeNull();
expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel);
expect(wrapper.find('.dropdown-toggle-text').text()).toBe(defaultLabel);
});
it('renders dropdown button icon', () => {
const dropdownIconEl = vm.$el.querySelector('[data-testid="chevron-down-icon"]');
createComponent();
expect(dropdownIconEl).not.toBeNull();
expect(wrapper.find('[data-testid="chevron-down-icon"]').exists()).toBe(true);
});
it('renders slot, if default slot exists', () => {
vm = createComponent(
{},
{
default: ['Lorem Ipsum Dolar'],
},
);
createComponent({}, { default: ['Lorem Ipsum Dolar'] });
expect(vm.$el.querySelector('.dropdown-toggle-text')).toBeNull();
expect(vm.$el).toHaveText('Lorem Ipsum Dolar');
expect(wrapper.find('.dropdown-toggle-text').exists()).toBe(false);
expect(wrapper.text()).toBe('Lorem Ipsum Dolar');
});
});
});

View File

@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
using RSpec::Parameterized::TableSyntax
describe '#check_runner_upgrade_status' do
subject(:result) { described_class.instance.check_runner_upgrade_status(runner_version) }
describe '#check_runner_upgrade_suggestion' do
subject(:result) { described_class.instance.check_runner_upgrade_suggestion(runner_version) }
let(:gitlab_version) { '14.1.1' }
let(:parsed_runner_version) { ::Gitlab::VersionInfo.parse(runner_version, parse_suffix: true) }
@ -26,7 +26,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
end
it 'returns :error' do
is_expected.to eq({ error: parsed_runner_version })
is_expected.to eq([parsed_runner_version, :error])
end
end
@ -53,7 +53,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { 'v14.0.1' }
it 'returns :not_available' do
is_expected.to eq({ not_available: parsed_runner_version })
is_expected.to eq([parsed_runner_version, :not_available])
end
end
end
@ -68,7 +68,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { nil }
it 'returns :invalid_version' do
is_expected.to match({ invalid_version: anything })
is_expected.to match([anything, :invalid_version])
end
end
@ -76,7 +76,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { 'junk' }
it 'returns :invalid_version' do
is_expected.to match({ invalid_version: anything })
is_expected.to match([anything, :invalid_version])
end
end
@ -87,7 +87,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { 'v14.2.0' }
it 'returns :not_available' do
is_expected.to eq({ not_available: parsed_runner_version })
is_expected.to eq([parsed_runner_version, :not_available])
end
end
end
@ -96,7 +96,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:gitlab_version) { '14.0.1' }
context 'with valid params' do
where(:runner_version, :expected_result, :expected_suggested_version) do
where(:runner_version, :expected_status, :expected_suggested_version) do
'v15.0.0' | :not_available | '15.0.0' # not available since the GitLab instance is still on 14.x, a major version might be incompatible, and a patch upgrade is not available
'v14.1.0-rc3' | :recommended | '14.1.1' # recommended since even though the GitLab instance is still on 14.0.x, there is a patch release (14.1.1) available which might contain security fixes
'v14.1.0~beta.1574.gf6ea9389' | :recommended | '14.1.1' # suffixes are correctly handled
@ -116,7 +116,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
end
with_them do
it { is_expected.to eq({ expected_result => Gitlab::VersionInfo.parse(expected_suggested_version) }) }
it { is_expected.to eq([Gitlab::VersionInfo.parse(expected_suggested_version), expected_status]) }
end
end
end
@ -125,7 +125,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:gitlab_version) { '13.9.0' }
context 'with valid params' do
where(:runner_version, :expected_result, :expected_suggested_version) do
where(:runner_version, :expected_status, :expected_suggested_version) do
'v14.0.0' | :recommended | '14.0.2' # recommended upgrade since 14.0.2 is available, even though the GitLab instance is still on 13.x and a major version might be incompatible
'v13.10.1' | :not_available | '13.10.1' # not available since 13.10.1 is already ahead of GitLab instance version and is the latest patch update for 13.10.x
'v13.10.0' | :recommended | '13.10.1' # recommended upgrade since 13.10.1 is available
@ -136,7 +136,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
end
with_them do
it { is_expected.to eq({ expected_result => Gitlab::VersionInfo.parse(expected_suggested_version) }) }
it { is_expected.to eq([Gitlab::VersionInfo.parse(expected_suggested_version), expected_status]) }
end
end
end
@ -152,7 +152,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { '14.11.0~beta.29.gd0c550e3' }
it 'recommends 15.1.0 since 14.11 is an unknown release and 15.1.0 is available' do
is_expected.to eq({ recommended: Gitlab::VersionInfo.new(15, 1, 0) })
is_expected.to eq([Gitlab::VersionInfo.new(15, 1, 0), :recommended])
end
end
end

View File

@ -124,6 +124,7 @@ RSpec.describe Participable do
end
let(:readable) { true }
let(:project) { build(:project, :public) }
it 'returns the list of participants' do
model.participant(:foo)
@ -132,7 +133,6 @@ RSpec.describe Participable do
user1 = build(:user)
user2 = build(:user)
user3 = build(:user)
project = build(:project, :public)
instance = model.new
allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
@ -155,7 +155,6 @@ RSpec.describe Participable do
instance = model.new
user1 = build(:user)
user2 = build(:user)
project = build(:project, :public)
allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
allow(instance).to receive(:bar).and_return(user2)
@ -164,6 +163,29 @@ RSpec.describe Participable do
expect(instance.visible_participants(user1)).to be_empty
end
end
context 'with multiple system notes from the same author and mentioned_users' do
let!(:user1) { create(:user) }
let!(:user2) { create(:user) }
it 'skips expensive checks if the author is aleady in participants list' do
model.participant(:notes)
instance = model.new
note1 = create(:system_note, author: user1)
note2 = create(:system_note, author: user1) # only skip system notes with no mentioned users
note3 = create(:system_note, author: user1, note: "assigned to #{user2.to_reference}")
note4 = create(:note, author: user2)
allow(instance).to receive(:project).and_return(project)
allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
allow(instance).to receive(:notes).and_return([note1, note2, note3, note4])
allow(Ability).to receive(:allowed?).with(anything, :read_project, anything).and_return(true)
allow(Ability).to receive(:allowed?).with(anything, :read_note, anything).exactly(3).times.and_return(true)
expect(instance.visible_participants(user1)).to match_array [user1, user2]
end
end
end
describe '#participant?' do

View File

@ -37,7 +37,7 @@ RSpec.describe 'Query.runners' do
end
before do
allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status)
allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_suggestion)
post_graphql(query, current_user: current_user)
end

View File

@ -15,8 +15,8 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute'
stub_const('Ci::Runners::ReconcileExistingRunnerVersionsService::VERSION_BATCH_SIZE', 1)
allow(::Gitlab::Ci::RunnerUpgradeCheck.instance)
.to receive(:check_runner_upgrade_status)
.and_return({ recommended: ::Gitlab::VersionInfo.new(14, 0, 2) })
.to receive(:check_runner_upgrade_suggestion)
.and_return([::Gitlab::VersionInfo.new(14, 0, 2), :recommended])
end
context 'with runner with new version' do
@ -26,9 +26,9 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute'
before do
allow(::Gitlab::Ci::RunnerUpgradeCheck.instance)
.to receive(:check_runner_upgrade_status)
.to receive(:check_runner_upgrade_suggestion)
.with('14.0.2')
.and_return({ not_available: ::Gitlab::VersionInfo.new(14, 0, 2) })
.and_return([::Gitlab::VersionInfo.new(14, 0, 2), :not_available])
.once
end
@ -59,8 +59,8 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute'
before do
allow(::Gitlab::Ci::RunnerUpgradeCheck.instance)
.to receive(:check_runner_upgrade_status)
.and_return({ not_available: ::Gitlab::VersionInfo.new(14, 0, 2) })
.to receive(:check_runner_upgrade_suggestion)
.and_return([::Gitlab::VersionInfo.new(14, 0, 2), :not_available])
end
it 'deletes orphan ci_runner_versions entry', :aggregate_failures do
@ -81,8 +81,8 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute'
context 'with no runner version changes' do
before do
allow(::Gitlab::Ci::RunnerUpgradeCheck.instance)
.to receive(:check_runner_upgrade_status)
.and_return({ not_available: ::Gitlab::VersionInfo.new(14, 0, 1) })
.to receive(:check_runner_upgrade_suggestion)
.and_return([::Gitlab::VersionInfo.new(14, 0, 1), :not_available])
end
it 'does not modify ci_runner_versions entries', :aggregate_failures do
@ -101,8 +101,8 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute'
context 'with failing version check' do
before do
allow(::Gitlab::Ci::RunnerUpgradeCheck.instance)
.to receive(:check_runner_upgrade_status)
.and_return({ error: ::Gitlab::VersionInfo.new(14, 0, 1) })
.to receive(:check_runner_upgrade_suggestion)
.and_return([::Gitlab::VersionInfo.new(14, 0, 1), :error])
end
it 'makes no changes to ci_runner_versions', :aggregate_failures do