Commit 36b475ac authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch '263402-enable-review-app-button-opens-twice-the-popup' into 'master'

Avoid opening 2 modals for enabling review app

See merge request gitlab-org/gitlab!45361
parents d780fdac c4c06acf
<script> <script>
import { GlButton, GlModal, GlModalDirective, GlLink, GlSprintf } from '@gitlab/ui'; import { GlLink, GlModal, GlSprintf } from '@gitlab/ui';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export default { export default {
components: { components: {
GlButton,
GlLink, GlLink,
GlModal, GlModal,
GlSprintf, GlSprintf,
ModalCopyButton, ModalCopyButton,
}, },
directives: { props: {
'gl-modal': GlModalDirective, modalId: {
type: String,
required: true,
},
}, },
instructionText: { instructionText: {
step1: s__( step1: s__(
...@@ -37,73 +39,60 @@ export default { ...@@ -37,73 +39,60 @@ export default {
- branches - branches
except: except:
- master`, - master`,
id: 'enable-review-app-info',
title: s__('ReviewApp|Enable Review App'), title: s__('ReviewApp|Enable Review App'),
}, },
}; };
</script> </script>
<template> <template>
<div> <gl-modal
<gl-button :modal-id="modalId"
v-gl-modal="$options.modalInfo.id" :title="$options.modalInfo.title"
variant="info" size="lg"
category="secondary" ok-only
type="button" ok-variant="light"
class="gl-w-full js-enable-review-app-button" :ok-title="$options.modalInfo.closeText"
> >
{{ s__('Environments|Enable review app') }} <p>
</gl-button> <gl-sprintf :message="$options.instructionText.step1">
<gl-modal <template #step="{ content }">
:modal-id="$options.modalInfo.id" <strong>{{ content }}</strong>
:title="$options.modalInfo.title" </template>
size="lg" <template #link="{ content }">
class="text-2 ws-normal" <gl-link
ok-only href="https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html"
ok-variant="light" target="_blank"
:ok-title="$options.modalInfo.closeText" >{{ content }}</gl-link
> >
</template>
</gl-sprintf>
</p>
<div>
<p> <p>
<gl-sprintf :message="$options.instructionText.step1"> <gl-sprintf :message="$options.instructionText.step2">
<template #step="{ content }"> <template #step="{ content }">
<strong>{{ content }}</strong> <strong>{{ content }}</strong>
</template> </template>
<template #link="{ content }">
<gl-link
href="https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html"
target="_blank"
>{{ content }}</gl-link
>
</template>
</gl-sprintf> </gl-sprintf>
</p> </p>
<div> <div class="gl-display-flex align-items-start">
<p> <pre class="gl-w-full"> {{ $options.modalInfo.copyString }} </pre>
<gl-sprintf :message="$options.instructionText.step2"> <modal-copy-button
<template #step="{ content }"> :title="$options.modalInfo.copyToClipboardText"
<strong>{{ content }}</strong> :text="$options.modalInfo.copyString"
</template> :modal-id="modalId"
</gl-sprintf> css-classes="border-0"
</p> />
<div class="flex align-items-start">
<pre class="w-100"> {{ $options.modalInfo.copyString }} </pre>
<modal-copy-button
:title="$options.modalInfo.copyToClipboardText"
:text="$options.modalInfo.copyString"
:modal-id="$options.modalInfo.id"
css-classes="border-0"
/>
</div>
</div> </div>
<p> </div>
<gl-sprintf :message="$options.instructionText.step3"> <p>
<template #step="{ content }"> <gl-sprintf :message="$options.instructionText.step3">
<strong>{{ content }}</strong> <template #step="{ content }">
</template> <strong>{{ content }}</strong>
<template #link="{ content }"> </template>
<gl-link href="blob/master/.gitlab-ci.yml" target="_blank">{{ content }}</gl-link> <template #link="{ content }">
</template> <gl-link href="blob/master/.gitlab-ci.yml" target="_blank">{{ content }}</gl-link>
</gl-sprintf> </template>
</p> </gl-sprintf>
</gl-modal> </p>
</div> </gl-modal>
</template> </template>
<script> <script>
import { GlBadge, GlButton, GlTab, GlTabs } from '@gitlab/ui'; import { GlBadge, GlButton, GlModalDirective, GlTab, GlTabs } from '@gitlab/ui';
import { deprecatedCreateFlash as Flash } from '~/flash'; import { deprecatedCreateFlash as Flash } from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import emptyState from './empty_state.vue'; import emptyState from './empty_state.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import environmentsMixin from '../mixins/environments_mixin'; import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '~/vue_shared/mixins/ci_pagination_api_mixin'; import CIPaginationMixin from '~/vue_shared/mixins/ci_pagination_api_mixin';
import EnableReviewAppButton from './enable_review_app_button.vue'; import EnableReviewAppModal from './enable_review_app_modal.vue';
import StopEnvironmentModal from './stop_environment_modal.vue'; import StopEnvironmentModal from './stop_environment_modal.vue';
import DeleteEnvironmentModal from './delete_environment_modal.vue'; import DeleteEnvironmentModal from './delete_environment_modal.vue';
import ConfirmRollbackModal from './confirm_rollback_modal.vue'; import ConfirmRollbackModal from './confirm_rollback_modal.vue';
export default { export default {
i18n: {
newEnvironmentButtonLabel: s__('Environments|New environment'),
reviewAppButtonLabel: s__('Environments|Enable review app'),
},
modal: {
id: 'enable-review-app-info',
},
components: { components: {
ConfirmRollbackModal, ConfirmRollbackModal,
emptyState, emptyState,
EnableReviewAppButton, EnableReviewAppModal,
GlBadge, GlBadge,
GlButton, GlButton,
GlTab, GlTab,
...@@ -23,9 +30,10 @@ export default { ...@@ -23,9 +30,10 @@ export default {
StopEnvironmentModal, StopEnvironmentModal,
DeleteEnvironmentModal, DeleteEnvironmentModal,
}, },
directives: {
'gl-modal': GlModalDirective,
},
mixins: [CIPaginationMixin, environmentsMixin], mixins: [CIPaginationMixin, environmentsMixin],
props: { props: {
endpoint: { endpoint: {
type: String, type: String,
...@@ -140,17 +148,25 @@ export default { ...@@ -140,17 +148,25 @@ export default {
gl-mt-3 gl-mt-3
gl-display-md-none!" gl-display-md-none!"
> >
<enable-review-app-button <gl-button
v-if="state.reviewAppDetails.can_setup_review_app" v-if="state.reviewAppDetails.can_setup_review_app"
v-gl-modal="$options.modal.id"
data-testid="enable-review-app"
variant="info"
category="secondary"
type="button"
class="gl-mb-3 gl-flex-fill-1" class="gl-mb-3 gl-flex-fill-1"
/> >
{{ $options.i18n.reviewAppButtonLabel }}
</gl-button>
<gl-button <gl-button
v-if="canCreateEnvironment" v-if="canCreateEnvironment"
:href="newEnvironmentPath" :href="newEnvironmentPath"
data-testid="new-environment"
category="primary" category="primary"
variant="success" variant="success"
> >
{{ s__('Environments|New environment') }} {{ $options.i18n.newEnvironmentButtonLabel }}
</gl-button> </gl-button>
</div> </div>
<gl-tabs content-class="gl-display-none"> <gl-tabs content-class="gl-display-none">
...@@ -176,17 +192,25 @@ export default { ...@@ -176,17 +192,25 @@ export default {
gl-lg-justify-content-end gl-lg-justify-content-end
gl-lg-mt-0" gl-lg-mt-0"
> >
<enable-review-app-button <gl-button
v-if="state.reviewAppDetails.can_setup_review_app" v-if="state.reviewAppDetails.can_setup_review_app"
v-gl-modal="$options.modal.id"
data-testid="enable-review-app"
variant="info"
category="secondary"
type="button"
class="gl-mb-3 gl-lg-mr-3 gl-lg-mb-0" class="gl-mb-3 gl-lg-mr-3 gl-lg-mb-0"
/> >
{{ $options.i18n.reviewAppButtonLabel }}
</gl-button>
<gl-button <gl-button
v-if="canCreateEnvironment" v-if="canCreateEnvironment"
:href="newEnvironmentPath" :href="newEnvironmentPath"
data-testid="new-environment"
category="primary" category="primary"
variant="success" variant="success"
> >
{{ s__('Environments|New environment') }} {{ $options.i18n.newEnvironmentButtonLabel }}
</gl-button> </gl-button>
</div> </div>
</template> </template>
...@@ -208,6 +232,11 @@ export default { ...@@ -208,6 +232,11 @@ export default {
<empty-state :help-path="helpPagePath" /> <empty-state :help-path="helpPagePath" />
</template> </template>
</container> </container>
<enable-review-app-modal
v-if="state.reviewAppDetails.can_setup_review_app"
:modal-id="$options.modal.id"
data-testid="enable-review-app-modal"
/>
</div> </div>
</div> </div>
</template> </template>
---
title: Avooid opening 2 modals for enabling review app
merge_request: 45361
author:
type: fixed
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import EnableReviewAppButton from '~/environments/components/enable_review_app_button.vue'; import EnableReviewAppButton from '~/environments/components/enable_review_app_modal.vue';
describe('Enable Review App Button', () => { describe('Enable Review App Button', () => {
let wrapper; let wrapper;
...@@ -9,19 +9,13 @@ describe('Enable Review App Button', () => { ...@@ -9,19 +9,13 @@ describe('Enable Review App Button', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('renders button with text', () => {
beforeEach(() => {
wrapper = mount(EnableReviewAppButton);
});
it('renders Enable Review text', () => {
expect(wrapper.text()).toBe('Enable review app');
});
});
describe('renders the modal', () => { describe('renders the modal', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(EnableReviewAppButton); wrapper = shallowMount(EnableReviewAppButton, {
propsData: {
modalId: 'fake-id',
},
});
}); });
it('renders the copyToClipboard button', () => { it('renders the copyToClipboard button', () => {
......
import { GlButton } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import EnableReviewAppModal from '~/environments/components/enable_review_app_modal.vue';
import Container from '~/environments/components/container.vue'; import Container from '~/environments/components/container.vue';
import EmptyState from '~/environments/components/empty_state.vue'; import EmptyState from '~/environments/components/empty_state.vue';
import EnvironmentsApp from '~/environments/components/environments_app.vue'; import EnvironmentsApp from '~/environments/components/environments_app.vue';
...@@ -35,13 +36,15 @@ describe('Environment', () => { ...@@ -35,13 +36,15 @@ describe('Environment', () => {
}); });
}; };
const createWrapper = (shallow = false) => { const createWrapper = (shallow = false, props = {}) => {
const fn = shallow ? shallowMount : mount; const fn = shallow ? shallowMount : mount;
wrapper = fn(EnvironmentsApp, { propsData: mockData }); wrapper = extendedWrapper(fn(EnvironmentsApp, { propsData: { ...mockData, ...props } }));
return axios.waitForAll(); return axios.waitForAll();
}; };
const findNewEnvironmentButton = () => wrapper.find(GlButton); const findEnableReviewAppButton = () => wrapper.findByTestId('enable-review-app');
const findEnableReviewAppModal = () => wrapper.findAll(EnableReviewAppModal);
const findNewEnvironmentButton = () => wrapper.findByTestId('new-environment');
const findEnvironmentsTabAvailable = () => wrapper.find('.js-environments-tab-available > a'); const findEnvironmentsTabAvailable = () => wrapper.find('.js-environments-tab-available > a');
const findEnvironmentsTabStopped = () => wrapper.find('.js-environments-tab-stopped > a'); const findEnvironmentsTabStopped = () => wrapper.find('.js-environments-tab-stopped > a');
...@@ -64,19 +67,6 @@ describe('Environment', () => { ...@@ -64,19 +67,6 @@ describe('Environment', () => {
it('should render the empty state', () => { it('should render the empty state', () => {
expect(wrapper.find(EmptyState).exists()).toBe(true); expect(wrapper.find(EmptyState).exists()).toBe(true);
}); });
describe('when it is possible to enable a review app', () => {
beforeEach(() => {
mockRequest(200, { environments: [], review_app: { can_setup_review_app: true } });
return createWrapper();
});
it('should render the enable review app button', () => {
expect(wrapper.find('.js-enable-review-app-button').text()).toContain(
'Enable review app',
);
});
});
}); });
describe('with paginated environments', () => { describe('with paginated environments', () => {
...@@ -91,7 +81,7 @@ describe('Environment', () => { ...@@ -91,7 +81,7 @@ describe('Environment', () => {
return createWrapper(); return createWrapper();
}); });
it('should render a conatiner table with environments', () => { it('should render a container table with environments', () => {
const containerTable = wrapper.find(Container); const containerTable = wrapper.find(Container);
expect(containerTable.exists()).toBe(true); expect(containerTable.exists()).toBe(true);
...@@ -181,8 +171,8 @@ describe('Environment', () => { ...@@ -181,8 +171,8 @@ describe('Environment', () => {
describe('environment button', () => { describe('environment button', () => {
describe('when user can create environment', () => { describe('when user can create environment', () => {
beforeEach(() => { beforeEach(() => {
mockRequest([environment]); mockRequest(200, { environments: [] });
wrapper = shallowMount(EnvironmentsApp, { propsData: mockData }); return createWrapper(true);
}); });
it('should render', () => { it('should render', () => {
...@@ -192,10 +182,8 @@ describe('Environment', () => { ...@@ -192,10 +182,8 @@ describe('Environment', () => {
describe('when user can not create environment', () => { describe('when user can not create environment', () => {
beforeEach(() => { beforeEach(() => {
mockRequest([environment]); mockRequest(200, { environments: [] });
wrapper = shallowMount(EnvironmentsApp, { return createWrapper(true, { ...mockData, canCreateEnvironment: false });
propsData: { ...mockData, canCreateEnvironment: false },
});
}); });
it('should not render', () => { it('should not render', () => {
...@@ -203,4 +191,41 @@ describe('Environment', () => { ...@@ -203,4 +191,41 @@ describe('Environment', () => {
}); });
}); });
}); });
describe('review app modal', () => {
describe('when it is not possible to enable a review app', () => {
beforeEach(() => {
mockRequest(200, { environments: [] });
return createWrapper(true);
});
it('should not render the enable review app button', () => {
expect(findEnableReviewAppButton().exists()).toBe(false);
});
it('should not render a review app modal', () => {
const modal = findEnableReviewAppModal();
expect(modal).toHaveLength(0);
expect(modal.exists()).toBe(false);
});
});
describe('when it is possible to enable a review app', () => {
beforeEach(() => {
mockRequest(200, { environments: [], review_app: { can_setup_review_app: true } });
return createWrapper(true);
});
it('should render the enable review app button', () => {
expect(findEnableReviewAppButton().exists()).toBe(true);
expect(findEnableReviewAppButton().text()).toContain('Enable review app');
});
it('should render only one review app modal', () => {
const modal = findEnableReviewAppModal();
expect(modal).toHaveLength(1);
expect(modal.at(0).exists()).toBe(true);
});
});
});
}); });
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