Skip to content

Commit 7d99a31

Browse files
authored
Command-level unit tests for codegen (#464)
* Command-level unit tests for codegen * Add unit tests for additional targets * Reset volume before each unit test * Add unit tests for JSON target * Increase Jest timeout * Add unit test for schema downloading * Increase Jest timeout for check/publish tests
1 parent 77fc89e commit 7d99a31

18 files changed

Lines changed: 7599 additions & 891 deletions

File tree

package-lock.json

Lines changed: 7150 additions & 881 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module "fs-monkey";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module "memfs";

packages/apollo-cli/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"env-ci": "^2.1.0",
3434
"git-parse": "^1.0.3",
3535
"git-rev-sync": "^1.12.0",
36-
"globby": "^8.0.1",
36+
"glob": "^7.1.2",
3737
"graphql": "^0.13.1",
3838
"graphql-tag": "^2.9.2",
3939
"heroku-cli-util": "^8.0.9",
@@ -64,7 +64,9 @@
6464
"ts-jest": "^22.4.6",
6565
"ts-node": "^5.0.1",
6666
"tslib": "^1.9.0",
67-
"typescript": "^2.8.3"
67+
"typescript": "^2.8.3",
68+
"memfs": "^2.9.1",
69+
"fs-monkey": "^0.3.3"
6870
},
6971
"precommit": "pretty-quick --staged",
7072
"engines": {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {Volume, createFsFromVolume} from 'memfs';
2+
import {patchFs} from 'fs-monkey';
3+
4+
export const vol = Volume.fromJSON({});
5+
export const fs = createFsFromVolume(vol);
6+
7+
export function withGlobalFS<T>(thunk: () => T): T {
8+
const unpatch = patchFs(vol);
9+
const ret = thunk();
10+
unpatch();
11+
return ret;
12+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`successful codegen infers Flow target and writes types 1`] = `
4+
"
5+
6+
/* @flow */
7+
/* eslint-disable */
8+
// This file was automatically generated and should not be edited.
9+
10+
// ====================================================
11+
// GraphQL query operation: SimpleQuery
12+
// ====================================================
13+
14+
export type SimpleQuery = {
15+
hello: string
16+
};
17+
18+
/* @flow */
19+
/* eslint-disable */
20+
// This file was automatically generated and should not be edited.
21+
22+
//==============================================================
23+
// START Enums and Input Objects
24+
//==============================================================
25+
26+
//==============================================================
27+
// END Enums and Input Objects
28+
//=============================================================="
29+
`;
30+
31+
exports[`successful codegen infers JSON target and writes operations 1`] = `
32+
"{
33+
\\"operations\\": [
34+
{
35+
\\"filePath\\": \\"queryOne.graphql\\",
36+
\\"operationName\\": \\"SimpleQuery\\",
37+
\\"operationType\\": \\"query\\",
38+
\\"rootType\\": \\"Query\\",
39+
\\"variables\\": [],
40+
\\"source\\": \\"query SimpleQuery {\\\\n hello\\\\n}\\",
41+
\\"fields\\": [
42+
{
43+
\\"responseName\\": \\"hello\\",
44+
\\"fieldName\\": \\"hello\\",
45+
\\"type\\": \\"String!\\",
46+
\\"isConditional\\": false,
47+
\\"isDeprecated\\": false
48+
}
49+
],
50+
\\"fragmentSpreads\\": [],
51+
\\"inlineFragments\\": [],
52+
\\"fragmentsReferenced\\": [],
53+
\\"sourceWithFragments\\": \\"query SimpleQuery {\\\\n hello\\\\n}\\",
54+
\\"operationId\\": \\"4de1e386589b0853a102a87a3f21ca25266116517e59c1e8be9442e2571f00bc\\"
55+
}
56+
],
57+
\\"fragments\\": [],
58+
\\"typesUsed\\": []
59+
}"
60+
`;
61+
62+
exports[`successful codegen infers Scala target and writes types 1`] = `
63+
"// This file was automatically generated and should not be edited.
64+
65+
66+
object SimpleQueryQuery extends com.apollographql.scalajs.GraphQLQuery {
67+
val operationString =
68+
\\"query SimpleQuery {\\" +
69+
\\" hello\\" +
70+
\\"}\\"
71+
val operation = com.apollographql.scalajs.gql(operationString)
72+
73+
type Variables = Unit
74+
75+
case class Data(hello: String) {
76+
}
77+
78+
object Data {
79+
val possibleTypes = scala.collection.Set(\\"Query\\")
80+
}
81+
}"
82+
`;
83+
84+
exports[`successful codegen infers Swift target and writes types 1`] = `
85+
"// This file was automatically generated and should not be edited.
86+
87+
import Apollo
88+
89+
public final class SimpleQueryQuery: GraphQLQuery {
90+
public let operationDefinition =
91+
\\"query SimpleQuery {\\\\n hello\\\\n}\\"
92+
93+
public init() {
94+
}
95+
96+
public struct Data: GraphQLSelectionSet {
97+
public static let possibleTypes = [\\"Query\\"]
98+
99+
public static let selections: [GraphQLSelection] = [
100+
GraphQLField(\\"hello\\", type: .nonNull(.scalar(String.self))),
101+
]
102+
103+
public private(set) var resultMap: ResultMap
104+
105+
public init(unsafeResultMap: ResultMap) {
106+
self.resultMap = unsafeResultMap
107+
}
108+
109+
public init(hello: String) {
110+
self.init(unsafeResultMap: [\\"__typename\\": \\"Query\\", \\"hello\\": hello])
111+
}
112+
113+
public var hello: String {
114+
get {
115+
return resultMap[\\"hello\\"]! as! String
116+
}
117+
set {
118+
resultMap.updateValue(newValue, forKey: \\"hello\\")
119+
}
120+
}
121+
}
122+
}"
123+
`;
124+
125+
exports[`successful codegen infers TypeScript target and writes types 1`] = `
126+
"
127+
128+
/* tslint:disable */
129+
// This file was automatically generated and should not be edited.
130+
131+
// ====================================================
132+
// GraphQL query operation: SimpleQuery
133+
// ====================================================
134+
135+
export interface SimpleQuery {
136+
hello: string;
137+
}
138+
139+
/* tslint:disable */
140+
// This file was automatically generated and should not be edited.
141+
142+
//==============================================================
143+
// START Enums and Input Objects
144+
//==============================================================
145+
146+
//==============================================================
147+
// END Enums and Input Objects
148+
//=============================================================="
149+
`;
150+
151+
exports[`successful codegen writes Flow types next to sources when no output is set 1`] = `
152+
"
153+
154+
/* @flow */
155+
/* eslint-disable */
156+
// This file was automatically generated and should not be edited.
157+
158+
// ====================================================
159+
// GraphQL query operation: SimpleQuery
160+
// ====================================================
161+
162+
export type SimpleQuery = {
163+
hello: string
164+
};
165+
166+
/* @flow */
167+
/* eslint-disable */
168+
// This file was automatically generated and should not be edited.
169+
170+
//==============================================================
171+
// START Enums and Input Objects
172+
//==============================================================
173+
174+
//==============================================================
175+
// END Enums and Input Objects
176+
//=============================================================="
177+
`;
178+
179+
exports[`successful codegen writes TypeScript types next to sources when no output is set 1`] = `
180+
"
181+
182+
/* tslint:disable */
183+
// This file was automatically generated and should not be edited.
184+
185+
// ====================================================
186+
// GraphQL query operation: SimpleQuery
187+
// ====================================================
188+
189+
export interface SimpleQuery {
190+
hello: string;
191+
}
192+
193+
/* tslint:disable */
194+
// This file was automatically generated and should not be edited.
195+
196+
//==============================================================
197+
// START Enums and Input Objects
198+
//==============================================================
199+
200+
//==============================================================
201+
// END Enums and Input Objects
202+
//=============================================================="
203+
`;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
query SimpleQuery {
2+
hello
3+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
jest.mock(
2+
"apollo-codegen-core/lib/localfs",
3+
() => {
4+
return require("../../../__mocks__/localfs");
5+
}
6+
);
7+
8+
// this is because of herkou-cli-utils hacky mocking system on their console logger
9+
import { stdout, mockConsole } from "heroku-cli-util";
10+
import * as path from "path";
11+
import * as fs from "fs";
12+
import { test as setup } from "apollo-cli-test";
13+
import { introspectionQuery, print, execute, buildSchema } from "graphql";
14+
import gql from "graphql-tag";
15+
import { fs as mockFS, vol } from "apollo-codegen-core/lib/localfs";
16+
17+
const test = setup.do(() => mockConsole());
18+
const fullSchema = execute(
19+
buildSchema(
20+
fs.readFileSync(path.resolve(__dirname, "../../schema/__tests__/fixtures/schema.graphql"), {
21+
encoding: "utf-8",
22+
})
23+
),
24+
gql(introspectionQuery)
25+
).data;
26+
27+
const simpleQuery = fs.readFileSync(path.resolve(__dirname, "./fixtures/simpleQuery.graphql"));
28+
29+
beforeEach(() => {
30+
vol.reset();
31+
vol.fromJSON({
32+
"__blankFileSoDirectoryExists": ""
33+
});
34+
})
35+
36+
jest.setTimeout(15000);
37+
38+
describe("successful codegen", () => {
39+
test
40+
.do(() => {
41+
vol.fromJSON({
42+
"schema.json": JSON.stringify(fullSchema.__schema),
43+
"queryOne.graphql": simpleQuery.toString()
44+
});
45+
})
46+
.command(["codegen:generate", "--schema=schema.json", "API.swift"])
47+
.it("infers Swift target and writes types", () => {
48+
expect(mockFS.readFileSync("API.swift").toString()).toMatchSnapshot();
49+
});
50+
51+
test
52+
.do(() => {
53+
vol.fromJSON({
54+
"schema.json": JSON.stringify(fullSchema.__schema),
55+
"queryOne.graphql": simpleQuery.toString()
56+
});
57+
})
58+
.command(["codegen:generate", "--schema=schema.json", "API.scala"])
59+
.it("infers Scala target and writes types", () => {
60+
expect(mockFS.readFileSync("API.scala").toString()).toMatchSnapshot();
61+
});
62+
63+
test
64+
.do(() => {
65+
vol.fromJSON({
66+
"schema.json": JSON.stringify(fullSchema.__schema),
67+
"queryOne.graphql": simpleQuery.toString()
68+
});
69+
})
70+
.command(["codegen:generate", "--schema=schema.json", "API.ts"])
71+
.it("infers TypeScript target and writes types", () => {
72+
expect(mockFS.readFileSync("API.ts").toString()).toMatchSnapshot();
73+
});
74+
75+
test
76+
.do(() => {
77+
vol.fromJSON({
78+
"schema.json": JSON.stringify(fullSchema.__schema),
79+
"queryOne.graphql": simpleQuery.toString()
80+
});
81+
})
82+
.command(["codegen:generate", "--schema=schema.json", "API.js"])
83+
.it("infers Flow target and writes types", () => {
84+
expect(mockFS.readFileSync("API.js").toString()).toMatchSnapshot();
85+
});
86+
87+
test
88+
.do(() => {
89+
vol.fromJSON({
90+
"schema.json": JSON.stringify(fullSchema.__schema),
91+
"queryOne.graphql": simpleQuery.toString()
92+
});
93+
})
94+
.command(["codegen:generate", "--schema=schema.json", "operations.json"])
95+
.it("infers JSON target and writes operations", () => {
96+
expect(mockFS.readFileSync("operations.json").toString()).toMatchSnapshot();
97+
});
98+
99+
test
100+
.do(() => {
101+
vol.fromJSON({
102+
"schema.json": JSON.stringify(fullSchema.__schema),
103+
"directory/component.tsx": `
104+
gql\`
105+
query SimpleQuery {
106+
hello
107+
}
108+
\`;
109+
`
110+
});
111+
})
112+
.command(["codegen:generate", "--schema=schema.json", "--queries=**/*.tsx", "--target=typescript"])
113+
.it("writes TypeScript types next to sources when no output is set", () => {
114+
expect(mockFS.readFileSync("directory/SimpleQuery.ts").toString()).toMatchSnapshot();
115+
});
116+
117+
test
118+
.do(() => {
119+
vol.fromJSON({
120+
"schema.json": JSON.stringify(fullSchema.__schema),
121+
"directory/component.jsx": `
122+
gql\`
123+
query SimpleQuery {
124+
hello
125+
}
126+
\`;
127+
`
128+
});
129+
})
130+
.command(["codegen:generate", "--schema=schema.json", "--queries=**/*.jsx", "--target=flow"])
131+
.it("writes Flow types next to sources when no output is set", () => {
132+
expect(mockFS.readFileSync("directory/SimpleQuery.js").toString()).toMatchSnapshot();
133+
});
134+
});
135+
136+
describe("error handling", () => {
137+
test
138+
.command(["codegen:generate", "--target=foobar"])
139+
.catch(err => expect(err.message).toMatch(/Unsupported target: foobar/))
140+
.it("errors with an unsupported target");
141+
142+
test
143+
.command(["codegen:generate", "--target=swift"])
144+
.catch(err => expect(err.message).toMatch(/The output path must be specified/))
145+
.it("errors when no output file is provided");
146+
});

0 commit comments

Comments
 (0)