Commit 6247dea3 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '292713-remove-loading-tf' into 'master'

Additional remove loading display for Terraform list

See merge request gitlab-org/gitlab!53897
parents d6d09424 9ec44ece
......@@ -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 removeState from '../graphql/mutations/remove_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
......@@ -28728,6 +28728,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] ""
......@@ -28804,6 +28807,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 StatesTable from '~/terraform/components/states_table.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