graphql / Apollo test case fails Error: Expected one matching operation for criteria "Match DocumentNode", found none - jasmine

I am trying to write a test for an angular service which I am using with graphql & Apollo.
I'm receiving these errors: Error: Expected one matching operation for criteria "Match DocumentNode", found none.
and
Error: Expected no open operations, found 1:
foo.spec.ts
import { TestBed } from '#angular/core/testing';
import {
....
const fragment: DocumentNode = gql`
fragment SummaryDetails on SummaryDetails {
foo
...
}
`;
const summaryQuery: DocumentNode = gql`
query Summary(
) {
Summary(
) {
...SummaryDetails
}
}
${fragment}
`;
const fundDataServiceParams: FundDataServiceParams = {
foo ...
};
const expectedRawSummaryDetails: any = {
Summary: [
{
foo ...
},
],
};
const expectedSummaryDetails: SummaryDetail[] = [
{
foo ...
},
];
describe('FundSummaryServiceRegisterMethod', () => {
let controller: ApolloTestingController;
let fundSummaryService: FundSummaryService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ApolloTestingModule, HttpClientTestingModule],
});
controller = TestBed.inject(ApolloTestingController);
foo = TestBed.inject(foo);
});
afterEach(() => {
controller.verify();
});
fit('should return foodetails', (done: DoneFn) => {
fundSummaryService
.register(fragment, fundDataServiceParams)
.subscribe((foo) => {
expect(foo).toEqual(foo);
done();
});
const op = controller.expectOne(fooQuery);
expect(op.operation.variables.foo).toEqual(foo.foo);
op.flushData(expectedfoo);
});
});
Note: I tried this old post's suggestion, didn't work.

The good news for you is that in the afterEach, controller.verify() is failing meaning there is a query in queue and not all of them have been dealt with. I am thinking that your summaryQuery is slightly off where it is not matching exactly the query where the afterEach is saying is still in queue and has not been flushed.
I think you can use a different definition of expectOne from the documentation to match the query in queue. I don't have much experience with Apollo or GraphQL but try something like this for the expectOne:
const op = controller.expectOne(operation => {
// can assert some stuff on the operation like the documentation shows
return true;
});
If the above works, it most likely means that summaryQuery does not match the query that ApolloTestingController sees.

Related

Error when building typedefs TypeError: Cannot read property 'some' of undefined

