Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c38fb401d2
commit
35b5da5522
|
@ -488,6 +488,8 @@ That's all of the required database changes.
|
|||
|
||||
module Geo
|
||||
class CoolWidgetState < ApplicationRecord
|
||||
include EachBatch
|
||||
|
||||
self.primary_key = :cool_widget_id
|
||||
|
||||
belongs_to :cool_widget, inverse_of: :cool_widget_state
|
||||
|
|
|
@ -452,6 +452,8 @@ That's all of the required database changes.
|
|||
``` ruby
|
||||
module Geo
|
||||
class CoolWidgetState < ApplicationRecord
|
||||
include EachBatch
|
||||
|
||||
self.primary_key = :cool_widget_id
|
||||
|
||||
belongs_to :cool_widget, inverse_of: :cool_widget_state
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -2,7 +2,7 @@
|
|||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '~> 6.1.4.1'
|
||||
gem 'rails', '~> 6.1.4.4'
|
||||
|
||||
gem 'bootsnap', '~> 1.9.1', require: false
|
||||
|
||||
|
|
114
Gemfile.lock
114
Gemfile.lock
|
@ -11,63 +11,63 @@ GEM
|
|||
RedCloth (4.3.2)
|
||||
acme-client (2.0.9)
|
||||
faraday (>= 0.17, < 2.0.0)
|
||||
actioncable (6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
actioncable (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
activejob (= 6.1.4.1)
|
||||
activerecord (= 6.1.4.1)
|
||||
activestorage (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
actionmailbox (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
actionview (= 6.1.4.1)
|
||||
activejob (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
actionmailer (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.4.1)
|
||||
actionview (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
actionpack (6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
activerecord (= 6.1.4.1)
|
||||
activestorage (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
actiontext (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
actionview (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
activejob (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
activerecord (6.1.4.1)
|
||||
activemodel (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
activemodel (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activerecord (6.1.4.4)
|
||||
activemodel (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activerecord-explain-analyze (0.1.0)
|
||||
activerecord (>= 4)
|
||||
pg
|
||||
activestorage (6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
activejob (= 6.1.4.1)
|
||||
activerecord (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
activestorage (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
marcel (~> 1.0.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (6.1.4.1)
|
||||
activesupport (6.1.4.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
@ -511,7 +511,7 @@ GEM
|
|||
omniauth (~> 1.3)
|
||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||
rubyntlm (~> 0.5)
|
||||
globalid (0.5.2)
|
||||
globalid (1.0.0)
|
||||
activesupport (>= 5.0)
|
||||
gon (6.4.0)
|
||||
actionpack (>= 3.0.20)
|
||||
|
@ -739,7 +739,7 @@ GEM
|
|||
lumberjack (1.2.7)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (1.0.1)
|
||||
marcel (1.0.2)
|
||||
marginalia (1.10.0)
|
||||
actionpack (>= 2.3)
|
||||
activerecord (>= 2.3)
|
||||
|
@ -971,20 +971,20 @@ GEM
|
|||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-timeout (0.5.2)
|
||||
rails (6.1.4.1)
|
||||
actioncable (= 6.1.4.1)
|
||||
actionmailbox (= 6.1.4.1)
|
||||
actionmailer (= 6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
actiontext (= 6.1.4.1)
|
||||
actionview (= 6.1.4.1)
|
||||
activejob (= 6.1.4.1)
|
||||
activemodel (= 6.1.4.1)
|
||||
activerecord (= 6.1.4.1)
|
||||
activestorage (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
rails (6.1.4.4)
|
||||
actioncable (= 6.1.4.4)
|
||||
actionmailbox (= 6.1.4.4)
|
||||
actionmailer (= 6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
actiontext (= 6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activemodel (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.4.1)
|
||||
railties (= 6.1.4.4)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
|
@ -998,9 +998,9 @@ GEM
|
|||
rails-i18n (6.0.0)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 7)
|
||||
railties (6.1.4.1)
|
||||
actionpack (= 6.1.4.1)
|
||||
activesupport (= 6.1.4.1)
|
||||
railties (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
method_source
|
||||
rake (>= 0.13)
|
||||
thor (~> 1.0)
|
||||
|
@ -1381,7 +1381,7 @@ GEM
|
|||
nokogiri (~> 1.8)
|
||||
yajl-ruby (1.4.1)
|
||||
yard (0.9.26)
|
||||
zeitwerk (2.5.1)
|
||||
zeitwerk (2.5.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -1589,7 +1589,7 @@ DEPENDENCIES
|
|||
rack-oauth2 (~> 1.16.0)
|
||||
rack-proxy (~> 0.6.0)
|
||||
rack-timeout (~> 0.5.1)
|
||||
rails (~> 6.1.4.1)
|
||||
rails (~> 6.1.4.4)
|
||||
rails-controller-testing
|
||||
rails-i18n (~> 6.0)
|
||||
rainbow (~> 3.0)
|
||||
|
|
|
@ -121,4 +121,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
|
|||
|
||||
## Is it awesome?
|
||||
|
||||
[These people](https://twitter.com/gitlab/likes) seem to like it.
|
||||
[These people](https://twitter.com/gitlab/followers) seem to like it.
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<script>
|
||||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { sprintf } from '~/locale';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { I18N_DETAILS_TITLE } from '../constants';
|
||||
import RunnerTypeBadge from './runner_type_badge.vue';
|
||||
import RunnerStatusBadge from './runner_status_badge.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlSprintf,
|
||||
TimeAgo,
|
||||
RunnerTypeBadge,
|
||||
RunnerStatusBadge,
|
||||
},
|
||||
props: {
|
||||
runner: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
paused() {
|
||||
return !this.runner.active;
|
||||
},
|
||||
heading() {
|
||||
const id = getIdFromGraphQLId(this.runner.id);
|
||||
return sprintf(I18N_DETAILS_TITLE, { runner_id: id });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100">
|
||||
<runner-status-badge :runner="runner" />
|
||||
<runner-type-badge v-if="runner" :type="runner.runnerType" />
|
||||
<template v-if="runner.createdAt">
|
||||
<gl-sprintf :message="__('%{runner} created %{timeago}')">
|
||||
<template #runner>
|
||||
<strong>{{ heading }}</strong>
|
||||
</template>
|
||||
<template #timeago>
|
||||
<time-ago :time="runner.createdAt" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</template>
|
||||
<template v-else>
|
||||
<strong>{{ heading }}</strong>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
|
@ -1,54 +0,0 @@
|
|||
<script>
|
||||
import { GlAlert, GlLink } from '@gitlab/ui';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { s__ } from '~/locale';
|
||||
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../constants';
|
||||
|
||||
const ALERT_DATA = {
|
||||
[INSTANCE_TYPE]: {
|
||||
message: s__(
|
||||
'Runners|This runner is available to all groups and projects in your GitLab instance.',
|
||||
),
|
||||
anchor: 'shared-runners',
|
||||
},
|
||||
[GROUP_TYPE]: {
|
||||
message: s__('Runners|This runner is available to all projects and subgroups in a group.'),
|
||||
anchor: 'group-runners',
|
||||
},
|
||||
[PROJECT_TYPE]: {
|
||||
message: s__('Runners|This runner is associated with one or more projects.'),
|
||||
anchor: 'specific-runners',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlAlert,
|
||||
GlLink,
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
validator(type) {
|
||||
return Boolean(ALERT_DATA[type]);
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
alert() {
|
||||
return ALERT_DATA[this.type];
|
||||
},
|
||||
helpHref() {
|
||||
return helpPagePath('ci/runners/runners_scope', { anchor: this.alert.anchor });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-alert v-if="alert" variant="info" :dismissible="false">
|
||||
{{ alert.message }}
|
||||
<gl-link :href="helpHref">{{ __('Learn more.') }}</gl-link>
|
||||
</gl-alert>
|
||||
</template>
|
|
@ -9,4 +9,6 @@ fragment RunnerDetailsShared on CiRunner {
|
|||
description
|
||||
maximumTimeout
|
||||
tagList
|
||||
createdAt
|
||||
status(legacyMode: null)
|
||||
}
|
||||
|
|
|
@ -2,19 +2,16 @@
|
|||
import createFlash from '~/flash';
|
||||
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { sprintf } from '~/locale';
|
||||
import RunnerTypeAlert from '../components/runner_type_alert.vue';
|
||||
import RunnerTypeBadge from '../components/runner_type_badge.vue';
|
||||
import RunnerHeader from '../components/runner_header.vue';
|
||||
import RunnerUpdateForm from '../components/runner_update_form.vue';
|
||||
import { I18N_DETAILS_TITLE, I18N_FETCH_ERROR } from '../constants';
|
||||
import { I18N_FETCH_ERROR } from '../constants';
|
||||
import getRunnerQuery from '../graphql/get_runner.query.graphql';
|
||||
import { captureException } from '../sentry_utils';
|
||||
|
||||
export default {
|
||||
name: 'RunnerDetailsApp',
|
||||
components: {
|
||||
RunnerTypeAlert,
|
||||
RunnerTypeBadge,
|
||||
RunnerHeader,
|
||||
RunnerUpdateForm,
|
||||
},
|
||||
props: {
|
||||
|
@ -43,11 +40,6 @@ export default {
|
|||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
pageTitle() {
|
||||
return sprintf(I18N_DETAILS_TITLE, { runner_id: this.runnerId });
|
||||
},
|
||||
},
|
||||
errorCaptured(error) {
|
||||
this.reportToSentry(error);
|
||||
},
|
||||
|
@ -60,12 +52,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="page-title">
|
||||
{{ pageTitle }} <runner-type-badge v-if="runner" :type="runner.runnerType" />
|
||||
</h2>
|
||||
|
||||
<runner-type-alert v-if="runner" :type="runner.runnerType" />
|
||||
|
||||
<runner-header v-if="runner" :runner="runner" />
|
||||
<runner-update-form :runner="runner" class="gl-my-5" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
- placeholder = local_assigns[:placeholder] || _('Search or filter results...')
|
||||
- block_css_class = type != :productivity_analytics ? 'row-content-block second-block' : ''
|
||||
- is_epic_board = board&.to_type == "EpicBoard"
|
||||
- if @group.present?
|
||||
- ff_resource = @group
|
||||
- else
|
||||
- ff_resource = board&.resource_parent&.group
|
||||
|
||||
- if is_epic_board
|
||||
- user_can_admin_list = can?(current_user, :admin_epic_board_list, board.resource_parent)
|
||||
- elsif board
|
||||
|
@ -26,7 +31,7 @@
|
|||
= check_box_tag checkbox_id, nil, false, class: "check-all-issues left"
|
||||
- if is_epic_board
|
||||
#js-board-filtered-search{ data: { full_path: @group&.full_path } }
|
||||
- elsif Feature.enabled?(:issue_boards_filtered_search, board&.resource_parent) && board
|
||||
- elsif Feature.enabled?(:issue_boards_filtered_search, ff_resource) && board
|
||||
#js-issue-board-filtered-search
|
||||
- else
|
||||
.issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name: ci_retry_downstream_pipeline
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76115
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347424
|
||||
milestone: '14.16'
|
||||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
||||
|
|
|
@ -876,6 +876,9 @@ msgstr ""
|
|||
msgid "%{rotation} has been recalculated with the remaining participants. Please review the new setup for %{rotation}. It is recommended that you reach out to the current on-call responder to ensure continuity of on-call coverage."
|
||||
msgstr ""
|
||||
|
||||
msgid "%{runner} created %{timeago}"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{scope} results for term '%{term}'"
|
||||
msgstr ""
|
||||
|
||||
|
@ -30741,9 +30744,6 @@ msgstr ""
|
|||
msgid "Runners|This runner has never contacted this instance"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|This runner is associated with one or more projects."
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|This runner is associated with specific projects."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -468,9 +468,9 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'runner page title', :js do
|
||||
it 'contains the runner id' do
|
||||
expect(find('.page-title')).to have_content("Runner ##{runner.id}")
|
||||
describe 'runner header', :js do
|
||||
it 'contains the runner status, type and id' do
|
||||
expect(page).to have_content("never contacted shared Runner ##{runner.id} created")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import { GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants';
|
||||
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
||||
import RunnerHeader from '~/runner/components/runner_header.vue';
|
||||
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
|
||||
import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
|
||||
|
||||
import { runnerData } from '../mock_data';
|
||||
|
||||
const mockRunner = runnerData.data.runner;
|
||||
|
||||
describe('RunnerHeader', () => {
|
||||
let wrapper;
|
||||
|
||||
const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
|
||||
const findRunnerStatusBadge = () => wrapper.findComponent(RunnerStatusBadge);
|
||||
const findTimeAgo = () => wrapper.findComponent(TimeAgo);
|
||||
|
||||
const createComponent = ({ runner = {}, mountFn = shallowMount } = {}) => {
|
||||
wrapper = mountFn(RunnerHeader, {
|
||||
propsData: {
|
||||
runner: {
|
||||
...mockRunner,
|
||||
...runner,
|
||||
},
|
||||
},
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
TimeAgo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('displays the runner status', () => {
|
||||
createComponent({
|
||||
mountFn: mount,
|
||||
runner: {
|
||||
status: STATUS_ONLINE,
|
||||
},
|
||||
});
|
||||
|
||||
expect(findRunnerStatusBadge().text()).toContain(`online`);
|
||||
});
|
||||
|
||||
it('displays the runner type', () => {
|
||||
createComponent({
|
||||
mountFn: mount,
|
||||
runner: {
|
||||
runnerType: GROUP_TYPE,
|
||||
},
|
||||
});
|
||||
|
||||
expect(findRunnerTypeBadge().text()).toContain(`group`);
|
||||
});
|
||||
|
||||
it('displays the runner id', () => {
|
||||
createComponent({
|
||||
runner: {
|
||||
id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain(`Runner #99`);
|
||||
});
|
||||
|
||||
it('displays the runner creation time', () => {
|
||||
createComponent();
|
||||
|
||||
expect(wrapper.text()).toMatch(/created .+/);
|
||||
expect(findTimeAgo().props('time')).toBe(mockRunner.createdAt);
|
||||
});
|
||||
|
||||
it('does not display runner creation time if createdAt missing', () => {
|
||||
createComponent({
|
||||
runner: {
|
||||
id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
|
||||
createdAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain(`Runner #99`);
|
||||
expect(wrapper.text()).not.toMatch(/created .+/);
|
||||
expect(findTimeAgo().exists()).toBe(false);
|
||||
});
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
import { GlAlert, GlLink } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import RunnerTypeAlert from '~/runner/components/runner_type_alert.vue';
|
||||
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
|
||||
|
||||
describe('RunnerTypeAlert', () => {
|
||||
let wrapper;
|
||||
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findLink = () => wrapper.findComponent(GlLink);
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
wrapper = shallowMount(RunnerTypeAlert, {
|
||||
propsData: {
|
||||
type: INSTANCE_TYPE,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe.each`
|
||||
type | exampleText | anchor
|
||||
${INSTANCE_TYPE} | ${'This runner is available to all groups and projects'} | ${'#shared-runners'}
|
||||
${GROUP_TYPE} | ${'This runner is available to all projects and subgroups in a group'} | ${'#group-runners'}
|
||||
${PROJECT_TYPE} | ${'This runner is associated with one or more projects'} | ${'#specific-runners'}
|
||||
`('When it is an $type level runner', ({ type, exampleText, anchor }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ props: { type } });
|
||||
});
|
||||
|
||||
it('Describes runner type', () => {
|
||||
expect(wrapper.text()).toMatch(exampleText);
|
||||
});
|
||||
|
||||
it(`Shows an "info" variant`, () => {
|
||||
expect(findAlert().props('variant')).toBe('info');
|
||||
});
|
||||
|
||||
it(`Links to anchor "${anchor}"`, () => {
|
||||
expect(findLink().attributes('href')).toBe(`/help/ci/runners/runners_scope${anchor}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When runner type is not correct', () => {
|
||||
it('Does not render content when type is missing', () => {
|
||||
createComponent({ props: { type: undefined } });
|
||||
|
||||
expect(wrapper.html()).toBe('');
|
||||
});
|
||||
|
||||
it('Validation fails for an incorrect type', () => {
|
||||
expect(() => {
|
||||
createComponent({ props: { type: 'NOT_A_TYPE' } });
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -127,7 +127,7 @@ describe('RunnerUpdateForm', () => {
|
|||
await submitFormAndWait();
|
||||
|
||||
// Some fields are not submitted
|
||||
const { ipAddress, runnerType, ...submitted } = mockRunner;
|
||||
const { ipAddress, runnerType, createdAt, status, ...submitted } = mockRunner;
|
||||
|
||||
expectToHaveSubmittedRunnerContaining(submitted);
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import createFlash from '~/flash';
|
||||
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
|
||||
import RunnerHeader from '~/runner/components/runner_header.vue';
|
||||
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
|
||||
import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue';
|
||||
import { captureException } from '~/runner/sentry_utils';
|
||||
|
@ -25,7 +25,7 @@ describe('RunnerDetailsApp', () => {
|
|||
let wrapper;
|
||||
let mockRunnerQuery;
|
||||
|
||||
const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
|
||||
const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
|
||||
|
||||
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
|
||||
wrapper = mountFn(RunnerDetailsApp, {
|
||||
|
@ -40,7 +40,7 @@ describe('RunnerDetailsApp', () => {
|
|||
return waitForPromises();
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
mockRunnerQuery = jest.fn().mockResolvedValue(runnerData);
|
||||
});
|
||||
|
||||
|
@ -56,15 +56,16 @@ describe('RunnerDetailsApp', () => {
|
|||
});
|
||||
|
||||
it('displays the runner id', async () => {
|
||||
await createComponentWithApollo();
|
||||
|
||||
expect(wrapper.text()).toContain(`Runner #${mockRunnerId}`);
|
||||
});
|
||||
|
||||
it('displays the runner type', async () => {
|
||||
await createComponentWithApollo({ mountFn: mount });
|
||||
|
||||
expect(findRunnerTypeBadge().text()).toBe('shared');
|
||||
expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId} created`);
|
||||
});
|
||||
|
||||
it('displays the runner type and status', async () => {
|
||||
await createComponentWithApollo({ mountFn: mount });
|
||||
|
||||
expect(findRunnerHeader().text()).toContain(`never contacted`);
|
||||
expect(findRunnerHeader().text()).toContain(`shared`);
|
||||
});
|
||||
|
||||
describe('When there is an error', () => {
|
||||
|
@ -73,14 +74,14 @@ describe('RunnerDetailsApp', () => {
|
|||
await createComponentWithApollo();
|
||||
});
|
||||
|
||||
it('error is reported to sentry', async () => {
|
||||
it('error is reported to sentry', () => {
|
||||
expect(captureException).toHaveBeenCalledWith({
|
||||
error: new Error('Network error: Error!'),
|
||||
component: 'RunnerDetailsApp',
|
||||
});
|
||||
});
|
||||
|
||||
it('error is shown to the user', async () => {
|
||||
it('error is shown to the user', () => {
|
||||
expect(createFlash).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'permission level for issue mutation is correctly verified' do |raises_for_all_errors = false|
|
||||
before do
|
||||
issue.assignees = []
|
||||
issue.author = user
|
||||
let_it_be(:other_user_author) { create(:user) }
|
||||
|
||||
def issue_attributes(issue)
|
||||
issue.attributes.except(
|
||||
# Description and title can be updated by authors and assignees of the issues
|
||||
'description',
|
||||
'title',
|
||||
# Those fields are calculated or expected to be modified during the mutations
|
||||
'author_id',
|
||||
'updated_at',
|
||||
'updated_by_id',
|
||||
'last_edited_at',
|
||||
'last_edited_by_id',
|
||||
'lock_version',
|
||||
# There were spec failures due to nano-second comparisons
|
||||
# this property isn't changed by any mutation so we don't have to verify it
|
||||
'created_at'
|
||||
)
|
||||
end
|
||||
|
||||
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned|
|
||||
let(:expected) { issue_attributes(issue) }
|
||||
|
||||
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
|
||||
before do
|
||||
issue.assignees = []
|
||||
issue.update!(author: other_user_author)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
|
@ -17,21 +39,25 @@ RSpec.shared_examples 'permission level for issue mutation is correctly verified
|
|||
end
|
||||
|
||||
it 'does not modify issue' do
|
||||
if raises_for_all_errors || raise_for_assigned
|
||||
if raises_for_all_errors || raise_for_assigned_and_author
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
else
|
||||
expect(subject[:issue]).to eq issue
|
||||
expect(issue_attributes(subject[:issue])).to eq expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'even if author of the issue' do
|
||||
before do
|
||||
issue.author = user
|
||||
issue.update!(author: user)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
it 'does not modify issue' do
|
||||
if raises_for_all_errors || raise_for_assigned_and_author
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
else
|
||||
expect(issue_attributes(subject[:issue])).to eq expected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'permission level for merge request mutation is correctly verified' do
|
||||
before do
|
||||
merge_request.assignees = []
|
||||
merge_request.reviewers = []
|
||||
merge_request.author = nil
|
||||
let(:other_user_author) { create(:user) }
|
||||
|
||||
def mr_attributes(mr)
|
||||
mr.attributes.except(
|
||||
# Authors and assignees can edit title, description, target branch and draft status
|
||||
'title',
|
||||
'description',
|
||||
'target_branch',
|
||||
'draft',
|
||||
# Those fields are calculated or expected to be modified during the mutations
|
||||
'author_id',
|
||||
'latest_merge_request_diff_id',
|
||||
'last_edited_at',
|
||||
'last_edited_by_id',
|
||||
'lock_version',
|
||||
'updated_at',
|
||||
'updated_by_id',
|
||||
'merge_status',
|
||||
# There were spec failures due to nano-second comparisons
|
||||
# this property isn't changed by any mutation so we don't have to verify it
|
||||
'created_at'
|
||||
)
|
||||
end
|
||||
|
||||
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned|
|
||||
let(:expected) { mr_attributes(merge_request) }
|
||||
|
||||
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
|
||||
before do
|
||||
merge_request.assignees = []
|
||||
merge_request.reviewers = []
|
||||
merge_request.update!(author: other_user_author)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
|
@ -18,12 +44,12 @@ RSpec.shared_examples 'permission level for merge request mutation is correctly
|
|||
end
|
||||
|
||||
it 'does not modify merge request' do
|
||||
if raise_for_assigned
|
||||
if raise_for_assigned_and_author
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
else
|
||||
# In some cases we simply do nothing instead of raising
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/196241
|
||||
expect(subject[:merge_request]).to eq merge_request
|
||||
expect(mr_attributes(subject[:merge_request])).to eq expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -40,11 +66,17 @@ RSpec.shared_examples 'permission level for merge request mutation is correctly
|
|||
|
||||
context 'even if author of the merge request' do
|
||||
before do
|
||||
merge_request.author = user
|
||||
merge_request.update!(author: user)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
if raise_for_assigned_and_author
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
else
|
||||
# In some cases we simply do nothing instead of raising
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/196241
|
||||
expect(mr_attributes(subject[:merge_request])).to eq expected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue