Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
895563036a
commit
91ef4dcc05
50 changed files with 268 additions and 525 deletions
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import ServiceDeskSetting from './service_desk_setting.vue';
|
||||
import ServiceDeskService from '../services/service_desk_service';
|
||||
import eventHub from '../event_hub';
|
||||
|
@ -122,11 +122,13 @@ export default {
|
|||
this.incomingEmail = data?.service_desk_address;
|
||||
this.showAlert(__('Changes were successfully made.'), 'success');
|
||||
})
|
||||
.catch(() =>
|
||||
.catch(err => {
|
||||
this.showAlert(
|
||||
__('An error occurred while saving the template. Please check if the template exists.'),
|
||||
),
|
||||
)
|
||||
sprintf(__('An error occured while making the changes: %{error}'), {
|
||||
error: err?.response?.data?.message,
|
||||
}),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isTemplateSaving = false;
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
@ -17,6 +17,7 @@ export default {
|
|||
GlFormSelect,
|
||||
GlToggle,
|
||||
GlLoadingIcon,
|
||||
GlSprintf,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
|
@ -60,6 +61,7 @@ export default {
|
|||
selectedTemplate: this.initialSelectedTemplate,
|
||||
outgoingName: this.initialOutgoingName || __('GitLab Support Bot'),
|
||||
projectKey: this.initialProjectKey,
|
||||
baseEmail: this.incomingEmail.replace(this.initialProjectKey, ''),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -123,12 +125,33 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="projectKey" class="form-text text-muted">
|
||||
<gl-sprintf :message="__('Emails sent to %{email} will still be supported')">
|
||||
<template #email>
|
||||
<code>{{ baseEmail }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<gl-loading-icon :inline="true" />
|
||||
<span class="sr-only">{{ __('Fetching incoming email') }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="hasProjectKeySupport">
|
||||
<label for="service-desk-project-suffix" class="mt-3">
|
||||
{{ __('Project name suffix') }}
|
||||
</label>
|
||||
<input id="service-desk-project-suffix" v-model.trim="projectKey" class="form-control" />
|
||||
<span class="form-text text-muted">
|
||||
{{
|
||||
__(
|
||||
'Project name suffix is a user-defined string which will be appended to the project path, and will form the Service Desk email address.',
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<label for="service-desk-template-select" class="mt-3">
|
||||
{{ __('Template to append to all Service Desk issues') }}
|
||||
</label>
|
||||
|
@ -144,19 +167,7 @@ export default {
|
|||
<span class="form-text text-muted">
|
||||
{{ __('Emails sent from Service Desk will have this name') }}
|
||||
</span>
|
||||
<template v-if="hasProjectKeySupport">
|
||||
<label for="service-desk-project-suffix" class="mt-3">
|
||||
{{ __('Project name suffix') }}
|
||||
</label>
|
||||
<input id="service-desk-project-suffix" v-model.trim="projectKey" class="form-control" />
|
||||
<span class="form-text text-muted mb-3">
|
||||
{{
|
||||
__(
|
||||
'Project name suffix is a user-defined string which will be appended to the project path, and will form the Service Desk email address.',
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
<div class="gl-display-flex gl-justify-content-end">
|
||||
<gl-button
|
||||
variant="success"
|
||||
class="gl-mt-5"
|
||||
|
@ -168,4 +179,5 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -90,6 +90,7 @@ export default {
|
|||
:size="12"
|
||||
:title="stateTitle"
|
||||
:aria-label="state"
|
||||
data-testid="referenceIcon"
|
||||
/>
|
||||
{{ displayReference }}
|
||||
</component>
|
||||
|
@ -105,6 +106,7 @@ export default {
|
|||
:title="removeButtonLabel"
|
||||
:aria-label="removeButtonLabel"
|
||||
:disabled="removeDisabled"
|
||||
data-testid="removeBtn"
|
||||
type="button"
|
||||
class="js-issue-token-remove-button"
|
||||
@click="onRemoveRequest"
|
||||
|
|
|
@ -111,7 +111,7 @@ const mixins = {
|
|||
return this.isMergeRequest && this.pipelineStatus && Object.keys(this.pipelineStatus).length;
|
||||
},
|
||||
isOpen() {
|
||||
return this.state === 'opened';
|
||||
return this.state === 'opened' || this.state === 'reopened';
|
||||
},
|
||||
isClosed() {
|
||||
return this.state === 'closed';
|
||||
|
|
|
@ -22,8 +22,8 @@ module Postgresql
|
|||
def self.lag_too_great?(max = 100.megabytes)
|
||||
return false unless in_use?
|
||||
|
||||
lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \
|
||||
"(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint"
|
||||
lag_function = "pg_wal_lsn_diff" \
|
||||
"(pg_current_wal_insert_lsn(), restart_lsn)::bigint"
|
||||
|
||||
# We force the use of a transaction here so the query always goes to the
|
||||
# primary, even when using the EE DB load balancer.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
- add_page_startup_api_call discussions_path(@issue)
|
||||
- add_page_startup_api_call notes_url
|
||||
|
||||
- @gfm_form = true
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/using_a_geo_server.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/using_a_geo_server.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/disaster_recovery/bring_primary_back.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/disaster_recovery/bring_primary_back.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/configuration.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/configuration.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/configuration.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/configuration.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/setup/database.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/setup/database.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/setup/database.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/setup/database.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/disaster_recovery/index.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/disaster_recovery/index.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/docker_registry.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/docker_registry.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/faq.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/faq.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/multiple_servers.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/multiple_servers.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/object_storage.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/object_storage.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/disaster_recovery/planned_failover.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/disaster_recovery/planned_failover.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/security_review.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/security_review.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/operations/fast_ssh_key_lookup.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/operations/fast_ssh_key_lookup.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/troubleshooting.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/troubleshooting.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/tuning.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/tuning.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/updating_the_geo_nodes.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/updating_the_geo_nodes.md).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/geo/replication/using_a_geo_server.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/geo/replication/using_a_geo_server.md).
|
|
@ -48,7 +48,7 @@ To enable the CAS OmniAuth provider you must register your application with your
|
|||
url: 'CAS_SERVER',
|
||||
login_url: '/CAS_PATH/login',
|
||||
service_validate_url: '/CAS_PATH/p3/serviceValidate',
|
||||
logout_url: '/CAS_PATH/logout'} }
|
||||
logout_url: '/CAS_PATH/logout' } }
|
||||
```
|
||||
|
||||
1. Change 'CAS_PATH' to the root of your CAS instance (ie. `cas`).
|
||||
|
|
|
@ -78,7 +78,8 @@ Follow these steps to incorporate the GitHub OAuth 2 app in your GitLab server:
|
|||
For GitHub Enterprise:
|
||||
|
||||
```yaml
|
||||
- { name: 'github', app_id: 'YOUR_APP_ID',
|
||||
- { name: 'github',
|
||||
app_id: 'YOUR_APP_ID',
|
||||
app_secret: 'YOUR_APP_SECRET',
|
||||
url: "https://github.example.com/",
|
||||
args: { scope: 'user:email' } }
|
||||
|
@ -125,7 +126,8 @@ omnibus_gitconfig['system'] = { "http" => ["sslVerify = false"] }
|
|||
For installation from source:
|
||||
|
||||
```yaml
|
||||
- { name: 'github', app_id: 'YOUR_APP_ID',
|
||||
- { name: 'github',
|
||||
app_id: 'YOUR_APP_ID',
|
||||
app_secret: 'YOUR_APP_SECRET',
|
||||
url: "https://github.example.com/",
|
||||
verify_ssl: false,
|
||||
|
|
|
@ -63,7 +63,8 @@ GitLab.com will generate an application ID and secret key for you to use.
|
|||
For installations from source:
|
||||
|
||||
```yaml
|
||||
- { name: 'gitlab', app_id: 'YOUR_APP_ID',
|
||||
- { name: 'gitlab',
|
||||
app_id: 'YOUR_APP_ID',
|
||||
app_secret: 'YOUR_APP_SECRET',
|
||||
args: { scope: 'api' } }
|
||||
```
|
||||
|
|
|
@ -84,7 +84,8 @@ On your GitLab server:
|
|||
For installations from source:
|
||||
|
||||
```yaml
|
||||
- { name: 'google_oauth2', app_id: 'YOUR_APP_ID',
|
||||
- { name: 'google_oauth2',
|
||||
app_id: 'YOUR_APP_ID',
|
||||
app_secret: 'YOUR_APP_SECRET',
|
||||
args: { access_type: 'offline', approval_prompt: '' } }
|
||||
```
|
||||
|
|
|
@ -207,6 +207,7 @@ remove the OmniAuth provider named `kerberos` from your `gitlab.yml` /
|
|||
|
||||
```yaml
|
||||
omniauth:
|
||||
# Rest of configuration omitted
|
||||
# ...
|
||||
providers:
|
||||
- { name: 'kerberos' } # <-- remove this line
|
||||
|
|
|
@ -142,7 +142,7 @@ The chosen OmniAuth provider is now active and can be used to sign in to GitLab
|
|||
|
||||
## Automatically Link Existing Users to OmniAuth Users
|
||||
|
||||
> [Introduced in GitLab 13.4.](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36664)
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36664) in GitLab 13.4.
|
||||
|
||||
You can automatically link OmniAuth users with existing GitLab users if their email addresses match.
|
||||
For example, the following setting is used to enable the auto link feature for both a SAML provider and the Twitter OAuth provider:
|
||||
|
|
|
@ -65,7 +65,8 @@ To enable the Twitter OmniAuth provider you must register your application with
|
|||
For installations from source:
|
||||
|
||||
```yaml
|
||||
- { name: 'twitter', app_id: 'YOUR_APP_ID',
|
||||
- { name: 'twitter',
|
||||
app_id: 'YOUR_APP_ID',
|
||||
app_secret: 'YOUR_APP_SECRET' }
|
||||
```
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ receivers:
|
|||
bearer_token: 9e1cbfcd546896a9ea8be557caf13a76
|
||||
send_resolved: true
|
||||
url: http://192.168.178.31:3001/root/manual_prometheus/prometheus/alerts/notify.json
|
||||
...
|
||||
# Rest of configuration omitted
|
||||
# ...
|
||||
```
|
||||
|
||||
For GitLab to associate your alerts with an [environment](../../ci/environments/index.md),
|
||||
|
|
|
@ -125,7 +125,7 @@ the Agent in subsequent steps. You can create an Agent record either:
|
|||
|
||||
- Through GraphQL: **(PREMIUM ONLY)**
|
||||
|
||||
```json
|
||||
```graphql
|
||||
mutation createAgent {
|
||||
createClusterAgent(input: { projectPath: "path-to/your-awesome-project", name: "<agent-name>" }) {
|
||||
clusterAgent {
|
||||
|
|
|
@ -269,7 +269,7 @@ To add a Kubernetes cluster to your project, group, or instance:
|
|||
|
||||
Copy the `<authentication_token>` value from the output:
|
||||
|
||||
```yaml
|
||||
```plaintext
|
||||
Name: gitlab-token-b5zv4
|
||||
Namespace: kube-system
|
||||
Labels: <none>
|
||||
|
|
|
@ -222,7 +222,8 @@ the environment of the deployed function:
|
|||
|
||||
```yaml
|
||||
provider:
|
||||
...
|
||||
# Other configuration omitted
|
||||
# ...
|
||||
environment:
|
||||
A_VARIABLE: ${env:A_VARIABLE}
|
||||
```
|
||||
|
|
|
@ -92,10 +92,6 @@ module Gitlab
|
|||
@version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
|
||||
end
|
||||
|
||||
def self.postgresql_9_or_less?
|
||||
version.to_f < 10
|
||||
end
|
||||
|
||||
def self.postgresql_minimum_supported_version?
|
||||
version.to_f >= MINIMUM_POSTGRES_VERSION
|
||||
end
|
||||
|
@ -127,28 +123,6 @@ module Gitlab
|
|||
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
|
||||
end
|
||||
|
||||
# map some of the function names that changed between PostgreSQL 9 and 10
|
||||
# https://wiki.postgresql.org/wiki/New_in_postgres_10
|
||||
def self.pg_wal_lsn_diff
|
||||
Gitlab::Database.postgresql_9_or_less? ? 'pg_xlog_location_diff' : 'pg_wal_lsn_diff'
|
||||
end
|
||||
|
||||
def self.pg_current_wal_insert_lsn
|
||||
Gitlab::Database.postgresql_9_or_less? ? 'pg_current_xlog_insert_location' : 'pg_current_wal_insert_lsn'
|
||||
end
|
||||
|
||||
def self.pg_last_wal_receive_lsn
|
||||
Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_receive_location' : 'pg_last_wal_receive_lsn'
|
||||
end
|
||||
|
||||
def self.pg_last_wal_replay_lsn
|
||||
Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn'
|
||||
end
|
||||
|
||||
def self.pg_last_xact_replay_timestamp
|
||||
'pg_last_xact_replay_timestamp'
|
||||
end
|
||||
|
||||
def self.nulls_last_order(field, direction = 'ASC')
|
||||
Arel.sql("#{field} #{direction} NULLS LAST")
|
||||
end
|
||||
|
|
|
@ -2686,6 +2686,9 @@ msgstr ""
|
|||
msgid "An error has occurred"
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occured while making the changes: %{error}"
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred adding a draft to the thread."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2959,9 +2962,6 @@ msgstr ""
|
|||
msgid "An error occurred while saving assignees"
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while saving the template. Please check if the template exists."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while searching for milestones"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9527,6 +9527,9 @@ msgstr ""
|
|||
msgid "Emails sent from Service Desk will have this name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Emails sent to %{email} will still be supported"
|
||||
msgstr ""
|
||||
|
||||
msgid "Emails separated by comma"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -48,7 +48,10 @@ describe('AddIssuableForm', () => {
|
|||
const input = findFormInput(wrapper);
|
||||
if (input) input.blur();
|
||||
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('with data', () => {
|
||||
|
|
|
@ -1,241 +1,146 @@
|
|||
import Vue from 'vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { PathIdSeparator } from '~/related_issues/constants';
|
||||
import issueToken from '~/related_issues/components/issue_token.vue';
|
||||
import IssueToken from '~/related_issues/components/issue_token.vue';
|
||||
|
||||
describe('IssueToken', () => {
|
||||
const idKey = 200;
|
||||
const displayReference = 'foo/bar#123';
|
||||
const title = 'some title';
|
||||
const pathIdSeparator = PathIdSeparator.Issue;
|
||||
const eventNamespace = 'pendingIssuable';
|
||||
let IssueToken;
|
||||
let vm;
|
||||
const path = '/foo/bar/issues/123';
|
||||
const pathIdSeparator = PathIdSeparator.Issue;
|
||||
const title = 'some title';
|
||||
|
||||
beforeEach(() => {
|
||||
IssueToken = Vue.extend(issueToken);
|
||||
let wrapper;
|
||||
|
||||
const defaultProps = {
|
||||
idKey,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
};
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(IssueToken, {
|
||||
propsData: { ...defaultProps, ...props },
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
if (vm) {
|
||||
vm.$destroy();
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
const findLink = () => wrapper.find({ ref: 'link' });
|
||||
const findReference = () => wrapper.find({ ref: 'reference' });
|
||||
const findReferenceIcon = () => wrapper.find('[data-testid="referenceIcon"]');
|
||||
const findRemoveBtn = () => wrapper.find('[data-testid="removeBtn"]');
|
||||
const findTitle = () => wrapper.find({ ref: 'title' });
|
||||
|
||||
describe('with reference supplied', () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
},
|
||||
}).$mount();
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('shows reference', () => {
|
||||
expect(vm.$el.textContent.trim()).toEqual(displayReference);
|
||||
expect(wrapper.text()).toContain(displayReference);
|
||||
});
|
||||
|
||||
it('does not link without path specified', () => {
|
||||
expect(vm.$refs.link.tagName.toLowerCase()).toEqual('span');
|
||||
expect(vm.$refs.link.getAttribute('href')).toBeNull();
|
||||
expect(findLink().element.tagName).toBe('SPAN');
|
||||
expect(findLink().attributes('href')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with reference and title supplied', () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
title,
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('shows reference and title', () => {
|
||||
expect(vm.$refs.reference.textContent.trim()).toEqual(displayReference);
|
||||
expect(vm.$refs.title.textContent.trim()).toEqual(title);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with path supplied', () => {
|
||||
const path = '/foo/bar/issues/123';
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
createComponent({
|
||||
title,
|
||||
path,
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
expect(findReference().text()).toBe(displayReference);
|
||||
expect(findTitle().text()).toBe(title);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with path and title supplied', () => {
|
||||
it('links reference and title', () => {
|
||||
expect(vm.$refs.link.getAttribute('href')).toEqual(path);
|
||||
createComponent({
|
||||
path,
|
||||
title,
|
||||
});
|
||||
|
||||
expect(findLink().attributes('href')).toBe(path);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with state supplied', () => {
|
||||
describe("`state: 'opened'`", () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
state: 'opened',
|
||||
},
|
||||
}).$mount();
|
||||
it.each`
|
||||
state | icon | cssClass
|
||||
${'opened'} | ${'issue-open-m'} | ${'issue-token-state-icon-open'}
|
||||
${'reopened'} | ${'issue-open-m'} | ${'issue-token-state-icon-open'}
|
||||
${'closed'} | ${'issue-close'} | ${'issue-token-state-icon-closed'}
|
||||
`('shows "$icon" icon when "$state"', ({ state, icon, cssClass }) => {
|
||||
createComponent({
|
||||
path,
|
||||
state,
|
||||
});
|
||||
|
||||
it('shows green circle icon', () => {
|
||||
expect(vm.$el.querySelector('.issue-token-state-icon-open.fa.fa-circle-o')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("`state: 'reopened'`", () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
state: 'reopened',
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('shows green circle icon', () => {
|
||||
expect(vm.$el.querySelector('.issue-token-state-icon-open.fa.fa-circle-o')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("`state: 'closed'`", () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
state: 'closed',
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('shows red minus icon', () => {
|
||||
expect(vm.$el.querySelector('.issue-token-state-icon-closed.fa.fa-minus')).toBeDefined();
|
||||
});
|
||||
expect(findReferenceIcon().props('name')).toBe(icon);
|
||||
expect(findReferenceIcon().classes()).toContain(cssClass);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with reference, title, state', () => {
|
||||
const state = 'opened';
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
title,
|
||||
state,
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('shows reference, title, and state', () => {
|
||||
const stateIcon = vm.$refs.reference.querySelector('svg');
|
||||
createComponent({
|
||||
title,
|
||||
state,
|
||||
});
|
||||
|
||||
expect(stateIcon.getAttribute('aria-label')).toEqual(state);
|
||||
expect(vm.$refs.reference.textContent.trim()).toEqual(displayReference);
|
||||
expect(vm.$refs.title.textContent.trim()).toEqual(title);
|
||||
expect(findReferenceIcon().attributes('aria-label')).toBe(state);
|
||||
expect(findReference().text()).toBe(displayReference);
|
||||
expect(findTitle().text()).toBe(title);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with canRemove', () => {
|
||||
describe('`canRemove: false` (default)', () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('does not have remove button', () => {
|
||||
expect(vm.$el.querySelector('.issue-token-remove-button')).toBeNull();
|
||||
createComponent();
|
||||
|
||||
expect(findRemoveBtn().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('`canRemove: true`', () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
createComponent({
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
canRemove: true,
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
});
|
||||
|
||||
it('has remove button', () => {
|
||||
expect(vm.$el.querySelector('.issue-token-remove-button')).toBeDefined();
|
||||
});
|
||||
});
|
||||
expect(findRemoveBtn().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
},
|
||||
}).$mount();
|
||||
it('emits event when clicked', () => {
|
||||
findRemoveBtn().trigger('click');
|
||||
|
||||
const emitted = wrapper.emitted(`${eventNamespace}RemoveRequest`);
|
||||
|
||||
expect(emitted).toHaveLength(1);
|
||||
expect(emitted[0]).toEqual([idKey]);
|
||||
});
|
||||
|
||||
it('when getting checked', () => {
|
||||
jest.spyOn(vm, '$emit').mockImplementation(() => {});
|
||||
vm.onRemoveRequest();
|
||||
|
||||
expect(vm.$emit).toHaveBeenCalledWith('pendingIssuableRemoveRequest', vm.idKey);
|
||||
it('tooltip should not be escaped', () => {
|
||||
expect(findRemoveBtn().attributes('data-original-title')).toBe(
|
||||
`Remove ${displayReference}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tooltip', () => {
|
||||
beforeEach(() => {
|
||||
vm = new IssueToken({
|
||||
propsData: {
|
||||
idKey,
|
||||
eventNamespace,
|
||||
displayReference,
|
||||
pathIdSeparator,
|
||||
canRemove: true,
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('should not be escaped', () => {
|
||||
const { originalTitle } = vm.$refs.removeButton.dataset;
|
||||
|
||||
expect(originalTitle).toEqual(`Remove ${displayReference}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,7 +18,10 @@ describe('RelatedIssuesBlock', () => {
|
|||
const findIssueCountBadgeAddButton = () => wrapper.find(GlButton);
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('with defaults', () => {
|
||||
|
|
|
@ -14,7 +14,10 @@ describe('RelatedIssuesList', () => {
|
|||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('with defaults', () => {
|
||||
|
|
|
@ -218,9 +218,7 @@ describe('ServiceDeskRoot', () => {
|
|||
.$nextTick()
|
||||
.then(waitForPromises)
|
||||
.then(() => {
|
||||
expect(wrapper.html()).toContain(
|
||||
'An error occurred while saving the template. Please check if the template exists.',
|
||||
);
|
||||
expect(wrapper.html()).toContain('An error occured while making the changes:');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -70,25 +70,6 @@ RSpec.describe Gitlab::Database do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.postgresql_9_or_less?' do
|
||||
it 'returns true when using postgresql 8.4' do
|
||||
allow(described_class).to receive(:version).and_return('8.4')
|
||||
expect(described_class.postgresql_9_or_less?).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns true when using PostgreSQL 9.6' do
|
||||
allow(described_class).to receive(:version).and_return('9.6')
|
||||
|
||||
expect(described_class.postgresql_9_or_less?).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false when using PostgreSQL 10 or newer' do
|
||||
allow(described_class).to receive(:version).and_return('10')
|
||||
|
||||
expect(described_class.postgresql_9_or_less?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.postgresql_minimum_supported_version?' do
|
||||
it 'returns false when using PostgreSQL 10' do
|
||||
allow(described_class).to receive(:version).and_return('10')
|
||||
|
@ -150,68 +131,6 @@ RSpec.describe Gitlab::Database do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.pg_wal_lsn_diff' do
|
||||
it 'returns old name when using PostgreSQL 9.6' do
|
||||
allow(described_class).to receive(:version).and_return('9.6')
|
||||
|
||||
expect(described_class.pg_wal_lsn_diff).to eq('pg_xlog_location_diff')
|
||||
end
|
||||
|
||||
it 'returns new name when using PostgreSQL 10 or newer' do
|
||||
allow(described_class).to receive(:version).and_return('10')
|
||||
|
||||
expect(described_class.pg_wal_lsn_diff).to eq('pg_wal_lsn_diff')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.pg_current_wal_insert_lsn' do
|
||||
it 'returns old name when using PostgreSQL 9.6' do
|
||||
allow(described_class).to receive(:version).and_return('9.6')
|
||||
|
||||
expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_xlog_insert_location')
|
||||
end
|
||||
|
||||
it 'returns new name when using PostgreSQL 10 or newer' do
|
||||
allow(described_class).to receive(:version).and_return('10')
|
||||
|
||||
expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_wal_insert_lsn')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.pg_last_wal_receive_lsn' do
|
||||
it 'returns old name when using PostgreSQL 9.6' do
|
||||
allow(described_class).to receive(:version).and_return('9.6')
|
||||
|
||||
expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_xlog_receive_location')
|
||||
end
|
||||
|
||||
it 'returns new name when using PostgreSQL 10 or newer' do
|
||||
allow(described_class).to receive(:version).and_return('10')
|
||||
|
||||
expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_wal_receive_lsn')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.pg_last_wal_replay_lsn' do
|
||||
it 'returns old name when using PostgreSQL 9.6' do
|
||||
allow(described_class).to receive(:version).and_return('9.6')
|
||||
|
||||
expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_xlog_replay_location')
|
||||
end
|
||||
|
||||
it 'returns new name when using PostgreSQL 10 or newer' do
|
||||
allow(described_class).to receive(:version).and_return('10')
|
||||
|
||||
expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_wal_replay_lsn')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.pg_last_xact_replay_timestamp' do
|
||||
it 'returns pg_last_xact_replay_timestamp' do
|
||||
expect(described_class.pg_last_xact_replay_timestamp).to eq('pg_last_xact_replay_timestamp')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.nulls_last_order' do
|
||||
it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column ASC NULLS LAST'}
|
||||
it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'}
|
||||
|
|
Loading…
Reference in a new issue