I am getting the following error when building Typedefs in Apollo Server:
return typeDef.definitions.some(definition => definition.kind === language_1.Kind.DIRECTIVE_DEFINITION &&
^
TypeError: Cannot read property 'some' of undefined
I tried to follow some solutions from here https://github.com/apollographql/apollo-server/issues/2961 but still, I am getting the error.
This is how I am creating the schema:
fs.readdirSync(__dirname)
.filter(dir => { console.log('dir', dir); return dir.indexOf('.') < 0 })
.forEach((dir) => {
const tmp = require(path.join(__dirname, dir)).default;
resolvers = merge(resolvers, tmp.resolvers);
typeDefs.push(tmp.types);
});
const schema = new ApolloServer({
typeDefs,
resolvers,
playground: {
endpoint: '/graphql',
settings: {
'editor.theme': 'light'
}
}
});
type.js
const Book = gql`
type Book {
title: String!
author: String!
}
`;
export const types = () => [Book];
export const typeResolvers = {
};
mutation.js
const Mutation = gql`
extend type Mutation {
addBook(book: BookInput): Book
}
`;
export const mutationTypes = () => [Mutation];
export const mutationResolvers = {
Mutation: {
addBook: async (_, args, ctx) => {
return []
}
}
};
index.js
export default {
types: () => [types, queryTypes, inputTypes, mutationTypes],
resolvers: Object.assign(queryResolvers, mutationResolvers, typeResolvers),
};
Any suggestions? What could I be missing?
I just had the same issue for the past 2 hours. I realized the file were i was instantiating my apollo server was being executed before the typedefs was created.
Simple way to test for this is to make a console.log(types, queryTypes, inputTypes, mutationTypes) right before the execution of const schema = new ApolloServer({ ....
One of them is undefined. Thanks.
After spending some time making changes, I finally got a working solution.
I had to make sure that typeDefs was an array of GraphQL Documents and not a type of [Function: types]. To do that, I removed unnecessary function syntax.
For example:
I replaced this export const types = () => [Book]; with this export const types = Book;
I replaced this types: () => [types, queryTypes, inputTypes, mutationTypes] with types: [types, queryTypes, inputTypes, mutationTypes]
... and pretty much every where I had () =>
Finally, before instantiating ApolloServer, instead of pushing tmp.types to the array of types, I used concat to use all defined graphql types in the I had defined the current file 'plus' the graphql types imported in every directory
typeDefs = typeDefs.concat(tmp.types);

Apollo client useQuery not updating data after useMutation

Using this mutation:
import produce from 'immer
const [createItem] = useMutation(CREATE_ITEM, {
update (client, { data: { createItem } }) {
const queryResults = client.readQuery({
query: GET_LATEST_ORDER,
variables: { orderDepth: 1 }
})
client.writeQuery({
query: GET_LATEST_ORDER,
variables: { orderDepth: 1 },
data: produce(queryResults, draft => {
draft.orders[0].items.push(createItem)
})
})
}
})
I am unable to get
const { loading, data, refetch } = useQuery(GET_LATEST_ORDER, {
variables: { orderDepth: 1 }
})
to show updated data after the mutation.
The apollo cache is updated correctly. But data on the useQuery does not change.
The issue ended up being the returned object from the mutation was not exactly the same. It was missing an #client field.
While obvious in hindsight no where I searched described this as a reason except for a comment I saw mentioning perhaps missing the __typename.
This would have been obvious had apollo thrown an error. However, no error was thrown, nor existed on the useQuery.

Rendered more hooks than during the previous render

How to use 2 graphql queries with react-apollo-hooks where the 2nd query depends on a parameter retrieved from the 1st query?
I try to use 2 queries which looks like this:
const [o, setO] = useState()
const { loading: loadingO, error: errorO, data: dataO } = useQuery(Q_GET_O, { onCompleted: d => setO(d.getO[0].id) });
if (loadingO) { return "error" }
const { loading: loadingOP, error: errorOP, data: dataOP } = useQuery(Q_GET_OP, { variables: { o } })
However, when I run my project, react-hooks gives me the following message:
"index.js:1437 Warning: React has detected a change in the order of Hooks called by Upgrade. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks"
I would like to know how I can use react-apollo-hooks in order to run a query that depends on another query. It works great if the graphql query variables are known in advance. However, I did not find a solution for variables that come from other query.
The problem here is that you are short circuit returning before all of your hooks have a chance to run.
React will complain if you exit a render function before all of the hooks have a chance to be called.
For example:
function BrokenFoo () {
const query = useSomeQuery();
if (query.loading) return <Loading />
// This will cause some issues because
// it's possible that we return before our useState hook gets called
const [bar, setBar] = useState();
return <SomeComponent bar={bar} setBar={setBar} data={query.data} />
}
To fix:
function FixedFoo () {
// This will be fine because
// all of the hooks have a chance to be called before a return
const query = useSomeQuery();
const [bar, setBar] = useState();
if (query.loading) return <Loading />
return <SomeComponent bar={bar} setBar={setBar} data={query.data} />
}
You can add the skip option to the second query and lose the if condition:
const { loading: loadingOP, error: errorOP, data: dataOP }
= useQuery(Q_GET_OP, { variables: { o }, skip: !o })
from the docs:
If skip is true, the query will be skipped entirely

Writing Structural Expectations with Jest

I am looking to write what I am calling structural expectations with Jest and I am not sure how this could be accomplished.
To start I have a graphql server and a database with a number of todo items. I currently have the following test that just returns true if the content within the database is the same as the response that I have written. I want to check instead that the response looks like an object with data that could be anything.
Here is the code that I have:
describe('To Do:', () => {
it('add todo items', async () => {
const response = await axios.post('http://localhost:5000/graphql', {
query: `
query {
getTodoItems {
message
id
dateCreated
dateDue
}
}
`
});
const { data } = response;
expect(data).toMatchObject({
data: {
getTodoItems: [
{
message: "message",
id: "5bd9aec8406e0a2170e04494",
dateCreated: "1540992712052",
dateDue: "1111111111"
},
{
message: "message",
id: "5bd9aeec60a9b2579882a308",
dateCreated: "1540992748028",
dateDue: "1111111111"
},
{
message: "new message",
id: "5bd9af15922b27236c91837c",
dateCreated: "1540992789836",
dateDue: "1111111111"
}
]
}
})
});
});
Now I want to write something like this, where there can be any number of returned items and they follow similar structuring:
describe('To Do:', () => {
it('add todo items', async () => {
const response = await axios.post('http://localhost:5000/graphql', {
query: `
query {
getTodoItems {
message
id
dateCreated
dateDue
}
}
`
});
const { data } = response;
expect(data).toMatchObject({
data: {
getTodoItems: [
{
message: expect.any(String),
id: expect.any(String),
dateCreated: expect.any(String),
dateDue: expect.any(String)
} // There needs to be unlimited additional items here
]
}
})
});
});
I have been looking throught the docs and I even tried nesting the expectations but I can't seem to get the desired response. Let me know what yo think or if I can clarify in any way.
I figured out the best way for me to do it. I would love to hear better answers. I wrote a function within the scope of the test as a jest.fn and then I called it. In that function, I made custom checks to parse the data that was received in the response. From there I added an expect function with the 'toHaveReturnedWith' method to see what the response of my custom function was and finishing out the test.
const addTodoResponse = jest.fn(() => {
// Custom parsing and check here
// Returns true or false
});
addTodoResponse();
expect(addTodoResponse).toHaveReturnedWith(true);
Are there better ways to do this out there?

Using graphql-tools to mock a GraphQL server seems broken

I've followed the documentation about using graphql-tools to mock a GraphQL server, however this throws an error for custom types, such as:
Expected a value of type "JSON" but received: [object Object]
The graphql-tools documentation about mocking explicitly states that they support custom types, and even provide an example of using the GraphQLJSON custom type from the graphql-type-json project.
I've provided a demo of a solution on github which uses graphql-tools to successfully mock a GraphQL server, but this relies on monkey-patching the built schema:
// Here we Monkey-patch the schema, as otherwise it will fall back
// to the default serialize which simply returns null.
schema._typeMap.JSON._scalarConfig.serialize = () => {
return { result: 'mocking JSON monkey-patched' }
}
schema._typeMap.MyCustomScalar._scalarConfig.serialize = () => {
return mocks.MyCustomScalar()
}
Possibly I'm doing something wrong in my demo, but without the monkey-patched code above I get the error regarding custom types mentioned above.
Does anyone have a better solution than my demo, or any clues as to what I might be doing wrong, and how I can change the code so that the demo works without monkey-patching the schema?
The relevant code in the demo index.js is as follows:
/*
** As per:
** http://dev.apollodata.com/tools/graphql-tools/mocking.html
** Note that there are references on the web to graphql-tools.mockServer,
** but these seem to be out of date.
*/
const { graphql, GraphQLScalarType } = require('graphql');
const { makeExecutableSchema, addMockFunctionsToSchema } = require('graphql-tools');
const GraphQLJSON = require('graphql-type-json');
const myCustomScalarType = new GraphQLScalarType({
name: 'MyCustomScalar',
description: 'Description of my custom scalar type',
serialize(value) {
let result;
// Implement your own behavior here by setting the 'result' variable
result = value || "I am the results of myCustomScalarType.serialize";
return result;
},
parseValue(value) {
let result;
// Implement your own behavior here by setting the 'result' variable
result = value || "I am the results of myCustomScalarType.parseValue";
return result;
},
parseLiteral(ast) {
switch (ast.kind) {
// Implement your own behavior here by returning what suits your needs
// depending on ast.kind
}
}
});
const schemaString = `
scalar MyCustomScalar
scalar JSON
type Foo {
aField: MyCustomScalar
bField: JSON
cField: String
}
type Query {
foo: Foo
}
`;
const resolverFunctions = {
Query: {
foo: {
aField: () => {
return 'I am the result of resolverFunctions.Query.foo.aField'
},
bField: () => ({ result: 'of resolverFunctions.Query.foo.bField' }),
cField: () => {
return 'I am the result of resolverFunctions.Query.foo.cField'
}
},
},
};
const mocks = {
Foo: () => ({
// aField: () => mocks.MyCustomScalar(),
// bField: () => ({ result: 'of mocks.foo.bField' }),
cField: () => {
return 'I am the result of mocks.foo.cField'
}
}),
cField: () => {
return 'mocking cField'
},
MyCustomScalar: () => {
return 'mocking MyCustomScalar'
},
JSON: () => {
return { result: 'mocking JSON'}
}
}
const query = `
{
foo {
aField
bField
cField
}
}
`;
const schema = makeExecutableSchema({
typeDefs: schemaString,
resolvers: resolverFunctions
})
addMockFunctionsToSchema({
schema,
mocks
});
// Here we Monkey-patch the schema, as otherwise it will fall back
// to the default serialize which simply returns null.
schema._typeMap.JSON._scalarConfig.serialize = () => {
return { result: 'mocking JSON monkey-patched' }
}
schema._typeMap.MyCustomScalar._scalarConfig.serialize = () => {
return mocks.MyCustomScalar()
}
graphql(schema, query).then((result) => console.log('Got result', JSON.stringify(result, null, 4)));
I and a few others are seeing a similar issue with live data sources (in my case MongoDB/Mongoose). I suspect it is something internal to the graphql-tools makeExecutableSchema and the way it ingests text-based schemas with custom types.
Here's another post on the issue: How to use graphql-type-json package with GraphQl
I haven't tried the suggestion to build the schema in code, so can't confirm whether it works or not.
My current workaround is to stringify the JSON fields (in the connector) when serving them to the client (and parsing on the client side) and vice-versa. A little clunky but I'm not really using GraphQL to query and/or selectively extract the properties within the JSON object. This wouldn't be optimal for large JSON objects I suspect.
If anyone else comes here from Google results, the solution for me was to add the JSON resolver as parameter to the makeExecutableSchema call. It's described here:
https://github.com/apollographql/apollo-test-utils/issues/28#issuecomment-377794825
That made the mocking work for me.

Resources