Skip to content

Commit 3c363c6

Browse files
author
Dane Pilcher
authored
test: build type and graphql statement generation (#757)
1 parent d74bbd9 commit 3c363c6

File tree

10 files changed

+197
-29
lines changed

10 files changed

+197
-29
lines changed

.codebuild/e2e_workflow.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ batch:
143143
TEST_SUITE: >-
144144
src/__tests__/build-app-ts.test.ts|src/__tests__/uninitialized-project-codegen-js.test.ts|src/__tests__/uninitialized-project-modelgen-android.test.ts|src/__tests__/uninitialized-project-modelgen-flutter.test.ts
145145
CLI_REGION: ap-southeast-1
146+
DISABLE_ESLINT_PLUGIN: true
146147
depend-on:
147148
- publish_to_local_registry
148149
- identifier: l_uninitialized_project_modelgen_ios_uninitialized_project_modelgen_js
@@ -250,6 +251,7 @@ batch:
250251
TEST_SUITE: >-
251252
src/__tests__/build-app-ts.test.ts|src/__tests__/uninitialized-project-codegen-js.test.ts|src/__tests__/uninitialized-project-modelgen-android.test.ts|src/__tests__/uninitialized-project-modelgen-flutter.test.ts
252253
CLI_REGION: us-east-1
254+
DISABLE_ESLINT_PLUGIN: true
253255
depend-on:
254256
- publish_to_local_registry
255257
- build_windows

packages/amplify-codegen-e2e-core/src/categories/api.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,35 @@ export function addApiWithBlankSchemaAndConflictDetection(cwd: string) {
120120
});
121121
}
122122

