Skip to content

Commit

Permalink
Fix bug when using paginate on unions (#753)
Browse files Browse the repository at this point in the history
* add failing test

* failing test

* check union types for ids when looking at paginate targets

* remove errant log

* treat artifacts the same way as unions if there isn't a globally defined key config

* add test for interfaces
  • Loading branch information
AlecAivazis authored Dec 10, 2022
1 parent 7a34399 commit 27cdbb0
Show file tree
Hide file tree
Showing 4 changed files with 370 additions and 54 deletions.
264 changes: 241 additions & 23 deletions packages/houdini/src/codegen/generators/artifacts/artifacts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { mockCollectedDoc, testConfig } from '../../../test'
// the config to use in tests
const config = testConfig()

// the documents to test
const docs: CollectedGraphQLDocument[] = [
mockCollectedDoc(`query TestQuery { version }`),
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
]

test('generates an artifact for every document', async function () {
// the documents to test
const docs: CollectedGraphQLDocument[] = [
mockCollectedDoc(`query TestQuery { version }`),
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
]

// execute the generator
await runPipeline(config, docs)

Expand All @@ -25,6 +25,12 @@ test('generates an artifact for every document', async function () {
})

test('adds kind, name, and raw, response, and selection', async function () {
// the documents to test
const docs: CollectedGraphQLDocument[] = [
mockCollectedDoc(`query TestQuery { version }`),
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
]

// execute the generator
await runPipeline(config, docs)

Expand Down Expand Up @@ -146,7 +152,7 @@ test('selection includes fragments', async function () {
"HoudiniHash=c8c8290bb733a727894c836300cd22e8ece993f2b7c2108998f1d63a595e6b5f";
`)

expect(docs[1]).toMatchInlineSnapshot(`
expect(selectionDocs[1]).toMatchInlineSnapshot(`
export default {
name: "TestFragment",
kind: "HoudiniFragment",
Expand Down Expand Up @@ -174,14 +180,16 @@ test('selection includes fragments', async function () {
})

test('internal directives are scrubbed', async function () {
// execute the generator
await runPipeline(config, [
const docs = [
mockCollectedDoc(`fragment A on User { firstName }`),
mockCollectedDoc(`query TestQuery { user { ...A @prepend } }`),
])
]

// execute the generator
await runPipeline(config, docs)

// load the contents of the file
expect(docs[0]).toMatchInlineSnapshot(`
expect(docs[1]).toMatchInlineSnapshot(`
export default {
name: "TestQuery",
kind: "HoudiniQuery",
Expand Down Expand Up @@ -233,8 +241,7 @@ test('internal directives are scrubbed', async function () {
})

test('variables only used by internal directives are scrubbed', async function () {
// execute the generator
await runPipeline(config, [
const docs = [
mockCollectedDoc(`fragment A on User { firstName }`),
mockCollectedDoc(
`query TestQuery($parentID: ID!) {
Expand All @@ -243,10 +250,13 @@ test('variables only used by internal directives are scrubbed', async function (
}
}`
),
])
]

// execute the generator
await runPipeline(config, docs)

// load the contents of the file
expect(docs[0]).toMatchInlineSnapshot(`
expect(docs[1]).toMatchInlineSnapshot(`
export default {
name: "TestQuery",
kind: "HoudiniQuery",
Expand Down Expand Up @@ -306,14 +316,16 @@ test('variables only used by internal directives are scrubbed', async function (
})

test('overlapping query and fragment selection', async function () {
// execute the generator
await runPipeline(config, [
const docs = [
mockCollectedDoc(`fragment A on User { firstName }`),
mockCollectedDoc(`query TestQuery { user { firstName ...A @prepend } }`),
])
]

// execute the generator
await runPipeline(config, docs)

// load the contents of the file
expect(docs[0]).toMatchInlineSnapshot(`
expect(docs[1]).toMatchInlineSnapshot(`
export default {
name: "TestQuery",
kind: "HoudiniQuery",
Expand Down Expand Up @@ -465,15 +477,209 @@ test('interface to interface inline fragment', async function () {
`)
})

test('overlapping query and fragment nested selection', async function () {
test('paginate over unions', async function () {
const docs = [
mockCollectedDoc(
`query TestQuery {
entitiesByCursor(first: 10) @paginate(name: "All_Users") {
edges {
node {
... on User {
firstName
}
}
}
}
}`
),
]
// execute the generator
await runPipeline(config, [
await runPipeline(config, docs)

// load the contents of the file
expect(docs[0]).toMatchInlineSnapshot(`
export default {
name: "TestQuery",
kind: "HoudiniQuery",
hash: "e51aa476e50a6550a2597054599ac958070848f0b5cb0301774e6b16d5ce629d",
refetch: {
update: "append",
path: ["entitiesByCursor"],
method: "cursor",
pageSize: 10,
embedded: false,
targetType: "Query",
paginated: true,
direction: "forward"
},
raw: \`query TestQuery($first: Int = 10, $after: String) {
entitiesByCursor(first: $first, after: $after) {
edges {
node {
... on User {
firstName
}
__typename
}
}
edges {
cursor
node {
__typename
}
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
\`,
rootType: "Query",
selection: {
fields: {
entitiesByCursor: {
type: "EntityConnection",
keyRaw: "entitiesByCursor::paginated",
list: {
name: "All_Users",
connection: true,
type: "Entity"
},
selection: {
fields: {
edges: {
type: "EntityEdge",
keyRaw: "edges",
update: "append",
selection: {
fields: {
node: {
type: "Entity",
keyRaw: "node",
nullable: true,
selection: {
abstractFields: {
fields: {
User: {
firstName: {
type: "String",
keyRaw: "firstName"
},
__typename: {
type: "String",
keyRaw: "__typename"
}
}
},
typeMap: {}
},
fields: {
__typename: {
type: "String",
keyRaw: "__typename"
}
}
},
abstract: true
},
cursor: {
type: "String",
keyRaw: "cursor"
}
}
}
},
pageInfo: {
type: "PageInfo",
keyRaw: "pageInfo",
selection: {
fields: {
hasPreviousPage: {
type: "Boolean",
keyRaw: "hasPreviousPage"
},
hasNextPage: {
type: "Boolean",
keyRaw: "hasNextPage"
},
startCursor: {
type: "String",
keyRaw: "startCursor"
},
endCursor: {
type: "String",
keyRaw: "endCursor"
}
}
}
}
}
},
filters: {
first: {
kind: "Variable",
value: "first"
},
after: {
kind: "Variable",
value: "after"
}
}
}
}
},
input: {
fields: {
first: "Int",
after: "String"
},
types: {}
},
policy: "CacheOrNetwork",
partial: false
};
"HoudiniHash=98c1fdc2506e4a951db5819b1c2a712c376e5190ec86b3cc3020babcbf667a63";
`)
})

test('overlapping query and fragment nested selection', async function () {
const docs = [
mockCollectedDoc(`fragment A on User { friends { ... on User { id } } }`),
mockCollectedDoc(`query TestQuery { friends {... on User { firstName } ...A @prepend } }`),
])
]

// execute the generator
await runPipeline(config, docs)

// load the contents of the file
expect(docs[0]).toMatchInlineSnapshot(`
expect(docs[1]).toMatchInlineSnapshot(`
export default {
name: "TestQuery",
kind: "HoudiniQuery",
Expand Down Expand Up @@ -3487,6 +3693,12 @@ describe('mutation artifacts', function () {
})

test('custom scalar shows up in artifact', async function () {
// the documents to test
const docs: CollectedGraphQLDocument[] = [
mockCollectedDoc(`query TestQuery { version }`),
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
]

// define a config with a custom scalar
const localConfig = testConfig({
schema: `
Expand Down Expand Up @@ -3558,6 +3770,12 @@ test('custom scalar shows up in artifact', async function () {
})

test('operation inputs', async function () {
// the documents to test
const docs: CollectedGraphQLDocument[] = [
mockCollectedDoc(`query TestQuery { version }`),
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
]

// the config to use in tests
const localConfig = testConfig({
schema: `
Expand Down
33 changes: 33 additions & 0 deletions packages/houdini/src/codegen/validators/typeCheck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,39 @@ const table: Row[] = [
`,
],
},
{
title: '@paginate can fall on an interface if every constituent has a custom key',
pass: true,
// name needs to be passed to validate the id field
documents: [
`
query QueryA {
ghostsByCursor(first: 10) @paginate(name: "GhostA") {
edges {
node {
... on Ghost {
name
}
}
}
}
}
`,
`
query QueryB {
ghostsByCursor(first: 10) @paginate(name: "GhostB") {
edges {
node {
... on Ghost {
name
}
}
}
}
}
`,
],
},
{
title: "@paginate can't fall under lists",
pass: false,
Expand Down
Loading

2 comments on commit 27cdbb0

@vercel
Copy link

@vercel vercel bot commented on 27cdbb0 Dec 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 27cdbb0 Dec 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs-next – ./site

docs-next-git-main-houdinigraphql.vercel.app
docs-next-houdinigraphql.vercel.app
docs-next-kohl.vercel.app

Please sign in to comment.