Commit 4f163766 authored by Simon Knox's avatar Simon Knox

Merge branch 'afontaine/add-buttons-back-to-environments' into 'master'

Add new environment and review app buttons back

See merge request gitlab-org/gitlab!75947
parents 44609bdc 6071b0e9
...@@ -12,11 +12,20 @@ export default { ...@@ -12,11 +12,20 @@ export default {
ModalCopyButton, ModalCopyButton,
}, },
inject: ['defaultBranchName'], inject: ['defaultBranchName'],
model: {
prop: 'visible',
event: 'change',
},
props: { props: {
modalId: { modalId: {
type: String, type: String,
required: true, required: true,
}, },
visible: {
type: Boolean,
required: false,
default: false,
},
}, },
instructionText: { instructionText: {
step1: s__( step1: s__(
...@@ -57,12 +66,15 @@ export default { ...@@ -57,12 +66,15 @@ export default {
</script> </script>
<template> <template>
<gl-modal <gl-modal
:visible="visible"
:modal-id="modalId" :modal-id="modalId"
:title="$options.modalInfo.title" :title="$options.modalInfo.title"
static
size="lg" size="lg"
ok-only ok-only
ok-variant="light" ok-variant="light"
:ok-title="$options.modalInfo.closeText" :ok-title="$options.modalInfo.closeText"
@change="$emit('change', $event)"
> >
<p> <p>
<gl-sprintf :message="$options.instructionText.step1"> <gl-sprintf :message="$options.instructionText.step1">
......
<script> <script>
import { GlBadge, GlTab, GlTabs } from '@gitlab/ui'; import { GlBadge, GlTab, GlTabs } from '@gitlab/ui';
import { s__ } from '~/locale';
import environmentAppQuery from '../graphql/queries/environment_app.query.graphql'; import environmentAppQuery from '../graphql/queries/environment_app.query.graphql';
import pollIntervalQuery from '../graphql/queries/poll_interval.query.graphql'; import pollIntervalQuery from '../graphql/queries/poll_interval.query.graphql';
import EnvironmentFolder from './new_environment_folder.vue'; import EnvironmentFolder from './new_environment_folder.vue';
import EnableReviewAppModal from './enable_review_app_modal.vue';
export default { export default {
components: { components: {
EnvironmentFolder, EnvironmentFolder,
EnableReviewAppModal,
GlBadge, GlBadge,
GlTab, GlTab,
GlTabs, GlTabs,
...@@ -22,22 +25,73 @@ export default { ...@@ -22,22 +25,73 @@ export default {
query: pollIntervalQuery, query: pollIntervalQuery,
}, },
}, },
inject: ['newEnvironmentPath', 'canCreateEnvironment'],
i18n: {
newEnvironmentButtonLabel: s__('Environments|New environment'),
reviewAppButtonLabel: s__('Environments|Enable review app'),
},
modalId: 'enable-review-app-info',
data() { data() {
return { interval: undefined }; return { interval: undefined, isReviewAppModalVisible: false };
}, },
computed: { computed: {
canSetupReviewApp() {
return this.environmentApp?.reviewApp?.canSetupReviewApp;
},
folders() { folders() {
return this.environmentApp?.environments.filter((e) => e.size > 1) ?? []; return this.environmentApp?.environments.filter((e) => e.size > 1) ?? [];
}, },
availableCount() { availableCount() {
return this.environmentApp?.availableCount; return this.environmentApp?.availableCount;
}, },
addEnvironment() {
if (!this.canCreateEnvironment) {
return null;
}
return {
text: this.$options.i18n.newEnvironmentButtonLabel,
attributes: {
href: this.newEnvironmentPath,
category: 'primary',
variant: 'confirm',
},
};
},
openReviewAppModal() {
if (!this.canSetupReviewApp) {
return null;
}
return {
text: this.$options.i18n.reviewAppButtonLabel,
attributes: {
category: 'secondary',
variant: 'confirm',
},
};
},
},
methods: {
showReviewAppModal() {
this.isReviewAppModalVisible = true;
},
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
<gl-tabs> <enable-review-app-modal
v-if="canSetupReviewApp"
v-model="isReviewAppModalVisible"
:modal-id="$options.modalId"
data-testid="enable-review-app-modal"
/>
<gl-tabs
:action-secondary="addEnvironment"
:action-primary="openReviewAppModal"
@primary="showReviewAppModal"
>
<gl-tab> <gl-tab>
<template #title> <template #title>
<span>{{ __('Available') }}</span> <span>{{ __('Available') }}</span>
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import EnableReviewAppButton from '~/environments/components/enable_review_app_modal.vue'; import EnableReviewAppButton from '~/environments/components/enable_review_app_modal.vue';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
describe('Enable Review App Button', () => { describe('Enable Review App Button', () => {
let wrapper; let wrapper;
let modal;
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -16,12 +18,15 @@ describe('Enable Review App Button', () => { ...@@ -16,12 +18,15 @@ describe('Enable Review App Button', () => {
shallowMount(EnableReviewAppButton, { shallowMount(EnableReviewAppButton, {
propsData: { propsData: {
modalId: 'fake-id', modalId: 'fake-id',
visible: true,
}, },
provide: { provide: {
defaultBranchName: 'main', defaultBranchName: 'main',
}, },
}), }),
); );
modal = wrapper.findComponent(GlModal);
}); });
it('renders the defaultBranchName copy', () => { it('renders the defaultBranchName copy', () => {
...@@ -32,5 +37,15 @@ describe('Enable Review App Button', () => { ...@@ -32,5 +37,15 @@ describe('Enable Review App Button', () => {
it('renders the copyToClipboard button', () => { it('renders the copyToClipboard button', () => {
expect(wrapper.findComponent(ModalCopyButton).exists()).toBe(true); expect(wrapper.findComponent(ModalCopyButton).exists()).toBe(true);
}); });
it('emits change events from the modal up', () => {
modal.vm.$emit('change', false);
expect(wrapper.emitted('change')).toEqual([[false]]);
});
it('passes visible to the modal', () => {
expect(modal.props('visible')).toBe(true);
});
}); });
}); });
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { mount } from '@vue/test-utils'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { s__ } from '~/locale';
import EnvironmentsApp from '~/environments/components/new_environments_app.vue'; import EnvironmentsApp from '~/environments/components/new_environments_app.vue';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue'; import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data'; import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data';
...@@ -22,7 +23,16 @@ describe('~/environments/components/new_environments_app.vue', () => { ...@@ -22,7 +23,16 @@ describe('~/environments/components/new_environments_app.vue', () => {
return createMockApollo([], mockResolvers); return createMockApollo([], mockResolvers);
}; };
const createWrapper = (apolloProvider) => mount(EnvironmentsApp, { apolloProvider }); const createWrapper = ({ provide = {}, apolloProvider } = {}) =>
mountExtended(EnvironmentsApp, {
provide: {
newEnvironmentPath: '/environments/new',
canCreateEnvironment: true,
defaultBranchName: 'main',
...provide,
},
apolloProvider,
});
beforeEach(() => { beforeEach(() => {
environmentAppMock = jest.fn(); environmentAppMock = jest.fn();
...@@ -37,7 +47,7 @@ describe('~/environments/components/new_environments_app.vue', () => { ...@@ -37,7 +47,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
environmentAppMock.mockReturnValue(resolvedEnvironmentsApp); environmentAppMock.mockReturnValue(resolvedEnvironmentsApp);
environmentFolderMock.mockReturnValue(resolvedFolder); environmentFolderMock.mockReturnValue(resolvedFolder);
const apolloProvider = createApolloProvider(); const apolloProvider = createApolloProvider();
wrapper = createWrapper(apolloProvider); wrapper = createWrapper({ apolloProvider });
await waitForPromises(); await waitForPromises();
await Vue.nextTick(); await Vue.nextTick();
...@@ -47,4 +57,66 @@ describe('~/environments/components/new_environments_app.vue', () => { ...@@ -47,4 +57,66 @@ describe('~/environments/components/new_environments_app.vue', () => {
expect(text).toContainEqual(expect.stringMatching('review')); expect(text).toContainEqual(expect.stringMatching('review'));
expect(text).not.toContainEqual(expect.stringMatching('production')); expect(text).not.toContainEqual(expect.stringMatching('production'));
}); });
it('should show a button to create a new environment', async () => {
environmentAppMock.mockReturnValue(resolvedEnvironmentsApp);
environmentFolderMock.mockReturnValue(resolvedFolder);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider });
await waitForPromises();
await Vue.nextTick();
const button = wrapper.findByRole('link', { name: s__('Environments|New environment') });
expect(button.attributes('href')).toBe('/environments/new');
});
it('should not show a button to create a new environment if the user has no permissions', async () => {
environmentAppMock.mockReturnValue(resolvedEnvironmentsApp);
environmentFolderMock.mockReturnValue(resolvedFolder);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({
apolloProvider,
provide: { canCreateEnvironment: false, newEnvironmentPath: '' },
});
await waitForPromises();
await Vue.nextTick();
const button = wrapper.findByRole('link', { name: s__('Environments|New environment') });
expect(button.exists()).toBe(false);
});
it('should show a button to open the review app modal', async () => {
environmentAppMock.mockReturnValue(resolvedEnvironmentsApp);
environmentFolderMock.mockReturnValue(resolvedFolder);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider });
await waitForPromises();
await Vue.nextTick();
const button = wrapper.findByRole('button', { name: s__('Environments|Enable review app') });
button.trigger('click');
await Vue.nextTick();
expect(wrapper.findByText(s__('ReviewApp|Enable Review App')).exists()).toBe(true);
});
it('should not show a button to open the review app modal if review apps are configured', async () => {
environmentAppMock.mockReturnValue({
...resolvedEnvironmentsApp,
reviewApp: { canSetupReviewApp: false },
});
environmentFolderMock.mockReturnValue(resolvedFolder);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider });
await waitForPromises();
await Vue.nextTick();
const button = wrapper.findByRole('button', { name: s__('Environments|Enable review app') });
expect(button.exists()).toBe(false);
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment