planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

View File

@@ -0,0 +1,274 @@
import gql from 'graphql-tag';
import { cloneDeep } from 'lodash';
import { shouldInclude, hasDirectives } from '../directives';
import { getQueryDefinition } from '../getFromAST';
describe('hasDirective', () => {
it('should allow searching the ast for a directive', () => {
const query = gql`
query Simple {
field @live
}
`;
expect(hasDirectives(['live'], query)).toBe(true);
expect(hasDirectives(['defer'], query)).toBe(false);
});
it('works for all operation types', () => {
const query = gql`
{
field @live {
subField {
hello @live
}
}
}
`;
const mutation = gql`
mutation Directive {
mutate {
field {
subField {
hello @live
}
}
}
}
`;
const subscription = gql`
subscription LiveDirective {
sub {
field {
subField {
hello @live
}
}
}
}
`;
[query, mutation, subscription].forEach(x => {
expect(hasDirectives(['live'], x)).toBe(true);
expect(hasDirectives(['defer'], x)).toBe(false);
});
});
it('works for simple fragments', () => {
const query = gql`
query Simple {
...fieldFragment
}
fragment fieldFragment on Field {
foo @live
}
`;
expect(hasDirectives(['live'], query)).toBe(true);
expect(hasDirectives(['defer'], query)).toBe(false);
});
it('works for nested fragments', () => {
const query = gql`
query Simple {
...fieldFragment1
}
fragment fieldFragment1 on Field {
bar {
baz {
...nestedFragment
}
}
}
fragment nestedFragment on Field {
foo @live
}
`;
expect(hasDirectives(['live'], query)).toBe(true);
expect(hasDirectives(['defer'], query)).toBe(false);
});
});
describe('shouldInclude', () => {
it('should should not include a skipped field', () => {
const query = gql`
query {
fortuneCookie @skip(if: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, {})).toBe(true);
});
it('should include an included field', () => {
const query = gql`
query {
fortuneCookie @include(if: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(shouldInclude(field, {})).toBe(true);
});
it('should not include a not include: false field', () => {
const query = gql`
query {
fortuneCookie @include(if: false)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, {})).toBe(true);
});
it('should include a skip: false field', () => {
const query = gql`
query {
fortuneCookie @skip(if: false)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(shouldInclude(field, {})).toBe(true);
});
it('should not include a field if skip: true and include: true', () => {
const query = gql`
query {
fortuneCookie @skip(if: true) @include(if: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, {})).toBe(true);
});
it('should not include a field if skip: true and include: false', () => {
const query = gql`
query {
fortuneCookie @skip(if: true) @include(if: false)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, {})).toBe(true);
});
it('should include a field if skip: false and include: true', () => {
const query = gql`
query {
fortuneCookie @skip(if: false) @include(if: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(shouldInclude(field, {})).toBe(true);
});
it('should not include a field if skip: false and include: false', () => {
const query = gql`
query {
fortuneCookie @skip(if: false) @include(if: false)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, {})).toBe(true);
});
it('should leave the original query unmodified', () => {
const query = gql`
query {
fortuneCookie @skip(if: false) @include(if: false)
}
`;
const queryClone = cloneDeep(query);
const field = getQueryDefinition(query).selectionSet.selections[0];
shouldInclude(field, {});
expect(query).toEqual(queryClone);
});
it('does not throw an error on an unsupported directive', () => {
const query = gql`
query {
fortuneCookie @dosomething(if: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(() => {
shouldInclude(field, {});
}).not.toThrow();
});
it('throws an error on an invalid argument for the skip directive', () => {
const query = gql`
query {
fortuneCookie @skip(nothing: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(() => {
shouldInclude(field, {});
}).toThrow();
});
it('throws an error on an invalid argument for the include directive', () => {
const query = gql`
query {
fortuneCookie @include(nothing: true)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(() => {
shouldInclude(field, {});
}).toThrow();
});
it('throws an error on an invalid variable name within a directive argument', () => {
const query = gql`
query {
fortuneCookie @include(if: $neverDefined)
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(() => {
shouldInclude(field, {});
}).toThrow();
});
it('evaluates variables on skip fields', () => {
const query = gql`
query($shouldSkip: Boolean) {
fortuneCookie @skip(if: $shouldSkip)
}
`;
const variables = {
shouldSkip: true,
};
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, variables)).toBe(true);
});
it('evaluates variables on include fields', () => {
const query = gql`
query($shouldSkip: Boolean) {
fortuneCookie @include(if: $shouldInclude)
}
`;
const variables = {
shouldInclude: false,
};
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(!shouldInclude(field, variables)).toBe(true);
});
it('throws an error if the value of the argument is not a variable or boolean', () => {
const query = gql`
query {
fortuneCookie @include(if: "string")
}
`;
const field = getQueryDefinition(query).selectionSet.selections[0];
expect(() => {
shouldInclude(field, {});
}).toThrow();
});
});

View File

@@ -0,0 +1,327 @@
import { print } from 'graphql/language/printer';
import gql from 'graphql-tag';
import { disableFragmentWarnings } from 'graphql-tag';
// Turn off warnings for repeated fragment names
disableFragmentWarnings();
import { getFragmentQueryDocument } from '../fragments';
describe('getFragmentQueryDocument', () => {
it('will throw an error if there is an operation', () => {
expect(() =>
getFragmentQueryDocument(
gql`
{
a
b
c
}
`,
),
).toThrowError(
'Found a query operation. No operations are allowed when using a fragment as a query. Only fragments are allowed.',
);
expect(() =>
getFragmentQueryDocument(
gql`
query {
a
b
c
}
`,
),
).toThrowError(
'Found a query operation. No operations are allowed when using a fragment as a query. Only fragments are allowed.',
);
expect(() =>
getFragmentQueryDocument(
gql`
query Named {
a
b
c
}
`,
),
).toThrowError(
"Found a query operation named 'Named'. No operations are allowed when using a fragment as a query. Only fragments are allowed.",
);
expect(() =>
getFragmentQueryDocument(
gql`
mutation Named {
a
b
c
}
`,
),
).toThrowError(
"Found a mutation operation named 'Named'. No operations are allowed when using a fragment as a query. " +
'Only fragments are allowed.',
);
expect(() =>
getFragmentQueryDocument(
gql`
subscription Named {
a
b
c
}
`,
),
).toThrowError(
"Found a subscription operation named 'Named'. No operations are allowed when using a fragment as a query. " +
'Only fragments are allowed.',
);
});
it('will throw an error if there is not exactly one fragment but no `fragmentName`', () => {
expect(() => {
getFragmentQueryDocument(gql`
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
}
`);
}).toThrowError(
'Found 2 fragments. `fragmentName` must be provided when there is not exactly 1 fragment.',
);
expect(() => {
getFragmentQueryDocument(gql`
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
}
fragment baz on Baz {
g
h
i
}
`);
}).toThrowError(
'Found 3 fragments. `fragmentName` must be provided when there is not exactly 1 fragment.',
);
expect(() => {
getFragmentQueryDocument(gql`
scalar Foo
`);
}).toThrowError(
'Found 0 fragments. `fragmentName` must be provided when there is not exactly 1 fragment.',
);
});
it('will create a query document where the single fragment is spread in the root query', () => {
expect(
print(
getFragmentQueryDocument(gql`
fragment foo on Foo {
a
b
c
}
`),
),
).toEqual(
print(gql`
{
...foo
}
fragment foo on Foo {
a
b
c
}
`),
);
});
it('will create a query document where the named fragment is spread in the root query', () => {
expect(
print(
getFragmentQueryDocument(
gql`
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
...foo
}
fragment baz on Baz {
g
h
i
...foo
...bar
}
`,
'foo',
),
),
).toEqual(
print(gql`
{
...foo
}
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
...foo
}
fragment baz on Baz {
g
h
i
...foo
...bar
}
`),
);
expect(
print(
getFragmentQueryDocument(
gql`
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
...foo
}
fragment baz on Baz {
g
h
i
...foo
...bar
}
`,
'bar',
),
),
).toEqual(
print(gql`
{
...bar
}
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
...foo
}
fragment baz on Baz {
g
h
i
...foo
...bar
}
`),
);
expect(
print(
getFragmentQueryDocument(
gql`
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
...foo
}
fragment baz on Baz {
g
h
i
...foo
...bar
}
`,
'baz',
),
),
).toEqual(
print(gql`
{
...baz
}
fragment foo on Foo {
a
b
c
}
fragment bar on Bar {
d
e
f
...foo
}
fragment baz on Baz {
g
h
i
...foo
...bar
}
`),
);
});
});

View File

@@ -0,0 +1,316 @@
import { print } from 'graphql/language/printer';
import gql from 'graphql-tag';
import { FragmentDefinitionNode, OperationDefinitionNode } from 'graphql';
import {
checkDocument,
getFragmentDefinitions,
getQueryDefinition,
getMutationDefinition,
createFragmentMap,
FragmentMap,
getDefaultValues,
getOperationName,
} from '../getFromAST';
describe('AST utility functions', () => {
it('should correctly check a document for correctness', () => {
const multipleQueries = gql`
query {
author {
firstName
lastName
}
}
query {
author {
address
}
}
`;
expect(() => {
checkDocument(multipleQueries);
}).toThrow();
const namedFragment = gql`
query {
author {
...authorDetails
}
}
fragment authorDetails on Author {
firstName
lastName
}
`;
expect(() => {
checkDocument(namedFragment);
}).not.toThrow();
});
it('should get fragment definitions from a document containing a single fragment', () => {
const singleFragmentDefinition = gql`
query {
author {
...authorDetails
}
}
fragment authorDetails on Author {
firstName
lastName
}
`;
const expectedDoc = gql`
fragment authorDetails on Author {
firstName
lastName
}
`;
const expectedResult: FragmentDefinitionNode[] = [
expectedDoc.definitions[0] as FragmentDefinitionNode,
];
const actualResult = getFragmentDefinitions(singleFragmentDefinition);
expect(actualResult.length).toEqual(expectedResult.length);
expect(print(actualResult[0])).toBe(print(expectedResult[0]));
});
it('should get fragment definitions from a document containing a multiple fragments', () => {
const multipleFragmentDefinitions = gql`
query {
author {
...authorDetails
...moreAuthorDetails
}
}
fragment authorDetails on Author {
firstName
lastName
}
fragment moreAuthorDetails on Author {
address
}
`;
const expectedDoc = gql`
fragment authorDetails on Author {
firstName
lastName
}
fragment moreAuthorDetails on Author {
address
}
`;
const expectedResult: FragmentDefinitionNode[] = [
expectedDoc.definitions[0] as FragmentDefinitionNode,
expectedDoc.definitions[1] as FragmentDefinitionNode,
];
const actualResult = getFragmentDefinitions(multipleFragmentDefinitions);
expect(actualResult.map(print)).toEqual(expectedResult.map(print));
});
it('should get the correct query definition out of a query containing multiple fragments', () => {
const queryWithFragments = gql`
fragment authorDetails on Author {
firstName
lastName
}
fragment moreAuthorDetails on Author {
address
}
query {
author {
...authorDetails
...moreAuthorDetails
}
}
`;
const expectedDoc = gql`
query {
author {
...authorDetails
...moreAuthorDetails
}
}
`;
const expectedResult: OperationDefinitionNode = expectedDoc
.definitions[0] as OperationDefinitionNode;
const actualResult = getQueryDefinition(queryWithFragments);
expect(print(actualResult)).toEqual(print(expectedResult));
});
it('should throw if we try to get the query definition of a document with no query', () => {
const mutationWithFragments = gql`
fragment authorDetails on Author {
firstName
lastName
}
mutation {
createAuthor(firstName: "John", lastName: "Smith") {
...authorDetails
}
}
`;
expect(() => {
getQueryDefinition(mutationWithFragments);
}).toThrow();
});
it('should get the correct mutation definition out of a mutation with multiple fragments', () => {
const mutationWithFragments = gql`
mutation {
createAuthor(firstName: "John", lastName: "Smith") {
...authorDetails
}
}
fragment authorDetails on Author {
firstName
lastName
}
`;
const expectedDoc = gql`
mutation {
createAuthor(firstName: "John", lastName: "Smith") {
...authorDetails
}
}
`;
const expectedResult: OperationDefinitionNode = expectedDoc
.definitions[0] as OperationDefinitionNode;
const actualResult = getMutationDefinition(mutationWithFragments);
expect(print(actualResult)).toEqual(print(expectedResult));
});
it('should create the fragment map correctly', () => {
const fragments = getFragmentDefinitions(gql`
fragment authorDetails on Author {
firstName
lastName
}
fragment moreAuthorDetails on Author {
address
}
`);
const fragmentMap = createFragmentMap(fragments);
const expectedTable: FragmentMap = {
authorDetails: fragments[0],
moreAuthorDetails: fragments[1],
};
expect(fragmentMap).toEqual(expectedTable);
});
it('should return an empty fragment map if passed undefined argument', () => {
expect(createFragmentMap(undefined)).toEqual({});
});
it('should get the operation name out of a query', () => {
const query = gql`
query nameOfQuery {
fortuneCookie
}
`;
const operationName = getOperationName(query);
expect(operationName).toEqual('nameOfQuery');
});
it('should get the operation name out of a mutation', () => {
const query = gql`
mutation nameOfMutation {
fortuneCookie
}
`;
const operationName = getOperationName(query);
expect(operationName).toEqual('nameOfMutation');
});
it('should return null if the query does not have an operation name', () => {
const query = gql`
{
fortuneCookie
}
`;
const operationName = getOperationName(query);
expect(operationName).toEqual(null);
});
it('should throw if type definitions found in document', () => {
const queryWithTypeDefination = gql`
fragment authorDetails on Author {
firstName
lastName
}
query($search: AuthorSearchInputType) {
author(search: $search) {
...authorDetails
}
}
input AuthorSearchInputType {
firstName: String
}
`;
expect(() => {
getQueryDefinition(queryWithTypeDefination);
}).toThrowError(
'Schema type definitions not allowed in queries. Found: "InputObjectTypeDefinition"',
);
});
describe('getDefaultValues', () => {
it('will create an empty variable object if no default values are provided', () => {
const basicQuery = gql`
query people($first: Int, $second: String) {
allPeople(first: $first) {
people {
name
}
}
}
`;
expect(getDefaultValues(getQueryDefinition(basicQuery))).toEqual({});
});
it('will create a variable object based on the definition node with default values', () => {
const basicQuery = gql`
query people($first: Int = 1, $second: String!) {
allPeople(first: $first) {
people {
name
}
}
}
`;
const complexMutation = gql`
mutation complexStuff(
$test: Input = { key1: ["value", "value2"], key2: { key3: 4 } }
) {
complexStuff(test: $test) {
people {
name
}
}
}
`;
expect(getDefaultValues(getQueryDefinition(basicQuery))).toEqual({
first: 1,
});
expect(getDefaultValues(getMutationDefinition(complexMutation))).toEqual({
test: { key1: ['value', 'value2'], key2: { key3: 4 } },
});
});
});
});

View File

@@ -0,0 +1,23 @@
import { getStoreKeyName } from '../storeUtils';
describe('getStoreKeyName', () => {
it(
'should return a deterministic version of the store key name no matter ' +
'which order the args object properties are in',
() => {
const validStoreKeyName =
'someField({"prop1":"value1","prop2":"value2"})';
let generatedStoreKeyName = getStoreKeyName('someField', {
prop1: 'value1',
prop2: 'value2',
});
expect(generatedStoreKeyName).toEqual(validStoreKeyName);
generatedStoreKeyName = getStoreKeyName('someField', {
prop2: 'value2',
prop1: 'value1',
});
expect(generatedStoreKeyName).toEqual(validStoreKeyName);
},
);
});

1242
node_modules/apollo-utilities/src/__tests__/transform.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff