Skip to content

Commit

Permalink
Test error thrown when FragmentRegistry cannot resolve all fragments.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Sep 12, 2022
1 parent 3ffb5eb commit 2f1c2fc
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 8 deletions.
104 changes: 104 additions & 0 deletions src/cache/inmemory/__tests__/fragmentRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,110 @@ describe("FragmentRegistry", () => {
});
});

it("throws an error when not all used fragments are defined", () => {
const cache = new InMemoryCache({
fragments: createFragmentRegistry(gql`
fragment IncompleteFragment on Person {
__typename
id
...MustBeDefinedByQuery
}
`),
});

const queryWithoutFragment = gql`
query WithoutFragment {
me {
...IncompleteFragment
}
}
`;

const queryWithFragment = gql`
query WithFragment {
me {
...IncompleteFragment
}
}
fragment MustBeDefinedByQuery on Person {
name
}
`;

expect(() => {
cache.writeQuery({
query: queryWithoutFragment,
data: {
me: {
__typename: "Person",
id: 12345,
name: "Ben",
},
},
});
}).toThrow(
/No fragment named MustBeDefinedByQuery/
);

expect(cache.extract()).toEqual({
// Nothing written because the cache.writeQuery failed above.
});

cache.writeQuery({
query: queryWithFragment,
data: {
me: {
__typename: "Person",
id: 12345,
name: "Ben Newman",
},
},
});

expect(cache.extract()).toEqual({
ROOT_QUERY: {
__typename: "Query",
me: { __ref: "Person:12345" },
},
"Person:12345": {
__typename: "Person",
id: 12345,
name: "Ben Newman",
},
});

expect(() => {
cache.diff({
query: queryWithoutFragment,
returnPartialData: true,
optimistic: true,
});
}).toThrow(
/No fragment named MustBeDefinedByQuery/
);

expect(() => {
cache.readQuery({
query: queryWithoutFragment
});
}).toThrow(
/No fragment named MustBeDefinedByQuery/
);

expect(
cache.readQuery({
query: queryWithFragment,
}),
).toEqual({
me: {
__typename: "Person",
id: 12345,
name: "Ben Newman",
},
});
});

it("can register fragments with unbound ...spreads", () => {
const cache = new InMemoryCache({
fragments: createFragmentRegistry(gql`
Expand Down
2 changes: 0 additions & 2 deletions src/cache/inmemory/fragmentRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,6 @@ class FragmentRegistry implements FragmentRegistryAPI {
const def = map[name];
if (def) {
defsToAppend.push(def);
} else {
// TODO Warn? Error?
}
});

Expand Down
10 changes: 5 additions & 5 deletions src/cache/inmemory/inMemoryCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ export class InMemoryCache extends ApolloCache<NormalizedCacheObject> {
}).result || null;
} catch (e) {
if (e instanceof MissingFieldError) {
// Swallow MissingFieldError and return null, so callers do not
// need to worry about catching "normal" exceptions resulting from
// incomplete cache data. Unexpected errors will be re-thrown. If
// you need more information about which fields were missing, use
// cache.diff instead, and examine diffResult.missing.
// Swallow MissingFieldError and return null, so callers do not need to
// worry about catching "normal" exceptions resulting from incomplete
// cache data. Unexpected errors will be re-thrown. If you need more
// information about which fields were missing, use cache.diff instead,
// and examine diffResult.missing.
return null;
}
throw e;
Expand Down
7 changes: 6 additions & 1 deletion src/cache/inmemory/readFromStore.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { invariant } from '../../utilities/globals';
import { invariant, InvariantError } from '../../utilities/globals';

import {
DocumentNode,
FieldNode,
Kind,
SelectionSetNode,
} from 'graphql';
import { wrap, OptimisticWrapperFunction } from 'optimism';
Expand Down Expand Up @@ -406,6 +407,10 @@ export class StoreReader {
context.lookupFragment,
);

if (!fragment && selection.kind === Kind.FRAGMENT_SPREAD) {
throw new InvariantError(`No fragment named ${selection.name.value}`);
}

if (fragment && policies.fragmentMatches(fragment, typename)) {
fragment.selectionSet.selections.forEach(workSet.add, workSet);
}
Expand Down
5 changes: 5 additions & 0 deletions src/cache/inmemory/writeToStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Trie } from '@wry/trie';
import {
SelectionSetNode,
FieldNode,
Kind,
} from 'graphql';

import {
Expand Down Expand Up @@ -566,6 +567,10 @@ export class StoreWriter {
context.lookupFragment,
);

if (!fragment && selection.kind === Kind.FRAGMENT_SPREAD) {
throw new InvariantError(`No fragment named ${selection.name.value}`);
}

if (fragment &&
policies.fragmentMatches(
fragment, typename, result, context.variables)) {
Expand Down

0 comments on commit 2f1c2fc

Please sign in to comment.