summaryrefslogtreecommitdiff
path: root/doc/development/fe_guide/graphql.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/fe_guide/graphql.md')
-rw-r--r--doc/development/fe_guide/graphql.md157
1 files changed, 148 insertions, 9 deletions
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index ad3958d4496..cae2435e4ff 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -664,7 +664,9 @@ it('calls mutation on submitting form ', () => {
### Testing with mocked Apollo Client
-To test the logic of Apollo cache updates, we might want to mock an Apollo Client in our unit tests. To separate tests with mocked client from 'usual' unit tests, it's recommended to create an additional component factory. This way we only create Apollo Client instance when it's necessary:
+To test the logic of Apollo cache updates, we might want to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/helpers/mock_apollo_helper.js) we created on top of it.
+
+To separate tests with mocked client from 'usual' unit tests, it's recommended to create an additional component factory. This way we only create Apollo Client instance when it's necessary:
```javascript
function createComponent() {...}
@@ -672,12 +674,6 @@ function createComponent() {...}
function createComponentWithApollo() {...}
```
-We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client in tests.
-
-```javascript
-import { createMockClient } from 'mock-apollo-client';
-```
-
Then we need to inject `VueApollo` to Vue local instance (`localVue.use()` can also be called within `createComponentWithApollo()`)
```javascript
@@ -741,8 +737,8 @@ describe('Some component with Apollo mock', () => {
})
```
-NOTE: **Note:**
-When mocking resolved values, make sure the structure of the response is the same as actual API response: i.e. root property should be `data` for example
+When mocking resolved values, ensure the structure of the response is the same
+as the actual API response. For example, root property should be `data`.
When testing queries, please keep in mind they are promises, so they need to be _resolved_ to render a result. Without resolving, we can check the `loading` state of the query:
@@ -830,6 +826,53 @@ it('calls a mutation with correct parameters and reorders designs', async () =>
});
```
+#### Testing `@client` queries
+
+If your application contains `@client` queries, most probably you will have an Apollo Client warning saying that you have a local query but no resolvers are defined. In order to fix it, you need to pass resolvers to the mocked client with a second parameter (bare minimum is an empty object):
+
+```javascript
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+...
+fakeApollo = createMockApollo(requestHandlers, {});
+```
+
+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:
+
+```javascript
+query fetchLocalUser {
+ fetchLocalUser @client {
+ name
+ }
+}
+```
+
+```javascript
+import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
+
+function createComponentWithApollo() {
+ const requestHandlers = [
+ [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
+ [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
+ ];
+
+ fakeApollo = createMockApollo(requestHandlers, {});
+ fakeApollo.clients.defaultClient.cache.writeQuery({
+ query: fetchLocalUserQuery,
+ data: {
+ fetchLocalUser: {
+ __typename: 'User',
+ name: 'Test',
+ },
+ },
+ })
+
+ wrapper = shallowMount(Index, {
+ localVue,
+ apolloProvider: fakeApollo,
+ });
+}
+```
+
## Handling errors
GitLab's GraphQL mutations currently have two distinct error modes: [Top-level](#top-level-errors) and [errors-as-data](#errors-as-data).
@@ -910,3 +953,99 @@ const defaultClient = createDefaultClient(
},
);
```
+
+## Making initial queries early with GraphQL startup calls
+
+To improve performance, sometimes we want to make initial GraphQL queries early. In order to do this, we can add them to **startup calls** with the following steps:
+
+- Move all the queries you need initially in your application to `app/graphql/queries`;
+- Add `__typename` property to every nested query level:
+
+ ```javascript
+ query getPermissions($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ __typename
+ userPermissions {
+ __typename
+ pushCode
+ forkProject
+ createMergeRequestIn
+ }
+ }
+ }
+ ```
+
+- If queries contain fragments, you need to move fragments to the query file directly instead of importing them:
+
+ ```javascript
+ fragment PageInfo on PageInfo {
+ __typename
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+ }
+
+ query getFiles(
+ $projectPath: ID!
+ $path: String
+ $ref: String!
+ ) {
+ project(fullPath: $projectPath) {
+ __typename
+ repository {
+ __typename
+ tree(path: $path, ref: $ref) {
+ __typename
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ }
+ }
+ }
+ }
+ ```
+
+- If the fragment is used only once, we can also remove the fragment altogether:
+
+ ```javascript
+ query getFiles(
+ $projectPath: ID!
+ $path: String
+ $ref: String!
+ ) {
+ project(fullPath: $projectPath) {
+ __typename
+ repository {
+ __typename
+ tree(path: $path, ref: $ref) {
+ __typename
+ pageInfo {
+ __typename
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+ }
+ }
+ }
+ }
+ }
+ }
+ ```
+
+- Add startup call(s) with correct variables to the HAML file that serves as a view
+for your application. To add GraphQL startup calls, we use
+`add_page_startup_graphql_call` helper where the first parameter is a path to the
+query, the second one is an object containing query variables. Path to the query is
+relative to `app/graphql/queries` folder: for example, if we need a
+`app/graphql/queries/repository/files.query.graphql` query, the path will be
+`repository/files`.
+
+ ```yaml
+ - current_route_path = request.fullpath.match(/-\/tree\/[^\/]+\/(.+$)/).to_a[1]
+ - add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path || "" })
+ - add_page_startup_graphql_call('repository/permissions', { projectPath: @project.full_path })
+ - add_page_startup_graphql_call('repository/files', { nextPageCursor: "", pageSize: 100, projectPath: @project.full_path, ref: current_ref, path: current_route_path || "/"})
+ ```