summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-30 00:10:03 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-30 00:10:03 +0000
commit420215876fadcc44935e7937cb56f7ffe5212ecb (patch)
treea24028043ba417bbdde620521e1940eb4ce727e1
parente4df6a7c53ab6e9dd6d63516ca9e0ad143cfa17a (diff)
downloadgitlab-ce-420215876fadcc44935e7937cb56f7ffe5212ecb.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/environments/components/edit_environment.vue4
-rw-r--r--app/assets/javascripts/environments/components/environment_form.vue45
-rw-r--r--app/assets/javascripts/environments/components/new_environment.vue4
-rw-r--r--app/assets/javascripts/security_configuration/components/feature_card.vue7
-rw-r--r--app/views/projects/environments/_form.html.haml21
-rw-r--r--doc/development/iterating_tables_in_batches.md4
-rw-r--r--doc/development/permissions.md3
-rw-r--r--doc/development/reference_processing.md3
-rw-r--r--doc/operations/incident_management/status_page.md7
-rw-r--r--doc/user/application_security/cve_id_request.md4
-rw-r--r--doc/user/discussions/index.md2
-rw-r--r--doc/user/group/epics/manage_epics.md5
-rw-r--r--doc/user/group/roadmap/index.md2
-rw-r--r--doc/user/permissions.md8
-rw-r--r--doc/user/project/issues/confidential_issues.md7
-rw-r--r--doc/user/project/issues/managing_issues.md2
-rw-r--r--doc/user/project/merge_requests/confidential.md2
-rw-r--r--doc/user/project/merge_requests/getting_started.md2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/environments/edit_environment_spec.js20
-rw-r--r--spec/frontend/environments/environment_form_spec.js126
-rw-r--r--spec/frontend/environments/new_environment_spec.js20
22 files changed, 177 insertions, 124 deletions
diff --git a/app/assets/javascripts/environments/components/edit_environment.vue b/app/assets/javascripts/environments/components/edit_environment.vue
index 80355708311..1cd960d7cd6 100644
--- a/app/assets/javascripts/environments/components/edit_environment.vue
+++ b/app/assets/javascripts/environments/components/edit_environment.vue
@@ -21,6 +21,7 @@ export default {
name: this.environment.name,
externalUrl: this.environment.external_url,
},
+ loading: false,
};
},
methods: {
@@ -28,6 +29,7 @@ export default {
this.formEnvironment = environment;
},
onSubmit() {
+ this.loading = true;
axios
.put(this.updateEnvironmentPath, {
id: this.environment.id,
@@ -38,6 +40,7 @@ export default {
.catch((error) => {
const message = error.response.data.message[0];
createFlash({ message });
+ this.loading = false;
});
},
},
@@ -48,6 +51,7 @@ export default {
:cancel-path="projectEnvironmentsPath"
:environment="formEnvironment"
:title="__('Edit environment')"
+ :loading="loading"
@change="onChange"
@submit="onSubmit"
/>
diff --git a/app/assets/javascripts/environments/components/environment_form.vue b/app/assets/javascripts/environments/components/environment_form.vue
index 636e263b990..6db8fe24e72 100644
--- a/app/assets/javascripts/environments/components/environment_form.vue
+++ b/app/assets/javascripts/environments/components/environment_form.vue
@@ -26,6 +26,11 @@ export default {
required: true,
type: String,
},
+ loading: {
+ required: false,
+ type: Boolean,
+ default: false,
+ },
},
i18n: {
header: __('Environments'),
@@ -42,21 +47,26 @@ export default {
helpPagePath: helpPagePath('ci/environments/index.md'),
data() {
return {
- errors: {
+ visited: {
name: null,
url: null,
},
};
},
+ computed: {
+ valid() {
+ return {
+ name: this.visited.name && this.environment.name !== '',
+ url: this.visited.url && isAbsolute(this.environment.externalUrl),
+ };
+ },
+ },
methods: {
onChange(env) {
this.$emit('change', env);
},
- validateUrl() {
- this.errors.url = isAbsolute(this.environment.externalUrl);
- },
- validateName() {
- this.errors.name = this.environment.name !== '';
+ visit(field) {
+ this.visited[field] = true;
},
},
};
@@ -89,40 +99,45 @@ export default {
<gl-form-group
:label="$options.i18n.nameLabel"
label-for="environment_name"
- :state="errors.name"
+ :state="valid.name"
:invalid-feedback="$options.i18n.nameFeedback"
>
<gl-form-input
id="environment_name"
:value="environment.name"
- :state="errors.name"
+ :state="valid.name"
name="environment[name]"
required
@input="onChange({ ...environment, name: $event })"
- @blur="validateName"
+ @blur="visit('name')"
/>
</gl-form-group>
<gl-form-group
:label="$options.i18n.urlLabel"
- :state="errors.url"
+ :state="valid.url"
:invalid-feedback="$options.i18n.urlFeedback"
label-for="environment_external_url"
>
<gl-form-input
id="environment_external_url"
:value="environment.externalUrl"
- :state="errors.url"
+ :state="valid.url"
name="environment[external_url]"
type="url"
@input="onChange({ ...environment, externalUrl: $event })"
- @blur="validateUrl"
+ @blur="visit('url')"
/>
</gl-form-group>
<div class="form-actions">
- <gl-button type="submit" variant="confirm" name="commit" class="js-no-auto-disable">{{
- $options.i18n.save
- }}</gl-button>
+ <gl-button
+ :loading="loading"
+ type="submit"
+ variant="confirm"
+ name="commit"
+ class="js-no-auto-disable"
+ >{{ $options.i18n.save }}</gl-button
+ >
<gl-button :href="cancelPath">{{ $options.i18n.cancel }}</gl-button>
</div>
</gl-form>
diff --git a/app/assets/javascripts/environments/components/new_environment.vue b/app/assets/javascripts/environments/components/new_environment.vue
index 6899ad63921..14da2668417 100644
--- a/app/assets/javascripts/environments/components/new_environment.vue
+++ b/app/assets/javascripts/environments/components/new_environment.vue
@@ -15,6 +15,7 @@ export default {
name: '',
externalUrl: '',
},
+ loading: false,
};
},
methods: {
@@ -22,6 +23,7 @@ export default {
this.environment = env;
},
onSubmit() {
+ this.loading = true;
axios
.post(this.projectEnvironmentsPath, {
name: this.environment.name,
@@ -31,6 +33,7 @@ export default {
.catch((error) => {
const message = error.response.data.message[0];
createFlash({ message });
+ this.loading = false;
});
},
},
@@ -41,6 +44,7 @@ export default {
:cancel-path="projectEnvironmentsPath"
:environment="environment"
:title="__('New environment')"
+ :loading="loading"
@change="onChange($event)"
@submit="onSubmit"
/>
diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue
index 6ca96504982..0ecfdf420db 100644
--- a/app/assets/javascripts/security_configuration/components/feature_card.vue
+++ b/app/assets/javascripts/security_configuration/components/feature_card.vue
@@ -83,7 +83,11 @@ export default {
<div class="gl-display-flex gl-align-items-baseline">
<h3 class="gl-font-lg gl-m-0 gl-mr-3">{{ feature.name }}</h3>
- <div :class="statusClasses" data-testid="feature-status">
+ <div
+ :class="statusClasses"
+ data-testid="feature-status"
+ :data-qa-selector="`${feature.type}_status`"
+ >
<template v-if="hasStatus">
<template v-if="enabled">
<gl-icon name="check-circle-filled" />
@@ -112,6 +116,7 @@ export default {
:href="feature.configurationPath"
variant="confirm"
:category="configurationButton.category"
+ :data-qa-selector="`${feature.type}_enable_button`"
class="gl-mt-5"
>
{{ configurationButton.text }}
diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml
deleted file mode 100644
index a295c8f6fb0..00000000000
--- a/app/views/projects/environments/_form.html.haml
+++ /dev/null
@@ -1,21 +0,0 @@
-.row.gl-mt-3.gl-mb-3
- .col-lg-3
- %h4.gl-mt-0
- = _("Environments")
- %p
- - link_to_read_more = link_to(_("More information"), help_page_path("ci/environments/index.md"))
- = _("Environments allow you to track deployments of your application %{link_to_read_more}.").html_safe % { link_to_read_more: link_to_read_more }
-
- = form_for [@project, @environment], html: { class: 'col-lg-9' } do |f|
- = form_errors(@environment)
-
- .form-group
- = f.label :name, _('Name'), class: 'label-bold'
- = f.text_field :name, required: true, class: 'form-control'
- .form-group
- = f.label :external_url, _('External URL'), class: 'label-bold'
- = f.url_field :external_url, class: 'form-control'
-
- .form-actions
- = f.submit _('Save'), class: 'gl-button btn btn-confirm'
- = link_to _('Cancel'), project_environments_path(@project), class: 'gl-button btn btn-cancel'
diff --git a/doc/development/iterating_tables_in_batches.md b/doc/development/iterating_tables_in_batches.md
index 3f2b467fec9..fba6f2f616d 100644
--- a/doc/development/iterating_tables_in_batches.md
+++ b/doc/development/iterating_tables_in_batches.md
@@ -334,8 +334,8 @@ end
The iteration uses the `id` column of the `projects` table. The batching does not affect the
subquery. This means for each iteration, the subquery is executed by the database. This adds a
constant "load" on the query which often ends up in statement timeouts. We have an unknown number
-of confidential issues, the execution time and the accessed database rows depend on the data
-distribution in the `issues` table.
+of [confidential issues](../user/project/issues/confidential_issues.md), the execution time
+and the accessed database rows depend on the data distribution in the `issues` table.
NOTE:
Using subqueries works only when the subquery returns a small number of rows.
diff --git a/doc/development/permissions.md b/doc/development/permissions.md
index 177fedcf454..b7079e9fb8e 100644
--- a/doc/development/permissions.md
+++ b/doc/development/permissions.md
@@ -86,7 +86,8 @@ is calculated properly.
### Confidential issues
-Confidential issues can be accessed only by project members who are at least
+[Confidential issues](../user/project/issues/confidential_issues.md) can be accessed
+only by project members who are at least
reporters (they can't be accessed by guests). Additionally they can be accessed
by their authors and assignees.
diff --git a/doc/development/reference_processing.md b/doc/development/reference_processing.md
index 2fd0ce51b39..ad6552e88fe 100644
--- a/doc/development/reference_processing.md
+++ b/doc/development/reference_processing.md
@@ -152,7 +152,8 @@ a resource that some subsequent readers should not be able to see.
For example, you might create an issue, and refer to a confidential issue `#1234`,
which you have access to. This is rendered in the cached HTML as a link to
-that confidential issue, with data attributes containing its ID, the ID of the
+that [confidential issue](../user/project/issues/confidential_issues.md),
+with data attributes containing its ID, the ID of the
project and other confidential data. A later reader, who has access to your issue
might not have permission to read issue `#1234`, and so we need to redact
these sensitive pieces of data. This is what `ReferenceParser` classes do.
diff --git a/doc/operations/incident_management/status_page.md b/doc/operations/incident_management/status_page.md
index d63d42e07c1..d14e4120511 100644
--- a/doc/operations/incident_management/status_page.md
+++ b/doc/operations/incident_management/status_page.md
@@ -124,7 +124,7 @@ To publish an incident:
1. Create an issue in the project you enabled the GitLab Status Page settings in.
1. A [project or group owner](../../user/permissions.md) must use the
`/publish` [quick action](../../user/project/quick_actions.md) to publish the
- issue to the GitLab Status Page. Confidential issues can't be published.
+ issue to the GitLab Status Page. [Confidential issues](../../user/project/issues/confidential_issues.md) can't be published.
A background worker publishes the issue onto the Status Page using the credentials
you provided during setup. As part of publication, GitLab:
@@ -168,5 +168,6 @@ To change the incident status from `open` to `closed`, close the incident issue
within GitLab. Closing the issue triggers a background worker to update the
GitLab Status Page website.
-If you make a published issue confidential, GitLab unpublishes it from your
-GitLab Status Page website.
+If you
+[make a published issue confidential](../../user/project/issues/confidential_issues.md#making-an-issue-confidential),
+GitLab unpublishes it from your GitLab Status Page website.
diff --git a/doc/user/application_security/cve_id_request.md b/doc/user/application_security/cve_id_request.md
index aaf701c91dc..009f8d828f7 100644
--- a/doc/user/application_security/cve_id_request.md
+++ b/doc/user/application_security/cve_id_request.md
@@ -28,7 +28,7 @@ If the following conditions are met, a **Request CVE ID** button appears in your
- The project is hosted in GitLab.com.
- The project is public.
- You are a maintainer of the project.
-- The issue is confidential.
+- The issue is [confidential](../project/issues/confidential_issues.md).
## Submitting a CVE ID Request
@@ -37,7 +37,7 @@ the [GitLab CVE project](https://gitlab.com/gitlab-org/cves).
![CVE ID request button](img/cve_id_request_button.png)
-Creating the confidential issue starts the CVE request process.
+Creating the [confidential issue](../project/issues/confidential_issues.md) starts the CVE request process.
![New CVE ID request issue](img/new_cve_request_issue.png)
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 825f9be6ba6..a1d8863710c 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -138,6 +138,8 @@ who have at least the Reporter role.
![Confidential comments](img/confidential_comments_v13_9.png)
+You can also make an [entire issue confidential](../project/issues/confidential_issues.md).
+
## Show only comments
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/26723) in GitLab 11.5.
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index b063ba9a75c..1b36d6f03df 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -193,7 +193,10 @@ or newest items to be shown first.
If you're working on items that contain private information, you can make an epic confidential.
NOTE:
-A confidential epic can only contain confidential issues and confidential child epics.
+A confidential epic can only contain [confidential issues](../../project/issues/confidential_issues.md)
+and confidential child epics. However, merge requests are public, if created in a public project.
+Read [Merge requests for confidential issues](../../project/merge_requests/confidential.md)
+to learn how to create a confidential merge request.
To make an epic confidential:
diff --git a/doc/user/group/roadmap/index.md b/doc/user/group/roadmap/index.md
index 88d43715c58..811297c6eda 100644
--- a/doc/user/group/roadmap/index.md
+++ b/doc/user/group/roadmap/index.md
@@ -69,7 +69,7 @@ You can also filter epics in the Roadmap view by the epics':
- Author
- Label
- Milestone
-- Confidentiality
+- [Confidentiality](../epics/manage_epics.md#make-an-epic-confidential)
- Epic
- Your Reaction
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index d3b33f79f27..16509565525 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -46,7 +46,7 @@ The following table lists project permissions available for each role:
| Action | Guest | Reporter | Developer |Maintainer| Owner |
|---------------------------------------------------|---------|------------|-------------|----------|--------|
| Assign issues | ✓ (*16*)| ✓ | ✓ | ✓ | ✓ |
-| Create confidential issue | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create [confidential issue](project/issues/confidential_issues.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| Download and browse job artifacts | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
@@ -97,7 +97,7 @@ The following table lists project permissions available for each role:
| [Set issue estimate and record time spent](project/time_tracking.md) | | ✓ | ✓ | ✓ | ✓ |
| View CI/CD analytics | | ✓ | ✓ | ✓ | ✓ |
| View Code Review analytics **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
-| View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ |
+| View [confidential issues](project/issues/confidential_issues.md) | (*2*) | ✓ | ✓ | ✓ | ✓ |
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
| View License list **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
@@ -198,7 +198,7 @@ The following table lists project permissions available for each role:
| Remove protected branches (*4*) | | | | | |
1. Guest users are able to perform this action on public and internal projects, but not private projects. This doesn't apply to [external users](#external-users) where explicit access must be given even if the project is internal.
-1. Guest users can only view the confidential issues they created themselves.
+1. Guest users can only view the [confidential issues](project/issues/confidential_issues.md) they created themselves.
1. If **Public pipelines** is enabled in **Project Settings > CI/CD**.
1. Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [protected branches](project/protected_branches.md).
1. If the [branch is protected](project/protected_branches.md), this depends on the access Developers and Maintainers are given.
@@ -256,7 +256,7 @@ Read through the documentation on [permissions for File Locking](project/file_lo
### Confidential Issues permissions
-Confidential issues can be accessed by users with reporter and higher permission levels,
+[Confidential issues](project/issues/confidential_issues.md) can be accessed by users with reporter and higher permission levels,
as well as by guest users that create a confidential issue. To learn more,
read through the documentation on [permissions and access to confidential issues](project/issues/confidential_issues.md#permissions-and-access-to-confidential-issues).
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index e6705933ae9..136e8ee2ebb 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -45,8 +45,8 @@ system note in the issue's comments.
## Indications of a confidential issue
There are a few things that visually separate a confidential issue from a
-regular one. In the issues index page view, you can see the eye-slash icon
-next to the issues that are marked as confidential.
+regular one. In the issues index page view, you can see the eye-slash (**(eye-slash)**) icon
+next to the issues that are marked as confidential:
![Confidential issues index page](img/confidential_issues_index_page.png)
@@ -91,3 +91,6 @@ sees in the project's search results respectively.
## Related links
- [Merge requests for confidential issues](../merge_requests/confidential.md)
+- [Make an epic confidential](../../group/epics/manage_epics.md#make-an-epic-confidential)
+- [Mark a comment as confidential](../../discussions/index.md#mark-a-comment-as-confidential)
+- [Security practices for confidential merge requests](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#security-releases-critical-non-critical-as-a-developer) at GitLab
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index c570bc9612a..a2185c83f4d 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -59,7 +59,7 @@ When you're creating a new issue, these are the fields you can fill in:
- Title
- Description
-- Checkbox to make the issue confidential
+- Checkbox to make the issue [confidential](confidential_issues.md)
- Assignee
- Weight
- [Epic](../../group/epics/index.md)
diff --git a/doc/user/project/merge_requests/confidential.md b/doc/user/project/merge_requests/confidential.md
index b50c5ee0ea9..6df84dd1dd1 100644
--- a/doc/user/project/merge_requests/confidential.md
+++ b/doc/user/project/merge_requests/confidential.md
@@ -70,4 +70,6 @@ to the public upstream project.
## Related links
- [Confidential issues](../issues/confidential_issues.md)
+- [Make an epic confidential](../../group/epics/manage_epics.md#make-an-epic-confidential)
+- [Mark a comment as confidential](../../discussions/index.md#mark-a-comment-as-confidential)
- [Security practices for confidential merge requests](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#security-releases-critical-non-critical-as-a-developer) at GitLab
diff --git a/doc/user/project/merge_requests/getting_started.md b/doc/user/project/merge_requests/getting_started.md
index ce39f39f0a1..1c8ee587838 100644
--- a/doc/user/project/merge_requests/getting_started.md
+++ b/doc/user/project/merge_requests/getting_started.md
@@ -140,7 +140,7 @@ when merged.
If the issue is [confidential](../issues/confidential_issues.md),
you may want to use a different workflow for
-[merge requests for confidential issues](../issues/confidential_issues.md#merge-requests-for-confidential-issues)
+[merge requests for confidential issues](confidential.md)
to prevent confidential information from being exposed.
### Deleting the source branch
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2a4f61d307f..6a0796bb5f0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12470,9 +12470,6 @@ msgstr ""
msgid "Environments Dashboard"
msgstr ""
-msgid "Environments allow you to track deployments of your application %{link_to_read_more}."
-msgstr ""
-
msgid "Environments allow you to track deployments of your application. %{linkStart}More information%{linkEnd}."
msgstr ""
diff --git a/spec/frontend/environments/edit_environment_spec.js b/spec/frontend/environments/edit_environment_spec.js
index de497b18dd6..3e7f5dd5ff4 100644
--- a/spec/frontend/environments/edit_environment_spec.js
+++ b/spec/frontend/environments/edit_environment_spec.js
@@ -1,3 +1,4 @@
+import { GlLoadingIcon } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -43,7 +44,9 @@ describe('~/environments/components/edit.vue', () => {
wrapper.destroy();
});
- const fillForm = async (expected, response) => {
+ const showsLoading = () => wrapper.find(GlLoadingIcon).exists();
+
+ const submitForm = async (expected, response) => {
mock
.onPut(DEFAULT_OPTS.provide.updateEnvironmentPath, {
name: expected.name,
@@ -72,10 +75,20 @@ describe('~/environments/components/edit.vue', () => {
expect(input().element.value).toBe(value);
});
+ it('shows loader after form is submitted', async () => {
+ const expected = { name: 'test', url: 'https://google.ca' };
+
+ expect(showsLoading()).toBe(false);
+
+ await submitForm(expected, [200, { path: '/test' }]);
+
+ expect(showsLoading()).toBe(true);
+ });
+
it('submits the updated environment on submit', async () => {
const expected = { name: 'test', url: 'https://google.ca' };
- await fillForm(expected, [200, { path: '/test' }]);
+ await submitForm(expected, [200, { path: '/test' }]);
expect(visitUrl).toHaveBeenCalledWith('/test');
});
@@ -83,8 +96,9 @@ describe('~/environments/components/edit.vue', () => {
it('shows errors on error', async () => {
const expected = { name: 'test', url: 'https://google.ca' };
- await fillForm(expected, [400, { message: ['name taken'] }]);
+ await submitForm(expected, [400, { message: ['name taken'] }]);
expect(createFlash).toHaveBeenCalledWith({ message: 'name taken' });
+ expect(showsLoading()).toBe(false);
});
});
diff --git a/spec/frontend/environments/environment_form_spec.js b/spec/frontend/environments/environment_form_spec.js
index 2d7cdc8a0bd..ed8fda71dab 100644
--- a/spec/frontend/environments/environment_form_spec.js
+++ b/spec/frontend/environments/environment_form_spec.js
@@ -1,97 +1,105 @@
+import { GlLoadingIcon } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import EnvironmentForm from '~/environments/components/environment_form.vue';
jest.mock('~/lib/utils/csrf');
-const DEFAULT_OPTS = {
- propsData: {
- environment: { name: '', externalUrl: '' },
- title: 'environment',
- cancelPath: '/cancel',
- },
+const DEFAULT_PROPS = {
+ environment: { name: '', externalUrl: '' },
+ title: 'environment',
+ cancelPath: '/cancel',
};
describe('~/environments/components/form.vue', () => {
let wrapper;
- const createWrapper = (opts = {}) =>
+ const createWrapper = (propsData = {}) =>
mountExtended(EnvironmentForm, {
- ...DEFAULT_OPTS,
- ...opts,
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...propsData,
+ },
});
- beforeEach(() => {
- wrapper = createWrapper();
- });
-
afterEach(() => {
wrapper.destroy();
});
- it('links to documentation regarding environments', () => {
- const link = wrapper.findByRole('link', { name: 'More information' });
- expect(link.attributes('href')).toBe('/help/ci/environments/index.md');
- });
-
- it('links the cancel button to the cancel path', () => {
- const cancel = wrapper.findByRole('link', { name: 'Cancel' });
+ describe('default', () => {
+ beforeEach(() => {
+ wrapper = createWrapper();
+ });
- expect(cancel.attributes('href')).toBe(DEFAULT_OPTS.propsData.cancelPath);
- });
+ it('links to documentation regarding environments', () => {
+ const link = wrapper.findByRole('link', { name: 'More information' });
+ expect(link.attributes('href')).toBe('/help/ci/environments/index.md');
+ });
- describe('name input', () => {
- let name;
+ it('links the cancel button to the cancel path', () => {
+ const cancel = wrapper.findByRole('link', { name: 'Cancel' });
- beforeEach(() => {
- name = wrapper.findByLabelText('Name');
+ expect(cancel.attributes('href')).toBe(DEFAULT_PROPS.cancelPath);
});
- it('should emit changes to the name', async () => {
- await name.setValue('test');
- await name.trigger('blur');
+ describe('name input', () => {
+ let name;
- expect(wrapper.emitted('change')).toEqual([[{ name: 'test', externalUrl: '' }]]);
- });
+ beforeEach(() => {
+ name = wrapper.findByLabelText('Name');
+ });
- it('should validate that the name is required', async () => {
- await name.setValue('');
- await name.trigger('blur');
+ it('should emit changes to the name', async () => {
+ await name.setValue('test');
+ await name.trigger('blur');
- expect(wrapper.findByText('This field is required').exists()).toBe(true);
- expect(name.attributes('aria-invalid')).toBe('true');
- });
- });
+ expect(wrapper.emitted('change')).toEqual([[{ name: 'test', externalUrl: '' }]]);
+ });
- describe('url input', () => {
- let url;
+ it('should validate that the name is required', async () => {
+ await name.setValue('');
+ await name.trigger('blur');
- beforeEach(() => {
- url = wrapper.findByLabelText('External URL');
+ expect(wrapper.findByText('This field is required').exists()).toBe(true);
+ expect(name.attributes('aria-invalid')).toBe('true');
+ });
});
- it('should emit changes to the url', async () => {
- await url.setValue('https://example.com');
- await url.trigger('blur');
+ describe('url input', () => {
+ let url;
+
+ beforeEach(() => {
+ url = wrapper.findByLabelText('External URL');
+ });
- expect(wrapper.emitted('change')).toEqual([
- [{ name: '', externalUrl: 'https://example.com' }],
- ]);
+ it('should emit changes to the url', async () => {
+ await url.setValue('https://example.com');
+ await url.trigger('blur');
+
+ expect(wrapper.emitted('change')).toEqual([
+ [{ name: '', externalUrl: 'https://example.com' }],
+ ]);
+ });
+
+ it('should validate that the url is required', async () => {
+ await url.setValue('example.com');
+ await url.trigger('blur');
+
+ expect(wrapper.findByText('The URL should start with http:// or https://').exists()).toBe(
+ true,
+ );
+ expect(url.attributes('aria-invalid')).toBe('true');
+ });
});
- it('should validate that the url is required', async () => {
- await url.setValue('example.com');
- await url.trigger('blur');
+ it('submits when the form does', async () => {
+ await wrapper.findByRole('form', { title: 'environment' }).trigger('submit');
- expect(wrapper.findByText('The URL should start with http:// or https://').exists()).toBe(
- true,
- );
- expect(url.attributes('aria-invalid')).toBe('true');
+ expect(wrapper.emitted('submit')).toEqual([[]]);
});
});
- it('submits when the form does', async () => {
- await wrapper.findByRole('form', { title: 'environment' }).trigger('submit');
-
- expect(wrapper.emitted('submit')).toEqual([[]]);
+ it('shows a loading icon while loading', () => {
+ wrapper = createWrapper({ loading: true });
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
diff --git a/spec/frontend/environments/new_environment_spec.js b/spec/frontend/environments/new_environment_spec.js
index b92c4a688b7..f6d970e02d8 100644
--- a/spec/frontend/environments/new_environment_spec.js
+++ b/spec/frontend/environments/new_environment_spec.js
@@ -1,3 +1,4 @@
+import { GlLoadingIcon } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -39,7 +40,9 @@ describe('~/environments/components/new.vue', () => {
wrapper.destroy();
});
- const fillForm = async (expected, response) => {
+ const showsLoading = () => wrapper.find(GlLoadingIcon).exists();
+
+ const submitForm = async (expected, response) => {
mock
.onPost(DEFAULT_OPTS.provide.projectEnvironmentsPath, {
name: expected.name,
@@ -68,10 +71,20 @@ describe('~/environments/components/new.vue', () => {
expect(input().element.value).toBe(value);
});
+ it('shows loader after form is submitted', async () => {
+ const expected = { name: 'test', url: 'https://google.ca' };
+
+ expect(showsLoading()).toBe(false);
+
+ await submitForm(expected, [200, { path: '/test' }]);
+
+ expect(showsLoading()).toBe(true);
+ });
+
it('submits the new environment on submit', async () => {
const expected = { name: 'test', url: 'https://google.ca' };
- await fillForm(expected, [200, { path: '/test' }]);
+ await submitForm(expected, [200, { path: '/test' }]);
expect(visitUrl).toHaveBeenCalledWith('/test');
});
@@ -79,8 +92,9 @@ describe('~/environments/components/new.vue', () => {
it('shows errors on error', async () => {
const expected = { name: 'test', url: 'https://google.ca' };
- await fillForm(expected, [400, { message: ['name taken'] }]);
+ await submitForm(expected, [400, { message: ['name taken'] }]);
expect(createFlash).toHaveBeenCalledWith({ message: 'name taken' });
+ expect(showsLoading()).toBe(false);
});
});