Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-02 06:08:27 +00:00
parent 895563036a
commit 91ef4dcc05
50 changed files with 268 additions and 525 deletions

View file

@ -1,6 +1,6 @@
<script> <script>
import { GlAlert } from '@gitlab/ui'; import { GlAlert } from '@gitlab/ui';
import { __ } from '~/locale'; import { __, sprintf } from '~/locale';
import ServiceDeskSetting from './service_desk_setting.vue'; import ServiceDeskSetting from './service_desk_setting.vue';
import ServiceDeskService from '../services/service_desk_service'; import ServiceDeskService from '../services/service_desk_service';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
@ -122,11 +122,13 @@ export default {
this.incomingEmail = data?.service_desk_address; this.incomingEmail = data?.service_desk_address;
this.showAlert(__('Changes were successfully made.'), 'success'); this.showAlert(__('Changes were successfully made.'), 'success');
}) })
.catch(() => .catch(err => {
this.showAlert( 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(() => { .finally(() => {
this.isTemplateSaving = false; this.isTemplateSaving = false;
}); });

View file

@ -1,5 +1,5 @@
<script> <script>
import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@ -17,6 +17,7 @@ export default {
GlFormSelect, GlFormSelect,
GlToggle, GlToggle,
GlLoadingIcon, GlLoadingIcon,
GlSprintf,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
props: { props: {
@ -60,6 +61,7 @@ export default {
selectedTemplate: this.initialSelectedTemplate, selectedTemplate: this.initialSelectedTemplate,
outgoingName: this.initialOutgoingName || __('GitLab Support Bot'), outgoingName: this.initialOutgoingName || __('GitLab Support Bot'),
projectKey: this.initialProjectKey, projectKey: this.initialProjectKey,
baseEmail: this.incomingEmail.replace(this.initialProjectKey, ''),
}; };
}, },
computed: { computed: {
@ -123,12 +125,33 @@ export default {
/> />
</div> </div>
</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>
<template v-else> <template v-else>
<gl-loading-icon :inline="true" /> <gl-loading-icon :inline="true" />
<span class="sr-only">{{ __('Fetching incoming email') }}</span> <span class="sr-only">{{ __('Fetching incoming email') }}</span>
</template> </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"> <label for="service-desk-template-select" class="mt-3">
{{ __('Template to append to all Service Desk issues') }} {{ __('Template to append to all Service Desk issues') }}
</label> </label>
@ -144,19 +167,7 @@ export default {
<span class="form-text text-muted"> <span class="form-text text-muted">
{{ __('Emails sent from Service Desk will have this name') }} {{ __('Emails sent from Service Desk will have this name') }}
</span> </span>
<template v-if="hasProjectKeySupport"> <div class="gl-display-flex gl-justify-content-end">
<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>
<gl-button <gl-button
variant="success" variant="success"
class="gl-mt-5" class="gl-mt-5"
@ -168,4 +179,5 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>

View file

@ -90,6 +90,7 @@ export default {
:size="12" :size="12"
:title="stateTitle" :title="stateTitle"
:aria-label="state" :aria-label="state"
data-testid="referenceIcon"
/> />
{{ displayReference }} {{ displayReference }}
</component> </component>
@ -105,6 +106,7 @@ export default {
:title="removeButtonLabel" :title="removeButtonLabel"
:aria-label="removeButtonLabel" :aria-label="removeButtonLabel"
:disabled="removeDisabled" :disabled="removeDisabled"
data-testid="removeBtn"
type="button" type="button"
class="js-issue-token-remove-button" class="js-issue-token-remove-button"
@click="onRemoveRequest" @click="onRemoveRequest"

View file

@ -111,7 +111,7 @@ const mixins = {
return this.isMergeRequest && this.pipelineStatus && Object.keys(this.pipelineStatus).length; return this.isMergeRequest && this.pipelineStatus && Object.keys(this.pipelineStatus).length;
}, },
isOpen() { isOpen() {
return this.state === 'opened'; return this.state === 'opened' || this.state === 'reopened';
}, },
isClosed() { isClosed() {
return this.state === 'closed'; return this.state === 'closed';

View file

@ -22,8 +22,8 @@ module Postgresql
def self.lag_too_great?(max = 100.megabytes) def self.lag_too_great?(max = 100.megabytes)
return false unless in_use? return false unless in_use?
lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \ lag_function = "pg_wal_lsn_diff" \
"(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint" "(pg_current_wal_insert_lsn(), restart_lsn)::bigint"
# We force the use of a transaction here so the query always goes to the # We force the use of a transaction here so the query always goes to the
# primary, even when using the EE DB load balancer. # primary, even when using the EE DB load balancer.

View file

@ -1,4 +1,5 @@
- add_page_startup_api_call discussions_path(@issue) - add_page_startup_api_call discussions_path(@issue)
- add_page_startup_api_call notes_url
- @gfm_form = true - @gfm_form = true

View file

@ -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).

View file

@ -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).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/replication/configuration.md'
---
This document was moved to [another location](../administration/geo/replication/configuration.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/replication/configuration.md'
---
This document was moved to [another location](../administration/geo/replication/configuration.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/setup/database.md'
---
This document was moved to [another location](../administration/geo/setup/database.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/setup/database.md'
---
This document was moved to [another location](../administration/geo/setup/database.md).

View file

@ -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).

View file

@ -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).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/replication/faq.md'
---
This document was moved to [another location](../administration/geo/replication/faq.md).

View file

@ -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).

View file

@ -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).

View file

@ -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).

View file

@ -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).

View file

@ -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).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/replication/troubleshooting.md'
---
This document was moved to [another location](../administration/geo/replication/troubleshooting.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../administration/geo/replication/tuning.md'
---
This document was moved to [another location](../administration/geo/replication/tuning.md).

View file

@ -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).

View file

@ -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).

View file

@ -48,7 +48,7 @@ To enable the CAS OmniAuth provider you must register your application with your
url: 'CAS_SERVER', url: 'CAS_SERVER',
login_url: '/CAS_PATH/login', login_url: '/CAS_PATH/login',
service_validate_url: '/CAS_PATH/p3/serviceValidate', 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`). 1. Change 'CAS_PATH' to the root of your CAS instance (ie. `cas`).

View file

@ -78,7 +78,8 @@ Follow these steps to incorporate the GitHub OAuth 2 app in your GitLab server:
For GitHub Enterprise: For GitHub Enterprise:
```yaml ```yaml
- { name: 'github', app_id: 'YOUR_APP_ID', - { name: 'github',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET', app_secret: 'YOUR_APP_SECRET',
url: "https://github.example.com/", url: "https://github.example.com/",
args: { scope: 'user:email' } } args: { scope: 'user:email' } }
@ -125,7 +126,8 @@ omnibus_gitconfig['system'] = { "http" => ["sslVerify = false"] }
For installation from source: For installation from source:
```yaml ```yaml
- { name: 'github', app_id: 'YOUR_APP_ID', - { name: 'github',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET', app_secret: 'YOUR_APP_SECRET',
url: "https://github.example.com/", url: "https://github.example.com/",
verify_ssl: false, verify_ssl: false,

View file

@ -63,7 +63,8 @@ GitLab.com will generate an application ID and secret key for you to use.
For installations from source: For installations from source:
```yaml ```yaml
- { name: 'gitlab', app_id: 'YOUR_APP_ID', - { name: 'gitlab',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET', app_secret: 'YOUR_APP_SECRET',
args: { scope: 'api' } } args: { scope: 'api' } }
``` ```

View file

@ -84,7 +84,8 @@ On your GitLab server:
For installations from source: For installations from source:
```yaml ```yaml
- { name: 'google_oauth2', app_id: 'YOUR_APP_ID', - { name: 'google_oauth2',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET', app_secret: 'YOUR_APP_SECRET',
args: { access_type: 'offline', approval_prompt: '' } } args: { access_type: 'offline', approval_prompt: '' } }
``` ```

View file

@ -207,6 +207,7 @@ remove the OmniAuth provider named `kerberos` from your `gitlab.yml` /
```yaml ```yaml
omniauth: omniauth:
# Rest of configuration omitted
# ... # ...
providers: providers:
- { name: 'kerberos' } # <-- remove this line - { name: 'kerberos' } # <-- remove this line

View file

@ -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 ## 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. 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: For example, the following setting is used to enable the auto link feature for both a SAML provider and the Twitter OAuth provider:

View file

@ -65,7 +65,8 @@ To enable the Twitter OmniAuth provider you must register your application with
For installations from source: For installations from source:
```yaml ```yaml
- { name: 'twitter', app_id: 'YOUR_APP_ID', - { name: 'twitter',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET' } app_secret: 'YOUR_APP_SECRET' }
``` ```

View file

@ -70,7 +70,8 @@ receivers:
bearer_token: 9e1cbfcd546896a9ea8be557caf13a76 bearer_token: 9e1cbfcd546896a9ea8be557caf13a76
send_resolved: true send_resolved: true
url: http://192.168.178.31:3001/root/manual_prometheus/prometheus/alerts/notify.json 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), For GitLab to associate your alerts with an [environment](../../ci/environments/index.md),

View file

@ -125,7 +125,7 @@ the Agent in subsequent steps. You can create an Agent record either:
- Through GraphQL: **(PREMIUM ONLY)** - Through GraphQL: **(PREMIUM ONLY)**
```json ```graphql
mutation createAgent { mutation createAgent {
createClusterAgent(input: { projectPath: "path-to/your-awesome-project", name: "<agent-name>" }) { createClusterAgent(input: { projectPath: "path-to/your-awesome-project", name: "<agent-name>" }) {
clusterAgent { clusterAgent {

View file

@ -269,7 +269,7 @@ To add a Kubernetes cluster to your project, group, or instance:
Copy the `<authentication_token>` value from the output: Copy the `<authentication_token>` value from the output:
```yaml ```plaintext
Name: gitlab-token-b5zv4 Name: gitlab-token-b5zv4
Namespace: kube-system Namespace: kube-system
Labels: <none> Labels: <none>

View file

@ -222,7 +222,8 @@ the environment of the deployed function:
```yaml ```yaml
provider: provider:
... # Other configuration omitted
# ...
environment: environment:
A_VARIABLE: ${env:A_VARIABLE} A_VARIABLE: ${env:A_VARIABLE}
``` ```

View file

@ -92,10 +92,6 @@ module Gitlab
@version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] @version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
end end
def self.postgresql_9_or_less?
version.to_f < 10
end
def self.postgresql_minimum_supported_version? def self.postgresql_minimum_supported_version?
version.to_f >= MINIMUM_POSTGRES_VERSION version.to_f >= MINIMUM_POSTGRES_VERSION
end end
@ -127,28 +123,6 @@ module Gitlab
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing # ignore - happens when Rake tasks yet have to create a database, e.g. for testing
end 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') def self.nulls_last_order(field, direction = 'ASC')
Arel.sql("#{field} #{direction} NULLS LAST") Arel.sql("#{field} #{direction} NULLS LAST")
end end

View file

@ -2686,6 +2686,9 @@ msgstr ""
msgid "An error has occurred" msgid "An error has occurred"
msgstr "" msgstr ""
msgid "An error occured while making the changes: %{error}"
msgstr ""
msgid "An error occurred adding a draft to the thread." msgid "An error occurred adding a draft to the thread."
msgstr "" msgstr ""
@ -2959,9 +2962,6 @@ msgstr ""
msgid "An error occurred while saving assignees" msgid "An error occurred while saving assignees"
msgstr "" msgstr ""
msgid "An error occurred while saving the template. Please check if the template exists."
msgstr ""
msgid "An error occurred while searching for milestones" msgid "An error occurred while searching for milestones"
msgstr "" msgstr ""
@ -9527,6 +9527,9 @@ msgstr ""
msgid "Emails sent from Service Desk will have this name" msgid "Emails sent from Service Desk will have this name"
msgstr "" msgstr ""
msgid "Emails sent to %{email} will still be supported"
msgstr ""
msgid "Emails separated by comma" msgid "Emails separated by comma"
msgstr "" msgstr ""

View file

@ -48,7 +48,10 @@ describe('AddIssuableForm', () => {
const input = findFormInput(wrapper); const input = findFormInput(wrapper);
if (input) input.blur(); if (input) input.blur();
if (wrapper) {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
}
}); });
describe('with data', () => { describe('with data', () => {

View file

@ -1,241 +1,146 @@
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import { PathIdSeparator } from '~/related_issues/constants'; 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', () => { describe('IssueToken', () => {
const idKey = 200; const idKey = 200;
const displayReference = 'foo/bar#123'; const displayReference = 'foo/bar#123';
const title = 'some title';
const pathIdSeparator = PathIdSeparator.Issue;
const eventNamespace = 'pendingIssuable'; const eventNamespace = 'pendingIssuable';
let IssueToken; const path = '/foo/bar/issues/123';
let vm; const pathIdSeparator = PathIdSeparator.Issue;
const title = 'some title';
beforeEach(() => { let wrapper;
IssueToken = Vue.extend(issueToken);
const defaultProps = {
idKey,
displayReference,
pathIdSeparator,
};
const createComponent = (props = {}) => {
wrapper = shallowMount(IssueToken, {
propsData: { ...defaultProps, ...props },
}); });
};
afterEach(() => { afterEach(() => {
if (vm) { if (wrapper) {
vm.$destroy(); 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', () => { describe('with reference supplied', () => {
beforeEach(() => { beforeEach(() => {
vm = new IssueToken({ createComponent();
propsData: {
idKey,
eventNamespace,
displayReference,
pathIdSeparator,
},
}).$mount();
}); });
it('shows reference', () => { it('shows reference', () => {
expect(vm.$el.textContent.trim()).toEqual(displayReference); expect(wrapper.text()).toContain(displayReference);
}); });
it('does not link without path specified', () => { it('does not link without path specified', () => {
expect(vm.$refs.link.tagName.toLowerCase()).toEqual('span'); expect(findLink().element.tagName).toBe('SPAN');
expect(vm.$refs.link.getAttribute('href')).toBeNull(); expect(findLink().attributes('href')).toBeUndefined();
}); });
}); });
describe('with reference and title supplied', () => { describe('with reference and title supplied', () => {
beforeEach(() => {
vm = new IssueToken({
propsData: {
idKey,
eventNamespace,
displayReference,
pathIdSeparator,
title,
},
}).$mount();
});
it('shows reference and title', () => { it('shows reference and title', () => {
expect(vm.$refs.reference.textContent.trim()).toEqual(displayReference); createComponent({
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,
title, title,
path,
},
}).$mount();
}); });
expect(findReference().text()).toBe(displayReference);
expect(findTitle().text()).toBe(title);
});
});
describe('with path and title supplied', () => {
it('links reference and title', () => { 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('with state supplied', () => {
describe("`state: 'opened'`", () => { it.each`
beforeEach(() => { state | icon | cssClass
vm = new IssueToken({ ${'opened'} | ${'issue-open-m'} | ${'issue-token-state-icon-open'}
propsData: { ${'reopened'} | ${'issue-open-m'} | ${'issue-token-state-icon-open'}
idKey, ${'closed'} | ${'issue-close'} | ${'issue-token-state-icon-closed'}
eventNamespace, `('shows "$icon" icon when "$state"', ({ state, icon, cssClass }) => {
displayReference, createComponent({
pathIdSeparator, path,
state: 'opened', state,
},
}).$mount();
}); });
it('shows green circle icon', () => { expect(findReferenceIcon().props('name')).toBe(icon);
expect(vm.$el.querySelector('.issue-token-state-icon-open.fa.fa-circle-o')).toBeDefined(); expect(findReferenceIcon().classes()).toContain(cssClass);
});
});
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();
});
}); });
}); });
describe('with reference, title, state', () => { describe('with reference, title, state', () => {
const state = 'opened'; const state = 'opened';
beforeEach(() => {
vm = new IssueToken({
propsData: {
idKey,
eventNamespace,
displayReference,
pathIdSeparator,
title,
state,
},
}).$mount();
});
it('shows reference, title, and state', () => { it('shows reference, title, and state', () => {
const stateIcon = vm.$refs.reference.querySelector('svg'); createComponent({
title,
state,
});
expect(stateIcon.getAttribute('aria-label')).toEqual(state); expect(findReferenceIcon().attributes('aria-label')).toBe(state);
expect(vm.$refs.reference.textContent.trim()).toEqual(displayReference); expect(findReference().text()).toBe(displayReference);
expect(vm.$refs.title.textContent.trim()).toEqual(title); expect(findTitle().text()).toBe(title);
}); });
}); });
describe('with canRemove', () => { describe('with canRemove', () => {
describe('`canRemove: false` (default)', () => { describe('`canRemove: false` (default)', () => {
beforeEach(() => {
vm = new IssueToken({
propsData: {
idKey,
eventNamespace,
displayReference,
pathIdSeparator,
},
}).$mount();
});
it('does not have remove button', () => { it('does not have remove button', () => {
expect(vm.$el.querySelector('.issue-token-remove-button')).toBeNull(); createComponent();
expect(findRemoveBtn().exists()).toBe(false);
}); });
}); });
describe('`canRemove: true`', () => { describe('`canRemove: true`', () => {
beforeEach(() => { beforeEach(() => {
vm = new IssueToken({ createComponent({
propsData: {
idKey,
eventNamespace, eventNamespace,
displayReference,
pathIdSeparator,
canRemove: true, canRemove: true,
}, });
}).$mount();
}); });
it('has remove button', () => { it('has remove button', () => {
expect(vm.$el.querySelector('.issue-token-remove-button')).toBeDefined(); expect(findRemoveBtn().exists()).toBe(true);
});
});
}); });
describe('methods', () => { it('emits event when clicked', () => {
beforeEach(() => { findRemoveBtn().trigger('click');
vm = new IssueToken({
propsData: { const emitted = wrapper.emitted(`${eventNamespace}RemoveRequest`);
idKey,
eventNamespace, expect(emitted).toHaveLength(1);
displayReference, expect(emitted[0]).toEqual([idKey]);
pathIdSeparator,
},
}).$mount();
}); });
it('when getting checked', () => { it('tooltip should not be escaped', () => {
jest.spyOn(vm, '$emit').mockImplementation(() => {}); expect(findRemoveBtn().attributes('data-original-title')).toBe(
vm.onRemoveRequest(); `Remove ${displayReference}`,
);
expect(vm.$emit).toHaveBeenCalledWith('pendingIssuableRemoveRequest', vm.idKey);
}); });
}); });
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}`);
});
}); });
}); });

