Commit 69ec8c2f authored by Enrique Alcantara's avatar Enrique Alcantara

Replace Vuex with VueApollo for submitting changes

Use VueApollo instead of Vuex when submitting
changes in the Static Site Editor
parent 69bdc477
<script> <script>
import { mapState, mapActions } from 'vuex';
import SkeletonLoader from '../components/skeleton_loader.vue'; import SkeletonLoader from '../components/skeleton_loader.vue';
import EditArea from '../components/edit_area.vue'; import EditArea from '../components/edit_area.vue';
import InvalidContentMessage from '../components/invalid_content_message.vue'; import InvalidContentMessage from '../components/invalid_content_message.vue';
import SubmitChangesError from '../components/submit_changes_error.vue'; import SubmitChangesError from '../components/submit_changes_error.vue';
import { SUCCESS_ROUTE } from '../router/constants';
import appDataQuery from '../graphql/queries/app_data.query.graphql'; import appDataQuery from '../graphql/queries/app_data.query.graphql';
import sourceContentQuery from '../graphql/queries/source_content.query.graphql'; import sourceContentQuery from '../graphql/queries/source_content.query.graphql';
import submitContentChangesMutation from '../graphql/mutations/submit_content_changes.mutation.graphql';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { LOAD_CONTENT_ERROR } from '../constants'; import { LOAD_CONTENT_ERROR } from '../constants';
import { SUCCESS_ROUTE } from '../router/constants';
export default { export default {
components: { components: {
...@@ -44,8 +44,14 @@ export default { ...@@ -44,8 +44,14 @@ export default {
}, },
}, },
}, },
data() {
return {
content: null,
submitChangesError: null,
isSavingChanges: false,
};
},
computed: { computed: {
...mapState(['isSavingChanges', 'submitChangesError']),
isLoadingContent() { isLoadingContent() {
return this.$apollo.queries.sourceContent.loading; return this.$apollo.queries.sourceContent.loading;
}, },
...@@ -54,11 +60,35 @@ export default { ...@@ -54,11 +60,35 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions(['setContent', 'submitChanges', 'dismissSubmitChangesError']), onDismissError() {
this.submitChangesError = null;
},
onSubmit({ content }) { onSubmit({ content }) {
this.setContent(content); this.content = content;
this.submitChanges();
},
submitChanges() {
this.isSavingChanges = true;
return this.submitChanges().then(() => this.$router.push(SUCCESS_ROUTE)); this.$apollo
.mutate({
mutation: submitContentChangesMutation,
variables: {
project: this.appData.project,
username: this.appData.username,
sourcePath: this.appData.sourcePath,
content: this.content,
},
})
.then(() => {
this.$router.push(SUCCESS_ROUTE);
})
.catch(e => {
this.submitChangesError = e.message;
})
.finally(() => {
this.isSavingChanges = false;
});
}, },
}, },
}; };
...@@ -71,7 +101,7 @@ export default { ...@@ -71,7 +101,7 @@ export default {
v-if="submitChangesError" v-if="submitChangesError"
:error="submitChangesError" :error="submitChangesError"
@retry="submitChanges" @retry="submitChanges"
@dismiss="dismissSubmitChangesError" @dismiss="onDismissError"
/> />
<edit-area <edit-area
v-if="isContentLoaded" v-if="isContentLoaded"
......
<script> <script>
import { mapState } from 'vuex'; import savedContentMetaQuery from '../graphql/queries/saved_content_meta.query.graphql';
import appDataQuery from '../graphql/queries/app_data.query.graphql';
import SavedChangesMessage from '../components/saved_changes_message.vue'; import SavedChangesMessage from '../components/saved_changes_message.vue';
import { HOME_ROUTE } from '../router/constants'; import { HOME_ROUTE } from '../router/constants';
...@@ -7,8 +8,13 @@ export default { ...@@ -7,8 +8,13 @@ export default {
components: { components: {
SavedChangesMessage, SavedChangesMessage,
}, },
computed: { apollo: {
...mapState(['savedContentMeta', 'returnUrl']), savedContentMeta: {
query: savedContentMetaQuery,
},
appData: {
query: appDataQuery,
},
}, },
created() { created() {
if (!this.savedContentMeta) { if (!this.savedContentMeta) {
...@@ -23,7 +29,7 @@ export default { ...@@ -23,7 +29,7 @@ export default {
:branch="savedContentMeta.branch" :branch="savedContentMeta.branch"
:commit="savedContentMeta.commit" :commit="savedContentMeta.commit"
:merge-request="savedContentMeta.mergeRequest" :merge-request="savedContentMeta.mergeRequest"
:return-url="returnUrl" :return-url="appData.returnUrl"
/> />
</div> </div>
</template> </template>
import Vuex from 'vuex'; import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import createState from '~/static_site_editor/store/state';
import { SUCCESS_ROUTE } from '~/static_site_editor/router/constants';
import Home from '~/static_site_editor/pages/home.vue'; import Home from '~/static_site_editor/pages/home.vue';
import SkeletonLoader from '~/static_site_editor/components/skeleton_loader.vue'; import SkeletonLoader from '~/static_site_editor/components/skeleton_loader.vue';
import EditArea from '~/static_site_editor/components/edit_area.vue'; import EditArea from '~/static_site_editor/components/edit_area.vue';
import InvalidContentMessage from '~/static_site_editor/components/invalid_content_message.vue'; import InvalidContentMessage from '~/static_site_editor/components/invalid_content_message.vue';
import SubmitChangesError from '~/static_site_editor/components/submit_changes_error.vue'; import SubmitChangesError from '~/static_site_editor/components/submit_changes_error.vue';
import submitContentChangesMutation from '~/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql';
import { SUCCESS_ROUTE } from '~/static_site_editor/router/constants';
import { import {
projectId as project,
returnUrl, returnUrl,
sourceContent as content, sourceContent as content,
sourceContentTitle as title, sourceContentTitle as title,
sourcePath,
username,
savedContentMeta,
submitChangesError, submitChangesError,
} from '../mock_data'; } from '../mock_data';
...@@ -24,32 +28,11 @@ describe('static_site_editor/pages/home', () => { ...@@ -24,32 +28,11 @@ describe('static_site_editor/pages/home', () => {
let store; let store;
let $apollo; let $apollo;
let $router; let $router;
let setContentActionMock; let mutateMock;
let submitChangesActionMock;
let dismissSubmitChangesErrorActionMock;
const buildStore = ({ initialState, getters } = {}) => {
setContentActionMock = jest.fn();
submitChangesActionMock = jest.fn();
dismissSubmitChangesErrorActionMock = jest.fn();
store = new Vuex.Store({
state: createState({
...initialState,
}),
getters: {
contentChanged: () => false,
...getters,
},
actions: {
setContent: setContentActionMock,
submitChanges: submitChangesActionMock,
dismissSubmitChangesError: dismissSubmitChangesErrorActionMock,
},
});
};
const buildApollo = (queries = {}) => { const buildApollo = (queries = {}) => {
mutateMock = jest.fn();
$apollo = { $apollo = {
queries: { queries: {
sourceContent: { sourceContent: {
...@@ -57,6 +40,7 @@ describe('static_site_editor/pages/home', () => { ...@@ -57,6 +40,7 @@ describe('static_site_editor/pages/home', () => {
}, },
...queries, ...queries,
}, },
mutate: mutateMock,
}; };
}; };
...@@ -76,7 +60,8 @@ describe('static_site_editor/pages/home', () => { ...@@ -76,7 +60,8 @@ describe('static_site_editor/pages/home', () => {
}, },
data() { data() {
return { return {
appData: { isSupportedContent: true, returnUrl }, appData: { isSupportedContent: true, returnUrl, project, username, sourcePath },
sourceContent: { title, content },
...data, ...data,
}; };
}, },
...@@ -91,7 +76,6 @@ describe('static_site_editor/pages/home', () => { ...@@ -91,7 +76,6 @@ describe('static_site_editor/pages/home', () => {
beforeEach(() => { beforeEach(() => {
buildApollo(); buildApollo();
buildRouter(); buildRouter();
buildStore();
}); });
afterEach(() => { afterEach(() => {
...@@ -102,8 +86,7 @@ describe('static_site_editor/pages/home', () => { ...@@ -102,8 +86,7 @@ describe('static_site_editor/pages/home', () => {
describe('when content is loaded', () => { describe('when content is loaded', () => {
beforeEach(() => { beforeEach(() => {
buildStore({ initialState: { isSavingChanges: true } }); buildWrapper();
buildWrapper({ sourceContent: { title, content } });
}); });
it('renders edit area', () => { it('renders edit area', () => {
...@@ -115,7 +98,7 @@ describe('static_site_editor/pages/home', () => { ...@@ -115,7 +98,7 @@ describe('static_site_editor/pages/home', () => {
title, title,
content, content,
returnUrl, returnUrl,
savingChanges: true, savingChanges: false,
}); });
}); });
}); });
...@@ -148,30 +131,44 @@ describe('static_site_editor/pages/home', () => { ...@@ -148,30 +131,44 @@ describe('static_site_editor/pages/home', () => {
expect(findSkeletonLoader().exists()).toBe(false); expect(findSkeletonLoader().exists()).toBe(false);
}); });
describe('when submitting changes fail', () => { it('displays invalid content message when content is not supported', () => {
buildWrapper({ appData: { isSupportedContent: false } });
expect(findInvalidContentMessage().exists()).toBe(true);
});
it('does not display invalid content message when content is not supported', () => {
buildWrapper({ appData: { isSupportedContent: true } });
expect(findInvalidContentMessage().exists()).toBe(false);
});
describe('when submitting changes fails', () => {
beforeEach(() => { beforeEach(() => {
buildStore({ mutateMock.mockRejectedValue(new Error(submitChangesError));
initialState: {
submitChangesError,
},
});
buildWrapper(); buildWrapper();
findEditArea().vm.$emit('submit', { content });
return wrapper.vm.$nextTick();
}); });
it('displays submit changes error message', () => { it('displays submit changes error message', () => {
expect(findSubmitChangesError().exists()).toBe(true); expect(findSubmitChangesError().exists()).toBe(true);
}); });
it('dispatches submitChanges action when error message emits retry event', () => { it('retries submitting changes when retry button is clicked', () => {
findSubmitChangesError().vm.$emit('retry'); findSubmitChangesError().vm.$emit('retry');
expect(submitChangesActionMock).toHaveBeenCalled(); expect(mutateMock).toHaveBeenCalled();
}); });
it('dispatches dismissSubmitChangesError action when error message emits dismiss event', () => { it('hides submit changes error message when dismiss button is clicked', () => {
findSubmitChangesError().vm.$emit('dismiss'); findSubmitChangesError().vm.$emit('dismiss');
expect(dismissSubmitChangesErrorActionMock).toHaveBeenCalled(); return wrapper.vm.$nextTick().then(() => {
expect(findSubmitChangesError().exists()).toBe(false);
});
}); });
}); });
...@@ -181,34 +178,32 @@ describe('static_site_editor/pages/home', () => { ...@@ -181,34 +178,32 @@ describe('static_site_editor/pages/home', () => {
expect(findSubmitChangesError().exists()).toBe(false); expect(findSubmitChangesError().exists()).toBe(false);
}); });
it('displays invalid content message when content is not supported', () => { describe('when submitting changes succeeds', () => {
buildWrapper({ appData: { isSupportedContent: false } });
expect(findInvalidContentMessage().exists()).toBe(true);
});
describe('when edit area emits submit event', () => {
const newContent = `new ${content}`; const newContent = `new ${content}`;
beforeEach(() => { beforeEach(() => {
submitChangesActionMock.mockResolvedValueOnce(); mutateMock.mockResolvedValueOnce({ data: { submitContentChanges: savedContentMeta } });
buildWrapper({ sourceContent: { title, content } }); buildWrapper();
findEditArea().vm.$emit('submit', { content: newContent }); findEditArea().vm.$emit('submit', { content: newContent });
});
it('dispatches setContent property', () => { return wrapper.vm.$nextTick();
expect(setContentActionMock).toHaveBeenCalledWith(expect.anything(), newContent, undefined);
}); });
it('dispatches submitChanges action', () => { it('dispatches submitContentChanges mutation', () => {
expect(submitChangesActionMock).toHaveBeenCalled(); expect(mutateMock).toHaveBeenCalledWith({
mutation: submitContentChangesMutation,
variables: {
content: newContent,
project,
sourcePath,
username,
},
});
}); });
it('pushes success route when submitting changes succeeds', () => { it('transitions to the SUCCESS route', () => {
return wrapper.vm.$nextTick().then(() => { expect($router.push).toHaveBeenCalledWith(SUCCESS_ROUTE);
expect($router.push).toHaveBeenCalledWith(SUCCESS_ROUTE);
});
}); });
}); });
}); });
import Vuex from 'vuex'; import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import createState from '~/static_site_editor/store/state';
import Success from '~/static_site_editor/pages/success.vue'; import Success from '~/static_site_editor/pages/success.vue';
import SavedChangesMessage from '~/static_site_editor/components/saved_changes_message.vue'; import SavedChangesMessage from '~/static_site_editor/components/saved_changes_message.vue';
import { savedContentMeta, returnUrl } from '../mock_data'; import { savedContentMeta, returnUrl } from '../mock_data';
...@@ -21,23 +20,22 @@ describe('static_site_editor/pages/success', () => { ...@@ -21,23 +20,22 @@ describe('static_site_editor/pages/success', () => {
}; };
}; };
const buildStore = (initialState = {}) => { const buildWrapper = (data = {}) => {
store = new Vuex.Store({
state: createState({
savedContentMeta,
returnUrl,
...initialState,
}),
});
};
const buildWrapper = () => {
wrapper = shallowMount(Success, { wrapper = shallowMount(Success, {
localVue, localVue,
store, store,
mocks: { mocks: {
$router: router, $router: router,
}, },
data() {
return {
savedContentMeta,
appData: {
returnUrl,
},
...data,
};
},
}); });
}; };
...@@ -45,7 +43,6 @@ describe('static_site_editor/pages/success', () => { ...@@ -45,7 +43,6 @@ describe('static_site_editor/pages/success', () => {
beforeEach(() => { beforeEach(() => {
buildRouter(); buildRouter();
buildStore();
}); });
afterEach(() => { afterEach(() => {
...@@ -74,8 +71,7 @@ describe('static_site_editor/pages/success', () => { ...@@ -74,8 +71,7 @@ describe('static_site_editor/pages/success', () => {
}); });
it('redirects to the HOME route when content has not been submitted', () => { it('redirects to the HOME route when content has not been submitted', () => {
buildStore({ savedContentMeta: null }); buildWrapper({ savedContentMeta: null });
buildWrapper();
expect(router.push).toHaveBeenCalledWith(HOME_ROUTE); expect(router.push).toHaveBeenCalledWith(HOME_ROUTE);
}); });
......
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