Commit 702c4a12 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '282440-mlunoe-improve-graphql-local-resolver-tests' into 'master'

Test(DevOps Adoption): `groups` local resolver

See merge request gitlab-org/gitlab!47793
parents 581efbce 251bfd72
......@@ -833,7 +833,7 @@ If your application contains `@client` queries, most probably you will have an A
```javascript
import createMockApollo from 'jest/helpers/mock_apollo_helper';
...
fakeApollo = createMockApollo(requestHandlers, {});
mockApollo = createMockApollo(requestHandlers, resolvers);
```
Sometimes we want to test a `result` hook of the local query. In order to have it triggered, we need to populate a cache with correct data to be fetched with this query:
......@@ -849,14 +849,14 @@ query fetchLocalUser {
```javascript
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
function createComponentWithApollo() {
function createMockApolloProvider() {
const requestHandlers = [
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
];
fakeApollo = createMockApollo(requestHandlers, {});
fakeApollo.clients.defaultClient.cache.writeQuery({
mockApollo = createMockApollo(requestHandlers, {});
mockApollo.clients.defaultClient.cache.writeQuery({
query: fetchLocalUserQuery,
data: {
fetchLocalUser: {
......@@ -864,15 +864,107 @@ function createComponentWithApollo() {
name: 'Test',
},
},
})
});
wrapper = shallowMount(Index, {
return mockApollo;
}
function createComponent(options = {}) {
const { mockApollo } = options;
return shallowMount(Index, {
localVue,
apolloProvider: fakeApollo,
apolloProvider: mockApollo,
});
}
```
Sometimes it is necessary to control what the local resolver returns and inspect how it is called by the component. This can be done by mocking your local resolver:
```javascript
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
function createMockApolloProvider(options = {}) {
const { fetchLocalUserSpy } = options;
mockApollo = createMockApollo([], {
Query: {
fetchLocalUser: fetchLocalUserSpy,
},
});
// Necessary for local resolvers to be activated
mockApollo.clients.defaultClient.cache.writeQuery({
query: fetchLocalUserQuery,
data: {},
});
return mockApollo;
}
```
In the test you can then control what the spy is supposed to do and inspect the component after the request have returned:
```javascript
describe('My Index test with `createMockApollo`', () => {
let wrapper;
let fetchLocalUserSpy;
afterEach(() => {
wrapper.destroy();
wrapper = null;
fetchLocalUserSpy = null;
});
describe('when loading', () => {
beforeEach(() => {
const mockApollo = createMockApolloProvider();
wrapper = createComponent({ mockApollo });
});
it('displays the loader', () => {
// Assess that the loader is present
});
});
describe('with data', () => {
beforeEach(async () => {
fetchLocalUserSpy = jest.fn().mockResolvedValue(localUserQueryResponse);
const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
wrapper = createComponent({ mockApollo });
await waitForPromises();
});
it('should fetch data once', () => {
expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
});
it('displays data', () => {
// Assess that data is present
});
});
describe('with error', () => {
const error = 'Error!';
beforeEach(async () => {
fetchLocalUserSpy = jest.fn().mockRejectedValueOnce(error);
const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
wrapper = createComponent({ mockApollo });
await waitForPromises();
});
it('should fetch data once', () => {
expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
});
it('displays the error', () => {
// Assess that the error is displayed
});
});
});
```
## Handling errors
GitLab's GraphQL mutations currently have two distinct error modes: [Top-level](#top-level-errors) and [errors-as-data](#errors-as-data).
......
......@@ -3,7 +3,7 @@ import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import * as Sentry from '~/sentry/wrapper';
import getGroupsQuery from '../graphql/queries/get_groups.query.graphql';
import DevopsAdoptionEmptyState from './devops_adoption_empty_state.vue';
import { DEVOPS_ADOPTION_STRINGS } from '../constants';
import { DEVOPS_ADOPTION_STRINGS, MAX_REQUEST_COUNT } from '../constants';
export default {
name: 'DevopsAdoptionApp',
......@@ -17,6 +17,7 @@ export default {
},
data() {
return {
requestCount: MAX_REQUEST_COUNT,
loadingError: false,
};
},
......@@ -25,7 +26,9 @@ export default {
query: getGroupsQuery,
loadingKey: 'loading',
result() {
if (this.groups?.pageInfo?.nextPage) {
this.requestCount -= 1;
if (this.requestCount > 0 && this.groups?.pageInfo?.nextPage) {
this.fetchNextPage();
}
},
......@@ -55,7 +58,8 @@ export default {
},
updateQuery: (previousResult, { fetchMoreResult }) => {
const { nodes, ...rest } = fetchMoreResult.groups;
const previousNodes = previousResult.groups.nodes;
const { nodes: previousNodes } = previousResult.groups;
return { groups: { ...rest, nodes: [...previousNodes, ...nodes] } };
},
})
......@@ -65,9 +69,9 @@ export default {
};
</script>
<template>
<gl-loading-icon v-if="isLoading" size="md" class="gl-my-5" />
<gl-alert v-else-if="loadingError" variant="danger" :dismissible="false" class="gl-mt-3">
<gl-alert v-if="loadingError" variant="danger" :dismissible="false" class="gl-mt-3">
{{ $options.i18n.groupsError }}
</gl-alert>
<gl-loading-icon v-else-if="isLoading" size="md" class="gl-my-5" />
<devops-adoption-empty-state v-else-if="isEmpty" />
</template>
import { s__ } from '~/locale';
export const MAX_REQUEST_COUNT = 10;
export const DEVOPS_ADOPTION_STRINGS = {
app: {
groupsError: s__('DevopsAdoption|There was an error fetching Groups'),
......
......@@ -17,6 +17,12 @@ export const groupNodes = [
},
];
export const nextGroupNode = {
__typename: 'Group',
full_name: 'Baz',
id: 'baz',
};
export const groupPageInfo = {
nextPage: 2,
};
......
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