View file

@ -18,7 +18,10 @@ describe('RelatedIssuesBlock', () => {
const findIssueCountBadgeAddButton = () => wrapper.find(GlButton); const findIssueCountBadgeAddButton = () => wrapper.find(GlButton);
afterEach(() => { afterEach(() => {
if (wrapper) {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
}
}); });
describe('with defaults', () => { describe('with defaults', () => {

View file

@ -14,7 +14,10 @@ describe('RelatedIssuesList', () => {
let wrapper; let wrapper;
afterEach(() => { afterEach(() => {
if (wrapper) {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
}
}); });
describe('with defaults', () => { describe('with defaults', () => {

View file

@ -218,9 +218,7 @@ describe('ServiceDeskRoot', () => {
.$nextTick() .$nextTick()
.then(waitForPromises) .then(waitForPromises)
.then(() => { .then(() => {
expect(wrapper.html()).toContain( expect(wrapper.html()).toContain('An error occured while making the changes:');
'An error occurred while saving the template. Please check if the template exists.',
);
}); });
}); });
}); });

View file

@ -70,25 +70,6 @@ RSpec.describe Gitlab::Database do
end end
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 describe '.postgresql_minimum_supported_version?' do
it 'returns false when using PostgreSQL 10' do it 'returns false when using PostgreSQL 10' do
allow(described_class).to receive(:version).and_return('10') allow(described_class).to receive(:version).and_return('10')
@ -150,68 +131,6 @@ RSpec.describe Gitlab::Database do
end end
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 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', 'ASC')).to eq 'column ASC NULLS LAST'}
it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'} it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'}