Commit 9ec44ece authored by Emily Ring's avatar Emily Ring Committed by Natalia Tepluhina

Add loading display when removing Terraform state

Updated terraform vue components to include loading display
Updated associated tests and translations
parent b028eb64
......@@ -80,6 +80,7 @@ export default {
lockingState: s__('Terraform|Locking state'),
name: s__('Terraform|Name'),
pipeline: s__('Terraform|Pipeline'),
removing: s__('Terraform|Removing'),
unknownUser: s__('Terraform|Unknown User'),
unlockingState: s__('Terraform|Unlocking state'),
updatedUser: s__('Terraform|%{user} updated %{timeAgo}'),
......@@ -141,6 +142,15 @@ export default {
</p>
</div>
<div v-else-if="item.loadingRemove" class="gl-mx-3">
<p
class="gl-display-flex gl-justify-content-start gl-align-items-baseline gl-m-0 gl-text-red-500"
>
<gl-loading-icon class="gl-pr-1" />
{{ $options.i18n.removing }}
</p>
</div>
<div
v-else-if="item.lockedAt"
:id="`terraformLockedBadgeContainer${item.name}`"
......
......@@ -9,7 +9,7 @@ import {
GlModal,
GlSprintf,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import { s__, sprintf } from '~/locale';
import addDataToState from '../graphql/mutations/add_data_to_state.mutation.graphql';
import lockState from '../graphql/mutations/lock_state.mutation.graphql';
import unlockState from '../graphql/mutations/unlock_state.mutation.graphql';
......@@ -52,6 +52,7 @@ export default {
),
modalRemove: s__('Terraform|Remove'),
remove: s__('Terraform|Remove state file and versions'),
removeSuccessful: s__('Terraform|%{name} successfully removed'),
unlock: s__('Terraform|Unlock'),
},
computed: {
......@@ -121,10 +122,13 @@ export default {
loadingRemove: true,
});
this.stateActionMutation(removeState);
this.stateActionMutation(
removeState,
sprintf(this.$options.i18n.removeSuccessful, { name: this.state.name }),
);
}
},
stateActionMutation(mutation) {
stateActionMutation(mutation, successMessage = null) {
let errorMessages = [];
this.$apollo
......@@ -143,6 +147,10 @@ export default {
data?.terraformStateLock?.errors ||
data?.terraformStateUnlock?.errors ||
[];
if (errorMessages.length === 0 && successMessage) {
this.$toast.show(successMessage);
}
})
.catch(() => {
errorMessages = [this.$options.i18n.errorUpdate];
......
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import TerraformList from './components/terraform_list.vue';
import resolvers from './graphql/resolvers';
Vue.use(GlToast);
Vue.use(VueApollo);
export default () => {
......
---
title: Display loading when removing Terraform state
merge_request: 53897
author:
type: changed
......@@ -28689,6 +28689,9 @@ msgstr ""
msgid "Terraform"
msgstr ""
msgid "Terraform|%{name} successfully removed"
msgstr ""
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
......@@ -28765,6 +28768,9 @@ msgstr ""
msgid "Terraform|Remove state file and versions"
msgstr ""
msgid "Terraform|Removing"
msgstr ""
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
......
......@@ -68,7 +68,7 @@ RSpec.describe 'Terraform', :js do
fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
click_button 'Remove'
expect(page).not_to have_content(additional_state.name)
expect(page).to have_content("#{additional_state.name} successfully removed")
expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
......
......@@ -14,6 +14,7 @@ localVue.use(VueApollo);
describe('StatesTableActions', () => {
let lockResponse;
let removeResponse;
let toast;
let unlockResponse;
let updateStateResponse;
let wrapper;
......@@ -59,10 +60,13 @@ describe('StatesTableActions', () => {
const createComponent = (propsData = defaultProps) => {
const apolloProvider = createMockApolloProvider();
toast = jest.fn();
wrapper = shallowMount(StateActions, {
apolloProvider,
localVue,
propsData,
mocks: { $toast: { show: toast } },
stubs: { GlDropdown, GlModal, GlSprintf },
});
......@@ -83,6 +87,7 @@ describe('StatesTableActions', () => {
afterEach(() => {
lockResponse = null;
removeResponse = null;
toast = null;
unlockResponse = null;
updateStateResponse = null;
wrapper.destroy();
......@@ -243,7 +248,6 @@ describe('StatesTableActions', () => {
describe('when clicking the remove button', () => {
beforeEach(() => {
findRemoveBtn().vm.$emit('click');
return waitForPromises();
});
......@@ -254,21 +258,70 @@ describe('StatesTableActions', () => {
});
describe('when submitting the remove modal', () => {
it('does not call the remove mutation when state name is missing', async () => {
findRemoveModal().vm.$emit('ok');
await wrapper.vm.$nextTick();
describe('when state name is missing', () => {
beforeEach(() => {
findRemoveModal().vm.$emit('ok');
return waitForPromises();
});
expect(removeResponse).not.toHaveBeenCalledWith();
it('does not call the remove mutation', () => {
expect(removeResponse).not.toHaveBeenCalledWith();
});
});
it('calls the remove mutation when state name is present', async () => {
await wrapper.setData({ removeConfirmText: defaultProps.state.name });
describe('when state name is present', () => {
beforeEach(async () => {
await wrapper.setData({ removeConfirmText: defaultProps.state.name });
findRemoveModal().vm.$emit('ok');
findRemoveModal().vm.$emit('ok');
await wrapper.vm.$nextTick();
await waitForPromises();
});
it('calls the remove mutation', () => {
expect(removeResponse).toHaveBeenCalledWith({ stateID: defaultProps.state.id });
});
it('calls the toast action', () => {
expect(toast).toHaveBeenCalledWith(`${defaultProps.state.name} successfully removed`);
});
expect(removeResponse).toHaveBeenCalledWith({
stateID: defaultProps.state.id,
it('calls mutations to set loading and errors', () => {
// loading update
expect(updateStateResponse).toHaveBeenNthCalledWith(
1,
{},
{
terraformState: {
...defaultProps.state,
_showDetails: false,
errorMessages: [],
loadingLock: false,
loadingRemove: true,
},
},
// Apollo fields
expect.any(Object),
expect.any(Object),
);
// final update
expect(updateStateResponse).toHaveBeenNthCalledWith(
2,
{},
{
terraformState: {
...defaultProps.state,
_showDetails: false,
errorMessages: [],
loadingLock: false,
loadingRemove: false,
},
},
// Apollo fields
expect.any(Object),
expect.any(Object),
);
});
});
});
......
import { GlIcon, GlTooltip } from '@gitlab/ui';
import { GlIcon, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
import StateActions from '~/terraform/components/states_table_actions.vue';
......@@ -92,6 +92,17 @@ describe('StatesTable', () => {
},
},
},
{
_showDetails: false,
errorMessages: [],
name: 'state-5',
loadingLock: false,
loadingRemove: true,
lockedAt: null,
lockedByUser: null,
updatedAt: '2020-10-10T00:00:00Z',
latestVersion: null,
},
],
};
......@@ -112,14 +123,15 @@ describe('StatesTable', () => {
});
it.each`
name | toolTipText | locked | lineNumber
${'state-1'} | ${'Locked by user-1 2 days ago'} | ${true} | ${0}
${'state-2'} | ${'Locking state'} | ${false} | ${1}
${'state-3'} | ${'Unlocking state'} | ${false} | ${2}
${'state-4'} | ${'Locked by Unknown User 5 days ago'} | ${true} | ${3}
name | toolTipText | locked | loading | lineNumber
${'state-1'} | ${'Locked by user-1 2 days ago'} | ${true} | ${false} | ${0}
${'state-2'} | ${'Locking state'} | ${false} | ${true} | ${1}
${'state-3'} | ${'Unlocking state'} | ${false} | ${true} | ${2}
${'state-4'} | ${'Locked by Unknown User 5 days ago'} | ${true} | ${false} | ${3}
${'state-5'} | ${'Removing'} | ${false} | ${true} | ${4}
`(
'displays the name and locked information "$name" for line "$lineNumber"',
({ name, toolTipText, locked, lineNumber }) => {
({ name, toolTipText, locked, loading, lineNumber }) => {
const states = wrapper.findAll('[data-testid="terraform-states-table-name"]');
const state = states.at(lineNumber);
......@@ -127,6 +139,7 @@ describe('StatesTable', () => {
expect(state.text()).toContain(name);
expect(state.find(GlIcon).exists()).toBe(locked);
expect(state.find(GlLoadingIcon).exists()).toBe(loading);
expect(toolTip.exists()).toBe(locked);
if (locked) {
......
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