123+
export function addApiWithDefaultSchemaAndConflictDetection(cwd: string) {
124+
return new Promise<void>((resolve, reject) => {
125+
spawn(getCLIPath(defaultOptions.testingWithLatestCodebase), ['add', 'api'], { cwd, stripColors: true })
126+
.wait('Select from one of the below mentioned services:')
127+
.sendCarriageReturn()
128+
.wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/)
129+
.sendKeyUp()
130+
.sendCarriageReturn()
131+
.wait(/.*Enable conflict detection.*/)
132+
.sendConfirmYes()
133+
.wait(/.*Select the default resolution strategy.*/)
134+
.sendCarriageReturn()
135+
.wait(/.*Here is the GraphQL API that we will create. Select a setting to edit or continue.*/)
136+
.sendCarriageReturn()
137+
.wait('Choose a schema template:')
138+
.sendCarriageReturn()
139+
.wait('Do you want to edit the schema now?')
140+
.sendConfirmNo()
141+
.wait('"amplify publish" will build all your local backend and frontend resources')
142+
.run((err: Error) => {
143+
if (!err) {
144+
resolve();
145+
} else {
146+
reject(err);
147+
}
148+
});
149+
});
150+
}
151+
123152
export function updateApiSchema(cwd: string, projectName: string, schemaName: string, forceUpdate: boolean = false) {
124153
const testSchemaPath = getSchemaPath(schemaName);
125154
let schemaText = fs.readFileSync(testSchemaPath).toString();

packages/amplify-codegen-e2e-core/src/init/initProjectHelper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ export function initIosProjectWithProfile(cwd: string, settings: Object): Promis
152152
.wait('Choose your default editor:')
153153
.sendLine(s.editor)
154154
.wait("Choose the type of app that you're building")
155-
.sendKeyDown(3)
156-
.sendCarriageReturn()
155+
.sendLine('ios')
157156
.wait('Select the authentication method you want to use:')
158157
.sendCarriageReturn()
159158
.wait('Please choose the profile you want to use')
Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,54 @@
11
import {
2-
initProjectWithQuickstart,
2+
initProjectWithProfile,
33
DEFAULT_ANDROID_CONFIG,
44
updateApiSchemaWithText,
5+
addApiWithDefaultSchemaAndConflictDetection,
56
generateModels,
7+
generateStatementsAndTypes,
68
androidBuild,
7-
acceptLicenses
9+
acceptLicenses,
10+
addCodegen,
11+
AmplifyFrontend,
12+
apiGqlCompile,
813
} from '@aws-amplify/amplify-codegen-e2e-core';
914
const { schemas } = require('@aws-amplify/graphql-schema-test-library');
10-
import { existsSync, writeFileSync, readdirSync, rmSync } from 'fs';
15+
import { existsSync, writeFileSync, readdirSync, rmSync, readFileSync } from 'fs';
1116
import path from 'path';
17+
import { parse } from 'graphql';
1218

13-
const skip = new Set(['v2-primary-key-with-composite-sort-key', 'custom-@primaryKey-with-sort-fields']);
19+
const skip = new Set([
20+
'v2-primary-key-with-composite-sort-key',
21+
'custom-@primaryKey-with-sort-fields',
22+
'v2-cyclic-has-one-dependency',
23+
'v2-cyclic-has-many-dependency',
24+
]);
1425

1526
describe('build app - Android', () => {
1627
let apiName: string;
1728
const projectRoot = path.resolve('test-apps/android');
1829
const config = DEFAULT_ANDROID_CONFIG;
1930
const modelDir = 'app/src/main/java/com/amplifyframework/datastore/generated/model';
31+
const statementsDir = 'app/src/main/graphql/com/amazonaws/amplify/generated/graphql';
2032

2133
beforeAll(async () => {
22-
await initProjectWithQuickstart(projectRoot, { ...config });
34+
await initProjectWithProfile(projectRoot, { ...config });
35+
await addApiWithDefaultSchemaAndConflictDetection(projectRoot);
2336
apiName = readdirSync(path.join(projectRoot, 'amplify', 'backend', 'api'))[0];
37+
await apiGqlCompile(projectRoot);
38+
await addCodegen(projectRoot, {
39+
frontendType: AmplifyFrontend.android,
40+
});
2441
await acceptLicenses(projectRoot);
2542
});
2643

2744
afterAll(async () => {
2845
rmSync(path.join(projectRoot, 'amplify'), { recursive: true, force: true });
46+
rmSync(path.join(projectRoot, '.graphqlconfig.yml'), { recursive: true, force: true });
2947
});
3048

3149
afterEach(() => {
3250
rmSync(path.join(projectRoot, modelDir), { recursive: true, force: true });
51+
rmSync(path.join(projectRoot, statementsDir), { recursive: true, force: true });
3352
});
3453

3554
Object.entries(schemas).forEach(([schemaName, schema]) => {
@@ -39,8 +58,14 @@ describe('build app - Android', () => {
3958
// @ts-ignore
4059
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${schema.sdl}`;
4160
updateApiSchemaWithText(projectRoot, apiName, schemaText);
61+
await apiGqlCompile(projectRoot);
4262
await generateModels(projectRoot);
63+
await generateStatementsAndTypes(projectRoot);
4364
await androidBuild(projectRoot, { ...config });
65+
// android uses raw graphql syntax
66+
parse(readFileSync(path.join(projectRoot, path.join(statementsDir, 'queries.graphql')), 'utf8'));
67+
parse(readFileSync(path.join(projectRoot, path.join(statementsDir, 'subscriptions.graphql')), 'utf8'));
68+
parse(readFileSync(path.join(projectRoot, path.join(statementsDir, 'mutations.graphql')), 'utf8'));
4469
};
4570
if (skip.has(schemaName)) {
4671
it.skip(testName, testFunction);
@@ -49,11 +74,38 @@ describe('build app - Android', () => {
4974
}
5075
});
5176

52-
it('fails build with syntax error', async () => {
77+
// not supported with conflict resolution enabled
78+
[
79+
['v2-cyclic-has-one-dependency', schemas['v2-cyclic-has-one-dependency']],
80+
['v2-cyclic-has-many-dependency', schemas['v2-cyclic-has-many-dependency']],
81+
].forEach(([schemaName, schema]) => {
82+
// @ts-ignore
83+
it(`builds models with ${schemaName}: ${schema.description}`, async () => {
84+
// @ts-ignore
85+
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${schema.sdl}`;
86+
updateApiSchemaWithText(projectRoot, apiName, schemaText);
87+
await generateModels(projectRoot);
88+
await androidBuild(projectRoot, { ...config });
89+
});
90+
});
91+
92+
it('fails build with syntax error in models', async () => {
5393
// @ts-ignore
5494
updateApiSchemaWithText(projectRoot, apiName, Object.values(schemas)[0].sdl);
95+
await apiGqlCompile(projectRoot);
5596
await generateModels(projectRoot);
5697
await writeFileSync(path.join(projectRoot, modelDir, 'AmplifyModelProvider.java'), 'foo\nbar');
5798
await expect(androidBuild(projectRoot, { ...config })).rejects.toThrowError();
5899
});
100+
101+
it('fails build with syntax error in statements', async () => {
102+
// @ts-ignore
103+
updateApiSchemaWithText(projectRoot, apiName, Object.values(schemas)[0].sdl);
104+
await apiGqlCompile(projectRoot);
105+
await generateModels(projectRoot);
106+
await generateStatementsAndTypes(projectRoot);
107+
writeFileSync(path.join(projectRoot, statementsDir, 'mutations.graphql'), 'foo\nbar');
108+
expect(() => parse(readFileSync(path.join(projectRoot, statementsDir, 'mutations.graphql'), 'utf8'))).toThrowError();
109+
await androidBuild(projectRoot, { ...config });
110+
});
59111
});

packages/amplify-codegen-e2e-tests/src/__tests__/build-app-swift.test.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
import {
2-
initProjectWithQuickstart,
2+
initProjectWithQuickstart
3+
initIosProjectWithProfile,
4+
addApiWithDefaultSchemaAndConflictDetection,
35
DEFAULT_IOS_CONFIG,
46
updateApiSchemaWithText,
57
generateModels,
8+
generateStatementsAndTypes,
9+
addCodegen,
10+
AmplifyFrontend,
11+
apiGqlCompile,
612
} from '@aws-amplify/amplify-codegen-e2e-core';
713
const { schemas } = require('@aws-amplify/graphql-schema-test-library');
8-
import { writeFileSync, readdirSync, readFileSync } from 'fs';
14+
import { writeFileSync, readdirSync, readFileSync, rmSync, mkdirSync } from 'fs';
15+
import { copySync } from 'fs-extra';
916
import path from 'path';
17+
import { parse } from 'graphql';
1018

1119
const skip = new Set([
1220
'v2-recursive-has-one-dependency',
1321
'v2-cyclic-has-one-dependency',
22+
'v2-cyclic-has-many-dependency',
1423
'@hasOne-with-@belongsTo-with-implicit-parameters',
1524
'@hasOne-with-@belongsTo-with-explicit-parameters',
1625
]);
@@ -22,15 +31,37 @@ describe('build app - Swift', () => {
2231
const config = DEFAULT_IOS_CONFIG;
2332

2433
beforeAll(async () => {
25-
await initProjectWithQuickstart(projectRoot, { ...config });
34+
await initIosProjectWithProfile(projectRoot, { ...config });
35+
await addApiWithDefaultSchemaAndConflictDetection(projectRoot);
2636
apiName = readdirSync(path.join(projectRoot, 'amplify', 'backend', 'api'))[0];
37+
await apiGqlCompile(projectRoot);
38+
await addCodegen(projectRoot, {
39+
frontendType: AmplifyFrontend.ios,
40+
});
2741
projectPBXProjCache = readFileSync(path.join(projectRoot, 'swift.xcodeproj', 'project.pbxproj'));
2842
});
2943

3044
afterEach(async () => {
3145
writeFileSync(path.join(projectRoot, 'swift.xcodeproj', 'project.pbxproj'), projectPBXProjCache);
3246
});
3347

48+
afterAll(async () => {
49+
await rmSync(path.join(projectRoot, 'amplify'), { recursive: true, force: true });
50+
rmSync(path.join(projectRoot, '.graphqlconfig.yml'), { recursive: true, force: true });
51+
52+
// generate models one more time
53+
// files are used in a GitHub action to test compilation
54+
// codebuild does not suport MacOS instances
55+
await initProjectWithQuickstart(projectRoot, { ...config });
56+
apiName = readdirSync(path.join(projectRoot, 'amplify', 'backend', 'api'))[0];
57+
const [schemaName, schema] = Object.entries(schemas)[0];
58+
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${(schema as any).sdl}`;
59+
updateApiSchemaWithText(projectRoot, apiName, schemaText);
60+
const schemaFolderName = schemaName.replace(/[^a-zA-Z0-9]/g, '');
61+
const outputDir = path.join(projectRoot, 'amplify', 'generated', 'models', schemaFolderName);
62+
await generateModels(projectRoot, outputDir);
63+
});
64+
3465
Object.entries(schemas).forEach(([schemaName, schema]) => {
3566
// @ts-ignore
3667
const testName = `builds with ${schemaName}: ${schema.description}`;
@@ -40,8 +71,14 @@ describe('build app - Swift', () => {
4071
// @ts-ignore
4172
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${schema.sdl}`;
4273
console.log(schemaText); // log so that ci does not timeout
43-
updateApiSchemaWithText(projectRoot, 'amplifyDatasource', schemaText);
74+
updateApiSchemaWithText(projectRoot, apiName, schemaText);
75+
await apiGqlCompile(projectRoot);
4476
await generateModels(projectRoot, outputDir);
77+
await generateStatementsAndTypes(projectRoot);
78+
// swift uses raw graphql syntax
79+
parse(readFileSync(path.join(projectRoot, 'graphql/queries.graphql'), 'utf8'));
80+
parse(readFileSync(path.join(projectRoot, 'graphql/subscriptions.graphql'), 'utf8'));
81+
parse(readFileSync(path.join(projectRoot, 'graphql/mutations.graphql'), 'utf8'));
4582
};
4683
if (skip.has(schemaName)) {
4784
it.skip(testName, testFunction);
Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,89 @@
11
import {
22
initProjectWithProfile,
33
DEFAULT_JS_CONFIG,
4-
addApiWithBlankSchemaAndConflictDetection,
4+
addApiWithDefaultSchemaAndConflictDetection,
55
updateApiSchemaWithText,
66
generateModels,
7+
generateStatementsAndTypes,
78
craInstall,
89
craBuild,
10+
addCodegen,
11+
AmplifyFrontend,
12+
apiGqlCompile,
913
} from '@aws-amplify/amplify-codegen-e2e-core';
1014
const { schemas } = require('@aws-amplify/graphql-schema-test-library');
1115
import { existsSync, writeFileSync, readdirSync, rmSync } from 'fs';
1216
import path from 'path';
1317

1418
const schema = 'simple_model.graphql';
1519

20+
// not supported with conflict resolution enabled
21+
const skip = new Set(['v2-cyclic-has-one-dependency', 'v2-cyclic-has-many-dependency']);
22+
1623
describe('build app - JS', () => {
1724
let apiName: string;
1825
const projectRoot = path.resolve('test-apps/ts');
1926
const config = DEFAULT_JS_CONFIG;
2027

2128
beforeAll(async () => {
2229
await initProjectWithProfile(projectRoot, { ...config });
23-
await addApiWithBlankSchemaAndConflictDetection(projectRoot);
24-
await craInstall(projectRoot, { ...config });
30+
await addApiWithDefaultSchemaAndConflictDetection(projectRoot);
2531
apiName = readdirSync(path.join(projectRoot, 'amplify', 'backend', 'api'))[0];
32+
await apiGqlCompile(projectRoot);
33+
await addCodegen(projectRoot, {
34+
frontendType: AmplifyFrontend.javascript,
35+
});
36+
await craInstall(projectRoot, { ...config });
2637
});
2738

2839
afterAll(async () => {
2940
await rmSync(path.join(projectRoot, 'amplify'), { recursive: true, force: true });
41+
rmSync(path.join(projectRoot, '.graphqlconfig.yml'), { recursive: true, force: true });
3042
});
3143

3244
Object.entries(schemas).forEach(([schemaName, schema]) => {
33-
// @ts-ignore
34-
it(`builds with ${schemaName}: ${schema.description}`, async () => {
35-
// @ts-ignore
36-
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${schema.sdl}`;
45+
const testName = `builds with ${schemaName}: ${(schema as any).description}`;
46+
const testFunction = async () => {
47+
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${(schema as any).sdl}`;
3748
updateApiSchemaWithText(projectRoot, apiName, schemaText);
49+
await apiGqlCompile(projectRoot);
3850
await generateModels(projectRoot);
51+
await generateStatementsAndTypes(projectRoot);
3952
await craBuild(projectRoot, { ...config });
40-
});
53+
};
54+
if (skip.has(schemaName)) {
55+
it.skip(testName, testFunction);
56+
} else {
57+
it(testName, testFunction);
58+
}
4159
});
4260

43-
it('fails build with syntax error', async () => {
61+
it('fails build with syntax error in models', async () => {
62+
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${(Object.values(schemas)[0] as any).sdl}`;
63+
updateApiSchemaWithText(projectRoot, apiName, schemaText);
64+
await apiGqlCompile(projectRoot);
65+
await generateStatementsAndTypes(projectRoot);
4466
await writeFileSync(path.join(projectRoot, 'src', 'models', 'index.d.ts'), 'foo\nbar');
4567
await expect(craBuild(projectRoot, { ...config })).rejects.toThrowError();
4668
});
69+
70+
it('fails build with syntax error in statements', async () => {
71+
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${(Object.values(schemas)[0] as any).sdl}`;
72+
updateApiSchemaWithText(projectRoot, apiName, schemaText);
73+
await apiGqlCompile(projectRoot);
74+
await generateModels(projectRoot);
75+
await generateStatementsAndTypes(projectRoot);
76+
await writeFileSync(path.join(projectRoot, 'src', 'graphql', 'queries.ts'), 'foo\nbar');
77+
await expect(craBuild(projectRoot, { ...config })).rejects.toThrowError();
78+
});
79+
80+
it('fails build with syntax error in types', async () => {
81+
const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${(Object.values(schemas)[0] as any).sdl}`;
82+
updateApiSchemaWithText(projectRoot, apiName, schemaText);
83+
await apiGqlCompile(projectRoot);
84+
await generateModels(projectRoot);
85+
await generateStatementsAndTypes(projectRoot);
86+
await writeFileSync(path.join(projectRoot, 'src', 'API.ts'), 'foo\nbar');
87+
await expect(craBuild(projectRoot, { ...config })).rejects.toThrowError();
88+
});
4789
});
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
src/models
1+
src/models
2+
src/graphql
3+
src/API.ts

packages/amplify-codegen-e2e-tests/test-apps/ts/package.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@
2828
"serve": "serve build",
2929
"set-schema": "node scripts/set-schema.js"
3030
},
31-
"eslintConfig": {
32-
"extends": [
33-
"react-app",
34-
"react-app/jest"
35-
]
36-
},
3731
"browserslist": {
3832
"production": [
3933
">0.2%",

packages/amplify-codegen-e2e-tests/test-apps/ts/src/App.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import { useEffect } from 'react';
2-
import * as models from './models'; // import and use models to avoid tree shaking
2+
// import and use to avoid tree shaking
3+
import * as models from './models';
4+
import * as queries from './graphql/queries';
5+
import * as mutations from './graphql/mutations';
6+
import * as subscriptions from './graphql/subscriptions';
7+
// don't need to import API.ts becuase it is imported by graphql statements
38

49
function App() {
510
useEffect(() => {
611
console.log(models);
12+
console.log(queries);
13+
console.log(mutations);
14+
console.log(subscriptions);
715
}, []);
816
return <div></div>;
917
}

0 commit comments

Comments
 (0)