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