Skip to content

Commit

Permalink
Add more tests of possibleTypes and fragmentMatches.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Sep 10, 2020
1 parent fcbc86f commit ae4a6f5
Show file tree
Hide file tree
Showing 2 changed files with 367 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/cache/inmemory/__tests__/__snapshots__/fragmentMatcher.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`policies.fragmentMatches can infer fuzzy subtypes heuristically 1`] = `
Object {
"ROOT_QUERY": Object {
"__typename": "Query",
"objects": Array [
Object {
"__typename": "E",
"c": "ce",
},
Object {
"__typename": "F",
"c": "cf",
},
Object {
"__typename": "G",
"c": "cg",
},
Object {
"__typename": "TooLong",
},
Object {
"__typename": "H",
},
],
},
}
`;
338 changes: 338 additions & 0 deletions src/cache/inmemory/__tests__/fragmentMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import gql from 'graphql-tag';

import { InMemoryCache } from '../inMemoryCache';
import { visit, FragmentDefinitionNode } from 'graphql';
import { hasOwn } from '../helpers';

describe('fragment matching', () => {
it('can match exact types with or without possibleTypes', () => {
Expand Down Expand Up @@ -222,4 +224,340 @@ describe('fragment matching', () => {
cache.writeQuery({ query, data });
expect(cache.readQuery({ query })).toEqual(data);
});

});

describe("policies.fragmentMatches", () => {
const warnings: any[] = [];
const { warn } = console;

beforeEach(() => {
warnings.length = 0;
console.warn = function (message: any) {
warnings.push(message);
};
});

afterEach(() => {
console.warn = warn;
});

it("can infer fuzzy subtypes heuristically", () => {
const cache = new InMemoryCache({
possibleTypes: {
A: ["B", "C"],
B: ["D"],
C: ["[E-Z]"],
},
});

const fragments = gql`
fragment FragA on A { a }
fragment FragB on B { b }
fragment FragC on C { c }
fragment FragD on D { d }
fragment FragE on E { e }
fragment FragF on F { f }
`;

function checkTypes(
expected: Record<string, Record<string, boolean>>,
) {
const checked = new Set<FragmentDefinitionNode>();

visit(fragments, {
FragmentDefinition(frag) {
function check(typename: string, result: boolean) {
if (result !== cache.policies.fragmentMatches(frag, typename)) {
fail(`fragment ${
frag.name.value
} should${result ? "" : " not"} have matched typename ${typename}`);
}
}

const supertype = frag.typeCondition.name.value;
expect("ABCDEF".split("")).toContain(supertype);

if (hasOwn.call(expected, supertype)) {
Object.keys(expected[supertype]).forEach(subtype => {
check(subtype, expected[supertype][subtype]);
});

checked.add(frag);
}
},
});

return checked;
}

expect(checkTypes({
A: {
A: true,
B: true,
C: true,
D: true,
E: false,
F: false,
G: false,
},
B: {
A: false,
B: true,
C: false,
D: true,
E: false,
F: false,
G: false,
},
C: {
A: false,
B: false,
C: true,
D: false,
E: false,
F: false,
G: false,
},
D: {
A: false,
B: false,
C: false,
D: true,
E: false,
F: false,
G: false,
},
E: {
A: false,
B: false,
C: false,
D: false,
E: true,
F: false,
G: false,
},
F: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: true,
G: false,
},
G: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: false,
G: true,
},
}).size).toBe("ABCDEF".length);

cache.writeQuery({
query: gql`
query {
objects {
...FragC
}
}
${fragments}
`,
data: {
objects: [
{ __typename: "E", c: "ce" },
{ __typename: "F", c: "cf" },
{ __typename: "G", c: "cg" },
// The /[E-Z]/ subtype pattern specified for the C supertype
// must match the entire subtype string.
{ __typename: "TooLong", c: "nope" },
// The H typename matches the regular expression for C, but it
// does not pass the heuristic test of having all the fields
// expected if FragC matched.
{ __typename: "H", h: "not c" },
],
},
});

expect(warnings).toEqual([
"Inferring subtype E of supertype C",
"Inferring subtype F of supertype C",
"Inferring subtype G of supertype C",
// Note that TooLong is not inferred here.
]);

expect(checkTypes({
A: {
A: true,
B: true,
C: true,
D: true,
E: true,
F: true,
G: true,
H: false,
},
B: {
A: false,
B: true,
C: false,
D: true,
E: false,
F: false,
G: false,
H: false,
},
C: {
A: false,
B: false,
C: true,
D: false,
E: true,
F: true,
G: true,
H: false,
},
D: {
A: false,
B: false,
C: false,
D: true,
E: false,
F: false,
G: false,
H: false,
},
E: {
A: false,
B: false,
C: false,
D: false,
E: true,
F: false,
G: false,
H: false,
},
F: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: true,
G: false,
H: false,
},
G: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: true,
G: true,
H: false,
},
}).size).toBe("ABCDEF".length);

expect(cache.extract()).toMatchSnapshot();

// Now add the TooLong subtype of C explicitly.
cache.policies.addPossibleTypes({
C: ["TooLong"],
});

expect(checkTypes({
A: {
A: true,
B: true,
C: true,
D: true,
E: true,
F: true,
G: true,
TooLong: true,
H: false,
},
B: {
A: false,
B: true,
C: false,
D: true,
E: false,
F: false,
G: false,
TooLong: false,
H: false,
},
C: {
A: false,
B: false,
C: true,
D: false,
E: true,
F: true,
G: true,
TooLong: true,
H: false,
},
D: {
A: false,
B: false,
C: false,
D: true,
E: false,
F: false,
G: false,
TooLong: false,
H: false,
},
E: {
A: false,
B: false,
C: false,
D: false,
E: true,
F: false,
G: false,
TooLong: false,
H: false,
},
F: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: true,
G: false,
TooLong: false,
H: false,
},
G: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: true,
G: true,
TooLong: false,
H: false,
},
H: {
A: false,
B: false,
C: false,
D: false,
E: false,
F: false,
G: false,
TooLong: false,
H: true,
},
}).size).toBe("ABCDEF".length);
});
});

0 comments on commit ae4a6f5

Please sign in to comment.