diff --git a/ee/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/ee/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 533e0e12b2c4653fa9c04970d15c6c884643306a..d318660cf33f528c077256dcd29d7b32bde81a2a 100644 --- a/ee/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/ee/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -1,10 +1,6 @@ import Vue from 'vue'; -import { initProtectedEnvironmentCreate } from 'ee/protected_environments/protected_environment_create'; -import { - initProtectedEnvironmentEditList, - initEditMultipleEnvironmentApprovalRules, -} from 'ee/protected_environments/protected_environment_edit_list'; import { initGroupProtectedEnvironmentList } from 'ee/protected_environments/group_protected_environment_list'; +import { initProtectedEnvironments } from 'ee/protected_environments/protected_environments'; import LicenseManagement from 'ee/vue_shared/license_compliance/license_management.vue'; import createStore from 'ee/vue_shared/license_compliance/store/index'; import showToast from '~/vue_shared/plugins/global_toast'; @@ -33,7 +29,5 @@ if (el?.dataset?.apiUrl) { toasts.forEach((toast) => showToast(toast.dataset.message)); -initProtectedEnvironmentEditList(); initGroupProtectedEnvironmentList(); -initProtectedEnvironmentCreate(); -initEditMultipleEnvironmentApprovalRules(); +initProtectedEnvironments(); diff --git a/ee/app/assets/javascripts/protected_environments/add_approvers.vue b/ee/app/assets/javascripts/protected_environments/add_approvers.vue index 00e7b5f3dc6922a04c366887022ab16e58a72417..9208c2e1ab75359ddb31b0aee1afb0f4e4601aae 100644 --- a/ee/app/assets/javascripts/protected_environments/add_approvers.vue +++ b/ee/app/assets/javascripts/protected_environments/add_approvers.vue @@ -68,29 +68,20 @@ export default { required: false, default: false, }, + approvalRules: { + type: Array, + required: false, + default: () => [], + }, }, data() { return { approvers: [], - approverInfo: [], + approverInfo: this.approvalRules, uniqueId: uniqueId('deployment-approvers-'), }; }, computed: { - approvalRules() { - return this.approverInfo.map((info) => { - switch (info.type) { - case 'user': - return { user_id: info.id, required_approvals: info.approvals }; - case 'group': - return { group_id: info.id, required_approvals: info.approvals }; - case 'access': - return { access_level: info.accessLevel, required_approvals: info.approvals }; - default: - return {}; - } - }); - }, hasSelectedApprovers() { return Boolean(this.approvers.length); }, @@ -118,6 +109,8 @@ export default { }); }), ); + + this.emitApprovalRules(); } catch (e) { Sentry.captureException(e); this.$emit( @@ -128,14 +121,21 @@ export default { ); } }, - approvalRules() { - this.$emit('change', this.approvalRules); + approvalRules(rules, oldRules) { + if (!rules.length && oldRules.length) { + this.approvers = rules; + } }, }, methods: { updateApprovers(permissions) { this.approvers = permissions; }, + updateApproverInfo(approver, approvals) { + const i = this.approverInfo.indexOf(approver); + this.$set(this.approverInfo, i, { ...approver, approvals }); + this.emitApprovalRules(); + }, removeApprover({ type, id }) { const key = ID_FOR_TYPE[type]; const index = this.approvers.findIndex(({ [key]: i }) => id === i); @@ -148,6 +148,21 @@ export default { approvalsId(index) { return `${this.uniqueId}-${index}`; }, + emitApprovalRules() { + const rules = this.approverInfo.map((info) => { + switch (info.type) { + case 'user': + return { user_id: info.id, required_approvals: info.approvals }; + case 'group': + return { group_id: info.id, required_approvals: info.approvals }; + case 'access': + return { access_level: info.accessLevel, required_approvals: info.approvals }; + default: + return {}; + } + }); + this.$emit('change', rules); + }, }, i18n: { approverLabel: s__('ProtectedEnvironment|Approvers'), @@ -213,11 +228,12 @@ export default { > <gl-form-input :id="approvalsId(index)" - v-model="approver.approvals" + :value="approver.approvals" :disabled="approver.inputDisabled" :state="isApprovalValid(approver.approvals)" :name="`approval-count-${approver.name}`" type="number" + @input="updateApproverInfo(approver, $event)" /> <template #invalid-feedback> {{ $options.i18n.approvalsInvalid }} diff --git a/ee/app/assets/javascripts/protected_environments/create_protected_environment.vue b/ee/app/assets/javascripts/protected_environments/create_protected_environment.vue index 1f64abcc41c2b8a266ddb5109f045ab16d6545d0..35b11f3b6206821808c6a864c460c0f231853494 100644 --- a/ee/app/assets/javascripts/protected_environments/create_protected_environment.vue +++ b/ee/app/assets/javascripts/protected_environments/create_protected_environment.vue @@ -3,6 +3,7 @@ import { GlAlert, GlButton, GlCard, + GlForm, GlFormGroup, GlCollapse, GlCollapsibleListbox, @@ -25,6 +26,7 @@ export default { GlButton, GlCard, GlCollapse, + GlForm, GlFormGroup, GlCollapsibleListbox, GlLink, @@ -33,16 +35,12 @@ export default { AddApprovers, }, mixins: [glFeatureFlagsMixin()], - inject: { accessLevelsData: { default: [] }, apiLink: {}, docsLink: {} }, - props: { - searchUnprotectedEnvironmentsUrl: { - type: String, - required: true, - }, - projectId: { - type: String, - required: true, - }, + inject: { + accessLevelsData: { default: [] }, + apiLink: {}, + docsLink: {}, + projectId: { default: '' }, + searchUnprotectedEnvironmentsUrl: { default: '' }, }, data() { return { @@ -55,6 +53,7 @@ export default { environmentsLoading: false, errorMessage: '', alertDismissed: false, + loading: false, }; }, computed: { @@ -101,6 +100,7 @@ export default { }, submitForm() { this.errorMessage = ''; + this.loading = true; const protectedEnvironment = { name: this.environment, @@ -109,11 +109,17 @@ export default { }; Api.createProtectedEnvironment(this.projectId, protectedEnvironment) .then(() => { - window.location.reload(); + this.$emit('reload'); + this.deployers = []; + this.approvers = []; + this.environment = ''; }) .catch((error) => { Sentry.captureException(error); this.errorMessage = __('Failed to protect the environment'); + }) + .finally(() => { + this.loading = false; }); }, }, @@ -134,81 +140,90 @@ export default { ), buttonText: s__('ProtectedEnvironment|Protect'), }, - APPROVAL_COUNT_OPTIONS: ['0', '1', '2', '3', '4', '5'].map((value) => ({ value, text: value })), }; </script> <template> - <gl-card data-testid="new-protected-environment"> - <template #header> - {{ $options.i18n.header }} - </template> - <template #default> - <gl-alert v-if="errorMessage" variant="danger" class="gl-mb-5" @dismiss="errorMessage = ''"> - {{ errorMessage }} - </gl-alert> - <gl-alert - v-if="!alertDismissed" - :title="$options.i18n.unifiedRulesAlertHeader" - class="gl-mb-5" - @dismiss="alertDismissed = false" - > - <p> - <gl-sprintf :message="$options.i18n.unifiedRulesAlertText"> - <template #apiLink="{ content }"> - <gl-link :href="apiLink">{{ content }}</gl-link> - </template> - <template #docsLink="{ content }"> - <gl-link :href="docsLink">{{ content }}</gl-link> - </template> - </gl-sprintf> - </p> - </gl-alert> - <gl-form-group - label-for="environment" - data-testid="create-environment" - :label="$options.i18n.environmentLabel" - > - <gl-collapsible-listbox - id="create-environment" - v-model="environment" - :toggle-text="environmentText" - :items="environments" - :searching="environmentsLoading" - searchable - @shown="fetchEnvironments" - @search="getProtectedEnvironments" - /> - </gl-form-group> - - <gl-collapse :visible="hasSelectedEnvironment"> + <gl-form @submit.prevent="submitForm"> + <gl-card data-testid="new-protected-environment"> + <template #header> + {{ $options.i18n.header }} + </template> + <template #default> + <gl-alert v-if="errorMessage" variant="danger" class="gl-mb-5" @dismiss="errorMessage = ''"> + {{ errorMessage }} + </gl-alert> + <gl-alert + v-if="!alertDismissed" + :title="$options.i18n.unifiedRulesAlertHeader" + class="gl-mb-5" + @dismiss="alertDismissed = false" + > + <p> + <gl-sprintf :message="$options.i18n.unifiedRulesAlertText"> + <template #apiLink="{ content }"> + <gl-link :href="apiLink">{{ content }}</gl-link> + </template> + <template #docsLink="{ content }"> + <gl-link :href="docsLink">{{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + </gl-alert> <gl-form-group - data-testid="create-deployer-dropdown" - label-for="create-deployer-dropdown" - :label="$options.i18n.deployerLabel" + label-for="environment" + data-testid="create-environment" + :label="$options.i18n.environmentLabel" > - <template #label-description> - {{ $options.i18n.deployerHelp }} - </template> - <access-dropdown - id="create-deployer-dropdown" - :access-levels-data="accessLevelsData" - :access-level="$options.ACCESS_LEVELS.DEPLOY" - :disabled="disabled" - :preselected-items="deployers" - @hidden="updateDeployers" + <gl-collapsible-listbox + id="create-environment" + v-model="environment" + :toggle-text="environmentText" + :items="environments" + :searching="environmentsLoading" + searchable + @shown="fetchEnvironments" + @search="getProtectedEnvironments" /> </gl-form-group> - <add-approvers - :project-id="projectId" - @change="updateApprovers" - @error="errorMessage = $event" - /> - </gl-collapse> - </template> - <template #footer> - <gl-button category="primary" variant="confirm" :disabled="isFormInvalid" @click="submitForm"> - {{ $options.i18n.buttonText }} - </gl-button> - </template> - </gl-card> + + <gl-collapse :visible="hasSelectedEnvironment"> + <gl-form-group + data-testid="create-deployer-dropdown" + label-for="create-deployer-dropdown" + :label="$options.i18n.deployerLabel" + > + <template #label-description> + {{ $options.i18n.deployerHelp }} + </template> + <access-dropdown + id="create-deployer-dropdown" + :access-levels-data="accessLevelsData" + :access-level="$options.ACCESS_LEVELS.DEPLOY" + :disabled="disabled" + :items="deployers" + @select="updateDeployers" + /> + </gl-form-group> + <add-approvers + :project-id="projectId" + :approval-rules="approvers" + @change="updateApprovers" + @error="errorMessage = $event" + /> + </gl-collapse> + </template> + <template #footer> + <gl-button + type="submit" + category="primary" + variant="confirm" + :loading="loading" + :disabled="isFormInvalid" + class="js-no-auto-disable" + > + {{ $options.i18n.buttonText }} + </gl-button> + </template> + </gl-card> + </gl-form> </template> diff --git a/ee/app/assets/javascripts/protected_environments/protected_environment_create.js b/ee/app/assets/javascripts/protected_environments/protected_environment_create.js deleted file mode 100644 index 8e851804d832d271375de3f1e3bbcb2d50c7ef15..0000000000000000000000000000000000000000 --- a/ee/app/assets/javascripts/protected_environments/protected_environment_create.js +++ /dev/null @@ -1,27 +0,0 @@ -import Vue from 'vue'; -import CreateProtectedEnvironment from './create_protected_environment.vue'; - -export const initProtectedEnvironmentCreate = () => { - const el = document.querySelector('.js-protected-environment-create-form'); - if (!el) { - return null; - } - - const { projectId, apiLink, docsLink } = el.dataset; - return new Vue({ - el, - provide: { - accessLevelsData: gon?.deploy_access_levels?.roles ?? [], - apiLink, - docsLink, - }, - render(h) { - return h(CreateProtectedEnvironment, { - props: { - projectId, - searchUnprotectedEnvironmentsUrl: gon.search_unprotected_environments_url, - }, - }); - }, - }); -}; diff --git a/ee/app/assets/javascripts/protected_environments/protected_environment_edit_list.js b/ee/app/assets/javascripts/protected_environments/protected_environment_edit_list.js deleted file mode 100644 index 6c44fa013f10e870432dc0674c63f28cd7bb815e..0000000000000000000000000000000000000000 --- a/ee/app/assets/javascripts/protected_environments/protected_environment_edit_list.js +++ /dev/null @@ -1,79 +0,0 @@ -import * as Sentry from '@sentry/browser'; -import Vue from 'vue'; -import Vuex from 'vuex'; -import { GlToast } from '@gitlab/ui'; -import { createStore } from './store/edit'; -import ProtectedEnvironmentEdit from './protected_environment_edit.vue'; -import EditProtectedEnvironmentList from './edit_protected_environments_list.vue'; - -Vue.use(GlToast); - -export const initProtectedEnvironmentEditList = () => { - const parentContainer = document.querySelector('.js-protected-environments-list'); - const envEditFormEls = parentContainer?.querySelectorAll('.js-protected-environment-edit-form'); - - if (envEditFormEls?.length) { - envEditFormEls.forEach((el) => { - const { - url, - label, - disabled, - preselectedItems, - requiredApprovalCount, - environmentName, - environmentLink, - deleteProtectedEnvironmentLink, - } = el.dataset; - - let preselected = []; - try { - preselected = JSON.parse(preselectedItems); - } catch (e) { - Sentry.captureException(e); - } - return new Vue({ - el, - render(createElement) { - return createElement(ProtectedEnvironmentEdit, { - props: { - parentContainer, - preselectedItems: preselected, - url, - label, - disabled, - requiredApprovalCount: parseInt(requiredApprovalCount, 10), - environmentName, - environmentLink, - deleteProtectedEnvironmentLink, - }, - }); - }, - }); - }); - } -}; - -export const initEditMultipleEnvironmentApprovalRules = () => { - Vue.use(Vuex); - - const el = document.getElementById('js-edit-protected-environment-list'); - - if (!el) { - return null; - } - - const { projectId } = el.dataset; - return new Vue({ - el, - store: createStore({ - ...el.dataset, - }), - provide: { - projectId, - accessLevelsData: gon?.deploy_access_levels?.roles ?? [], - }, - render(createElement) { - return createElement(EditProtectedEnvironmentList); - }, - }); -}; diff --git a/ee/app/assets/javascripts/protected_environments/protected_environments.js b/ee/app/assets/javascripts/protected_environments/protected_environments.js new file mode 100644 index 0000000000000000000000000000000000000000..73d1d3f3d96974d95ae46adfe25ce1fdd82236cc --- /dev/null +++ b/ee/app/assets/javascripts/protected_environments/protected_environments.js @@ -0,0 +1,32 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import { createStore } from './store/edit'; +import ProtectedEnvironmentsApp from './protected_environments_app.vue'; + +export const initProtectedEnvironments = () => { + Vue.use(Vuex); + + const el = document.getElementById('js-protected-environments'); + + if (!el) { + return null; + } + + const { projectId, apiLink, docsLink } = el.dataset; + return new Vue({ + el, + store: createStore({ + ...el.dataset, + }), + provide: { + projectId, + accessLevelsData: gon?.deploy_access_levels?.roles ?? [], + apiLink, + docsLink, + searchUnprotectedEnvironmentsUrl: gon.search_unprotected_environments_url, + }, + render(createElement) { + return createElement(ProtectedEnvironmentsApp); + }, + }); +}; diff --git a/ee/app/assets/javascripts/protected_environments/protected_environments.vue b/ee/app/assets/javascripts/protected_environments/protected_environments.vue index 4c820826fa095b5778354abf86442a8794d79581..a6582cb106fc88839e88a881441b8ff2e7c9457f 100644 --- a/ee/app/assets/javascripts/protected_environments/protected_environments.vue +++ b/ee/app/assets/javascripts/protected_environments/protected_environments.vue @@ -69,6 +69,8 @@ export default { }, unprotect() { this.$emit('unprotect', this.environmentToUnprotect); + }, + clearEnvironment() { this.environmentToUnprotect = null; }, }, @@ -91,6 +93,7 @@ export default { :visible="isUnprotectModalVisible" v-bind="$options.modalOptions" @primary="unprotect" + @hide="clearEnvironment" > {{ confirmUnprotectText }} </gl-modal> diff --git a/ee/app/assets/javascripts/protected_environments/protected_environments_app.vue b/ee/app/assets/javascripts/protected_environments/protected_environments_app.vue new file mode 100644 index 0000000000000000000000000000000000000000..d1722195f8b37ccf3f5a10c9114f373e5f3518bc --- /dev/null +++ b/ee/app/assets/javascripts/protected_environments/protected_environments_app.vue @@ -0,0 +1,21 @@ +<script> +import { mapActions } from 'vuex'; +import EditProtectedEnvironmentList from './edit_protected_environments_list.vue'; +import CreateProtectedEnvironment from './create_protected_environment.vue'; + +export default { + components: { + CreateProtectedEnvironment, + EditProtectedEnvironmentList, + }, + methods: { + ...mapActions(['fetchProtectedEnvironments']), + }, +}; +</script> +<template> + <div> + <create-protected-environment @reload="fetchProtectedEnvironments" /> + <edit-protected-environment-list /> + </div> +</template> diff --git a/ee/app/views/projects/protected_environments/_environments_list.html.haml b/ee/app/views/projects/protected_environments/_environments_list.html.haml deleted file mode 100644 index ecbd578f17c01422540c258f73cf24ee486471b3..0000000000000000000000000000000000000000 --- a/ee/app/views/projects/protected_environments/_environments_list.html.haml +++ /dev/null @@ -1 +0,0 @@ -#js-edit-protected-environment-list{ data: { project_id: @project.id } } diff --git a/ee/app/views/projects/protected_environments/_form.html.haml b/ee/app/views/projects/protected_environments/_form.html.haml deleted file mode 100644 index 96cf130467d71c037131e6055ffee9d75f34a2d8..0000000000000000000000000000000000000000 --- a/ee/app/views/projects/protected_environments/_form.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- add_page_specific_style 'page_bundles/ci_cd_settings' - -.js-protected-environment-create-form{ data: { project_id: @project.id, - api_link: help_page_path('api/protected_environments.md'), - docs_link: help_page_path('ci/environments/deployment_approvals.md', anchor: 'multiple-approval-rules') } } diff --git a/ee/app/views/projects/settings/ci_cd/_protected_environments.html.haml b/ee/app/views/projects/settings/ci_cd/_protected_environments.html.haml index 44e34248a72d94be7af2d84148d03c84cbcf3305..067a4b96629383fa8d6f39139cfce874b8487e9d 100644 --- a/ee/app/views/projects/settings/ci_cd/_protected_environments.html.haml +++ b/ee/app/views/projects/settings/ci_cd/_protected_environments.html.haml @@ -1,5 +1,5 @@ - expanded = expanded_by_default? -- can_admin_project = can?(current_user, :admin_project, @project) +- add_page_specific_style 'page_bundles/ci_cd_settings' - if @project.licensed_feature_available?(:protected_environments) %section.protected-environments-settings.settings.no-animate#js-protected-environments-settings{ class: ('expanded' if expanded) } @@ -17,8 +17,9 @@ - c.with_body do = s_('ProtectedEnvironment|No environments in this project are protected.') - = render 'projects/protected_environments/form' + #js-protected-environments{ data: { project_id: @project.id, + api_link: help_page_path('api/protected_environments.md'), + docs_link: help_page_path('ci/environments/deployment_approvals.md', anchor: 'multiple-approval-rules') } } - = render partial: 'projects/protected_environments/environments_list', locals: { can_admin_project: can_admin_project } = render partial: 'projects/protected_environments/group_environments_list' = render 'projects/protected_environments/deployment_approval_options', project: @project diff --git a/ee/spec/frontend/protected_environments/create_protected_environment_spec.js b/ee/spec/frontend/protected_environments/create_protected_environment_spec.js index 12c347f72d732a22d9df68f59d55e06c65eeb82a..76ec54c53db669d75934ca0e98ab991da89fce87 100644 --- a/ee/spec/frontend/protected_environments/create_protected_environment_spec.js +++ b/ee/spec/frontend/protected_environments/create_protected_environment_spec.js @@ -1,6 +1,6 @@ import MockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; -import { GlAlert, GlCollapsibleListbox, GlFormInput } from '@gitlab/ui'; +import { GlAlert, GlCollapsibleListbox, GlForm } from '@gitlab/ui'; import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; @@ -30,14 +30,8 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { wrapper.findByTestId('create-environment').findComponent(GlCollapsibleListbox); const findAccessDropdown = () => wrapper.findByTestId('create-deployer-dropdown').findComponent(AccessDropdown); - const findRequiredCountForApprover = (name) => - wrapper - .findAllComponents(GlFormInput) - .wrappers.find((w) => w.attributes('name') === `approval-count-${name}`); const findAddApprovers = () => wrapper.findComponent(AddApprovers); - const findApproverDropdown = () => findAddApprovers().findComponent(AccessDropdown); - const findSubmitButton = () => - wrapper.findByRole('button', { name: s__('ProtectedEnvironment|Protect') }); + const findForm = () => wrapper.findComponent(GlForm); beforeEach(() => { window.gon = { @@ -58,13 +52,11 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { projectId = PROJECT_ID, } = {}) => { wrapper = mountExtended(CreateProtectedEnvironment, { - propsData: { - searchUnprotectedEnvironmentsUrl, - projectId, - }, provide: { apiLink: API_LINK, docsLink: DOCS_LINK, + searchUnprotectedEnvironmentsUrl, + projectId, accessLevelsData: [ { id: 40, @@ -92,13 +84,13 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { avatar_url: '/root.png', id: 1, }); - findAccessDropdown().vm.$emit('hidden', deployAccessLevels); + findAccessDropdown().vm.$emit('select', deployAccessLevels); findEnvironmentsListbox().vm.$emit('select', name); - findApproverDropdown().vm.$emit('select', deployAccessLevels); - await waitForPromises(); - findRequiredCountForApprover('root').vm.$emit('input', requiredApprovalCount); - await nextTick(); - await findSubmitButton().vm.$emit('click'); + findAddApprovers().vm.$emit( + 'change', + deployAccessLevels.map((rule) => ({ ...rule, required_approvals: requiredApprovalCount })), + ); + await findForm().trigger('submit'); }; describe('alert', () => { @@ -187,17 +179,6 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { }); }); - describe('on successful protected environment', () => { - it('should reload the page', async () => { - createComponent(); - mockAxios.onPost().replyOnce(HTTP_STATUS_OK); - await submitForm(); - await waitForPromises(); - - expect(window.location.reload).toHaveBeenCalled(); - }); - }); - describe('on failed protected environment', () => { it('should show an error message', async () => { mockAxios.onPost().replyOnce(HTTP_STATUS_BAD_REQUEST, {}); @@ -206,8 +187,6 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { await waitForPromises(); expect(findAlert().text()).toBe(__('Failed to protect the environment')); - - expect(window.location.reload).not.toHaveBeenCalled(); }); }); }); diff --git a/ee/spec/frontend/protected_environments/protected_environments_app_spec.js b/ee/spec/frontend/protected_environments/protected_environments_app_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..3e0f89a986cd3a9d7f1d50bedfb19583b35fe2ea --- /dev/null +++ b/ee/spec/frontend/protected_environments/protected_environments_app_spec.js @@ -0,0 +1,38 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import ProtectedEnvironmentsApp from 'ee/protected_environments/protected_environments_app.vue'; +import EditProtectedEnvironmentsList from 'ee/protected_environments/edit_protected_environments_list.vue'; +import CreateProtectedEnvironment from 'ee/protected_environments/create_protected_environment.vue'; + +Vue.use(Vuex); + +describe('ee/protected_environments/protected_environments_app.vue', () => { + let fetchProtectedEnvironments; + let createApp; + let editApp; + + beforeEach(() => { + fetchProtectedEnvironments = jest.fn(); + const store = new Vuex.Store({ actions: { fetchProtectedEnvironments } }); + const wrapper = shallowMountExtended(ProtectedEnvironmentsApp, { + store, + }); + + createApp = wrapper.findComponent(CreateProtectedEnvironment); + editApp = wrapper.findComponent(EditProtectedEnvironmentsList); + }); + + it('mounts the create app', () => { + expect(createApp.exists()).toBe(true); + }); + + it('re-fetches environments when receiving reload', () => { + createApp.vm.$emit('reload'); + expect(fetchProtectedEnvironments).toHaveBeenCalled(); + }); + + it('mounts the edit app', () => { + expect(editApp.exists()).toBe(true); + }); +});