@aws-amplify/amplify-util-uibuilder 1.9.0-replace-lambda-callouts-branch.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +40 -9
  2. package/lib/clients/amplify-studio-client.d.ts +5 -1
  3. package/lib/clients/amplify-studio-client.d.ts.map +1 -1
  4. package/lib/clients/amplify-studio-client.js +54 -8
  5. package/lib/clients/amplify-studio-client.js.map +1 -1
  6. package/lib/commands/generateComponents.d.ts.map +1 -1
  7. package/lib/commands/generateComponents.js +33 -34
  8. package/lib/commands/generateComponents.js.map +1 -1
  9. package/lib/commands/utils/codegenResources.d.ts +1 -15
  10. package/lib/commands/utils/codegenResources.d.ts.map +1 -1
  11. package/lib/commands/utils/codegenResources.js +1 -122
  12. package/lib/commands/utils/codegenResources.js.map +1 -1
  13. package/lib/commands/utils/detachedFormCleanup.d.ts +3 -4
  14. package/lib/commands/utils/detachedFormCleanup.d.ts.map +1 -1
  15. package/lib/commands/utils/detachedFormCleanup.js.map +1 -1
  16. package/lib/commands/utils/storageManager.d.ts +3 -4
  17. package/lib/commands/utils/storageManager.d.ts.map +1 -1
  18. package/lib/commands/utils/storageManager.js +1 -5
  19. package/lib/commands/utils/storageManager.js.map +1 -1
  20. package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts +8 -16
  21. package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
  22. package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +186 -74
  23. package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
  24. package/package.json +28 -19
  25. package/src/__tests__/__snapshots__/syncAmplifyUiComponents.test.ts.snap +46 -0
  26. package/src/__tests__/cloneComponentsFromEnv.test.ts +8 -0
  27. package/src/__tests__/detachedFormCleanup.test.ts +7 -5
  28. package/src/__tests__/generateComponents.test.ts +187 -15
  29. package/src/__tests__/getAmplifyDataSchema.test.ts +1 -2
  30. package/src/__tests__/prePushHandler.test.ts +7 -0
  31. package/src/__tests__/storageManager.test.ts +7 -4
  32. package/src/__tests__/syncAmplifyUiComponents.test.ts +106 -95
  33. package/src/clients/amplify-studio-client.ts +56 -9
  34. package/src/commands/generateComponents.ts +45 -56
  35. package/src/commands/utils/codegenResources.ts +1 -225
  36. package/src/commands/utils/detachedFormCleanup.ts +3 -4
  37. package/src/commands/utils/storageManager.ts +3 -7
  38. package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +210 -123
  39. package/tsconfig.tsbuildinfo +1 -1
  40. package/src/__tests__/createUiBuilderComponent.test.ts +0 -90
@@ -1,15 +1,32 @@
1
1
  import aws from 'aws-sdk'; // eslint-disable-line import/no-extraneous-dependencies
2
2
  import * as utils from '../commands/utils';
3
3
  import { run } from '../commands/generateComponents';
4
+ import { isDataStoreEnabled } from '@aws-amplify/amplify-category-api';
5
+ import { getTransformerVersion } from '../commands/utils/featureFlags';
4
6
 
5
7
  jest.mock('../commands/utils');
6
8
  jest.mock('@aws-amplify/amplify-cli-core');
9
+ jest.mock('@aws-amplify/amplify-category-api', () => ({
10
+ ...jest.requireActual('@aws-amplify/amplify-category-api'),
11
+ isDataStoreEnabled: jest.fn(),
12
+ }));
13
+ jest.mock('../commands/utils/featureFlags', () => ({
14
+ ...jest.requireActual('../commands/utils/featureFlags'),
15
+ getTransformerVersion: jest.fn(),
16
+ }));
7
17
  const awsMock = aws as any;
8
18
  const utilsMock = utils as any;
9
19
 
20
+ const isDataStoreEnabledMocked = jest.mocked(isDataStoreEnabled);
21
+ const getTransformerVersionMocked = jest.mocked(getTransformerVersion);
10
22
  utilsMock.shouldRenderComponents = jest.fn().mockReturnValue(true);
11
23
  utilsMock.notifyMissingPackages = jest.fn().mockReturnValue(true);
12
24
  utilsMock.getAmplifyDataSchema = jest.fn().mockReturnValue({});
25
+ utilsMock.isFormDetachedFromModel = jest.fn().mockReturnValue(false);
26
+ utilsMock.extractUIComponents = jest.fn().mockReturnValue(undefined);
27
+ utilsMock.waitForSucceededJob = jest
28
+ .fn()
29
+ .mockReturnValue({ asset: { downloadUrl: 'amazon.com' }, statusMessage: `{\"codegenErrors\": [{\"schemaName\": \"BlogUpdateForm\"}]}` });
13
30
 
14
31
  jest.mock('../commands/utils/featureFlags', () => ({
15
32
  getTransformerVersion: jest.fn().mockReturnValue(2),
@@ -19,7 +36,25 @@ describe('can generate components', () => {
19
36
  let context: any;
20
37
  let schemas: any;
21
38
  let mockedExport: jest.Mock<any, any>;
39
+ const getMetadataPromise = jest.fn().mockReturnValue({
40
+ features: {
41
+ autoGenerateForms: 'true',
42
+ autoGenerateViews: 'true',
43
+ formFeatureFlags: {
44
+ isRelationshipSupported: 'false',
45
+ isNonModelSupported: 'false',
46
+ },
47
+ },
48
+ });
49
+ const startCodegenJobPromise = jest.fn().mockReturnValue({
50
+ entity: { id: 'jobId123' },
51
+ });
52
+ const mockStartCodegenJob = jest.fn().mockReturnValue({
53
+ promise: startCodegenJobPromise,
54
+ });
22
55
  beforeEach(() => {
56
+ isDataStoreEnabledMocked.mockResolvedValue(true);
57
+ getTransformerVersionMocked.mockResolvedValue(2);
23
58
  context = {
24
59
  amplify: {
25
60
  invokePluginMethod: () => ({}),
@@ -34,13 +69,11 @@ describe('can generate components', () => {
34
69
  schemas = {
35
70
  entities: [
36
71
  {
37
- resultType: 'SUCCESS',
38
72
  schemaName: 'testSchema',
39
73
  name: 'testSchema',
40
74
  schemaVersion: '1.0',
41
75
  },
42
76
  {
43
- resultType: 'FAILURE',
44
77
  schemaName: 'testSchema',
45
78
  name: 'testSchema',
46
79
  schemaVersion: '1.0',
@@ -65,16 +98,11 @@ describe('can generate components', () => {
65
98
  promise: () => mockedExport(),
66
99
  }),
67
100
  getMetadata: jest.fn().mockReturnValue({
68
- promise: jest.fn().mockReturnValue({
69
- features: {
70
- autoGenerateForms: 'true',
71
- autoGenerateViews: 'true',
72
- formFeatureFlags: {
73
- isRelationshipSupported: 'false',
74
- isNonModelSupported: 'false',
75
- },
76
- },
77
- }),
101
+ promise: getMetadataPromise,
102
+ }),
103
+ startCodegenJob: mockStartCodegenJob,
104
+ getCodegenJob: jest.fn().mockReturnValue({
105
+ promise: jest.fn().mockReturnValue({ status: 'succeeded' }),
78
106
  }),
79
107
  });
80
108
  utilsMock.generateUiBuilderComponents = jest.fn().mockReturnValue(schemas.entities);
@@ -87,11 +115,155 @@ describe('can generate components', () => {
87
115
  });
88
116
 
89
117
  it('runs generateComponents', async () => {
118
+ utilsMock.isFormDetachedFromModel = jest.fn().mockReturnValueOnce(true);
90
119
  await run(context, 'PostPull');
91
120
  expect(mockedExport).toBeCalledTimes(3);
92
- expect(utilsMock.generateUiBuilderComponents).toBeCalledTimes(1);
93
- expect(utilsMock.generateUiBuilderThemes).toBeCalledTimes(1);
94
- expect(utilsMock.generateUiBuilderForms).toBeCalledTimes(1);
121
+ expect(startCodegenJobPromise).toBeCalledTimes(1);
122
+ expect(utilsMock.waitForSucceededJob).toBeCalledTimes(1);
123
+ expect(utilsMock.getUiBuilderComponentsPath).toBeCalledTimes(1);
124
+ expect(utilsMock.extractUIComponents).toBeCalledTimes(1);
95
125
  expect(utilsMock.deleteDetachedForms).toBeCalledTimes(1);
96
126
  });
127
+
128
+ it('should autogenerate forms if transformer v2 and datastore and feature flag are enabled', async () => {
129
+ isDataStoreEnabledMocked.mockResolvedValue(true);
130
+ getTransformerVersionMocked.mockResolvedValue(2);
131
+ getMetadataPromise.mockReturnValue({
132
+ features: {
133
+ autoGenerateForms: 'true',
134
+ autoGenerateViews: 'true',
135
+ formFeatureFlags: {
136
+ isRelationshipSupported: 'false',
137
+ isNonModelSupported: 'false',
138
+ },
139
+ },
140
+ });
141
+ await run(context, 'PostPull');
142
+ expect(mockStartCodegenJob).toHaveBeenCalledWith({
143
+ appId: 'testAppId',
144
+ environmentName: 'testEnvName',
145
+ codegenJobToCreate: {
146
+ renderConfig: {
147
+ react: {
148
+ module: 'es2020',
149
+ target: 'es2020',
150
+ script: 'jsx',
151
+ renderTypeDeclarations: true,
152
+ },
153
+ },
154
+ genericDataSchema: undefined,
155
+ autoGenerateForms: true,
156
+ features: {
157
+ isNonModelSupported: false,
158
+ isRelationshipSupported: false,
159
+ },
160
+ },
161
+ });
162
+ });
163
+
164
+ it('should not autogenerate forms if transformer v1', async () => {
165
+ isDataStoreEnabledMocked.mockResolvedValue(true);
166
+ getTransformerVersionMocked.mockResolvedValue(1);
167
+ getMetadataPromise.mockReturnValue({
168
+ features: {
169
+ autoGenerateForms: 'true',
170
+ autoGenerateViews: 'true',
171
+ formFeatureFlags: {
172
+ isRelationshipSupported: 'false',
173
+ isNonModelSupported: 'false',
174
+ },
175
+ },
176
+ });
177
+ await run(context, 'PostPull');
178
+ expect(mockStartCodegenJob).toHaveBeenCalledWith({
179
+ appId: 'testAppId',
180
+ environmentName: 'testEnvName',
181
+ codegenJobToCreate: {
182
+ renderConfig: {
183
+ react: {
184
+ module: 'es2020',
185
+ target: 'es2020',
186
+ script: 'jsx',
187
+ renderTypeDeclarations: true,
188
+ },
189
+ },
190
+ genericDataSchema: undefined,
191
+ autoGenerateForms: false,
192
+ features: {
193
+ isNonModelSupported: false,
194
+ isRelationshipSupported: false,
195
+ },
196
+ },
197
+ });
198
+ });
199
+
200
+ it('should not autogenerate forms if datastore is not enabled', async () => {
201
+ isDataStoreEnabledMocked.mockResolvedValue(false);
202
+ getMetadataPromise.mockReturnValue({
203
+ features: {
204
+ autoGenerateForms: 'true',
205
+ autoGenerateViews: 'true',
206
+ formFeatureFlags: {
207
+ isRelationshipSupported: 'false',
208
+ isNonModelSupported: 'false',
209
+ },
210
+ },
211
+ });
212
+ await run(context, 'PostPull');
213
+ expect(mockStartCodegenJob).toHaveBeenCalledWith({
214
+ appId: 'testAppId',
215
+ environmentName: 'testEnvName',
216
+ codegenJobToCreate: {
217
+ renderConfig: {
218
+ react: {
219
+ module: 'es2020',
220
+ target: 'es2020',
221
+ script: 'jsx',
222
+ renderTypeDeclarations: true,
223
+ },
224
+ },
225
+ genericDataSchema: undefined,
226
+ autoGenerateForms: false,
227
+ features: {
228
+ isNonModelSupported: false,
229
+ isRelationshipSupported: false,
230
+ },
231
+ },
232
+ });
233
+ });
234
+
235
+ it('should not autogenerate forms if feature flag is not enabled', async () => {
236
+ isDataStoreEnabledMocked.mockResolvedValue(true);
237
+ getMetadataPromise.mockReturnValue({
238
+ features: {
239
+ autoGenerateForms: 'false',
240
+ autoGenerateViews: 'true',
241
+ formFeatureFlags: {
242
+ isRelationshipSupported: 'false',
243
+ isNonModelSupported: 'false',
244
+ },
245
+ },
246
+ });
247
+ await run(context, 'PostPull');
248
+ expect(mockStartCodegenJob).toHaveBeenCalledWith({
249
+ appId: 'testAppId',
250
+ environmentName: 'testEnvName',
251
+ codegenJobToCreate: {
252
+ renderConfig: {
253
+ react: {
254
+ module: 'es2020',
255
+ target: 'es2020',
256
+ script: 'jsx',
257
+ renderTypeDeclarations: true,
258
+ },
259
+ },
260
+ genericDataSchema: undefined,
261
+ autoGenerateForms: false,
262
+ features: {
263
+ isNonModelSupported: false,
264
+ isRelationshipSupported: false,
265
+ },
266
+ },
267
+ });
268
+ });
97
269
  });
@@ -1,10 +1,9 @@
1
1
  import { printer } from '@aws-amplify/amplify-prompts'; // eslint-disable-line import/no-extraneous-dependencies
2
- import { mocked } from 'ts-jest/utils';
3
2
  import { getAmplifyDataSchema } from '../commands/utils';
4
3
 
5
4
  jest.mock('@aws-amplify/amplify-prompts');
6
5
 
7
- const printerMock = mocked(printer);
6
+ const printerMock = jest.mocked(printer);
8
7
 
9
8
  const sampleIntrospectionSchema = {
10
9
  version: 1,
@@ -4,15 +4,21 @@ import { Form } from 'aws-sdk/clients/amplifyuibuilder'; // eslint-disable-line
4
4
  import { printer } from '@aws-amplify/amplify-prompts'; // eslint-disable-line import/no-extraneous-dependencies
5
5
  import * as utils from '../commands/utils';
6
6
  import { prePushHandler } from '../utils/prePushHandler';
7
+ import { isDataStoreEnabled } from '@aws-amplify/amplify-category-api';
7
8
 
8
9
  jest.mock('../commands/utils');
9
10
  jest.mock('@aws-amplify/amplify-cli-core');
10
11
  jest.mock('../commands/utils/featureFlags', () => ({
11
12
  getTransformerVersion: jest.fn().mockImplementation(() => 2),
12
13
  }));
14
+ jest.mock('@aws-amplify/amplify-category-api', () => ({
15
+ ...jest.requireActual('@aws-amplify/amplify-category-api'),
16
+ isDataStoreEnabled: jest.fn(),
17
+ }));
13
18
 
14
19
  const awsMock = aws as any;
15
20
  const utilsMock = utils as any;
21
+ const isDataStoreEnabledMocked = jest.mocked(isDataStoreEnabled);
16
22
 
17
23
  utilsMock.shouldRenderComponents = jest.fn().mockImplementation(() => true);
18
24
 
@@ -22,6 +28,7 @@ describe('handlePrePush', () => {
22
28
  let exportedForms: Form[];
23
29
 
24
30
  beforeEach(() => {
31
+ isDataStoreEnabledMocked.mockResolvedValue(true);
25
32
  context = {
26
33
  amplify: {
27
34
  invokePluginMethod: jest.fn().mockResolvedValue({ models: { Comment: {} } }),
@@ -1,8 +1,11 @@
1
- import { StudioSchema } from '@aws-amplify/codegen-ui';
2
1
  import { hasStorageField } from '../commands/utils';
2
+ import { Form } from 'aws-sdk/clients/amplifyuibuilder';
3
3
 
4
- const formWithNoCustomization: StudioSchema = {
4
+ const formWithNoCustomization: Form = {
5
5
  id: 'f-123456',
6
+ appId: '123',
7
+ environmentName: 'staging',
8
+ schemaVersion: '1.0',
6
9
  name: 'BlogCreateForm',
7
10
  formActionType: 'create',
8
11
  dataType: {
@@ -15,7 +18,7 @@ const formWithNoCustomization: StudioSchema = {
15
18
  cta: {},
16
19
  };
17
20
 
18
- const formWithNoStorageField: StudioSchema = {
21
+ const formWithNoStorageField: Form = {
19
22
  ...formWithNoCustomization,
20
23
  dataType: { dataSourceType: 'Custom', dataTypeName: 'Blog' },
21
24
  fields: {
@@ -23,7 +26,7 @@ const formWithNoStorageField: StudioSchema = {
23
26
  },
24
27
  };
25
28
 
26
- const formWithStorageField: StudioSchema = {
29
+ const formWithStorageField: Form = {
27
30
  ...formWithNoCustomization,
28
31
  dataType: { dataSourceType: 'Custom', dataTypeName: 'Blog' },
29
32
  fields: {
@@ -1,17 +1,19 @@
1
- /* eslint-disable spellcheck/spell-checker */
2
- import { AmplifyCategories, AmplifySupportedService, stateManager } from '@aws-amplify/amplify-cli-core'; // eslint-disable-line import/no-extraneous-dependencies
3
- import aws from 'aws-sdk'; // eslint-disable-line import/no-extraneous-dependencies
1
+ import { AmplifyCategories, AmplifySupportedService, stateManager } from '@aws-amplify/amplify-cli-core';
2
+ import aws from 'aws-sdk';
4
3
  import {
5
- generateUiBuilderComponents,
6
- generateUiBuilderThemes,
7
4
  getEnvName,
8
5
  getAppId,
9
6
  resolveAppId,
10
- generateUiBuilderForms,
7
+ mapGenericDataSchemaToCodegen,
8
+ waitForSucceededJob,
9
+ extractUIComponents,
10
+ fetchWithRetries,
11
11
  } from '../commands/utils';
12
12
  import { AmplifyStudioClient } from '../clients';
13
- import * as createUiBuilderComponentDependency from '../commands/utils/codegenResources';
14
- import { exampleSchema } from './utils';
13
+ import { isDataStoreEnabled } from '@aws-amplify/amplify-category-api';
14
+ import type { GenericDataSchema } from '@aws-amplify/codegen-ui';
15
+ import fetch, { Response } from 'node-fetch';
16
+ import { existsSync, writeFileSync } from 'fs';
15
17
 
16
18
  jest.mock('@aws-amplify/amplify-cli-core', () => ({
17
19
  ...jest.requireActual('@aws-amplify/amplify-cli-core'),
@@ -23,13 +25,57 @@ jest.mock('@aws-amplify/amplify-cli-core', () => ({
23
25
  jest.mock('../commands/utils/featureFlags', () => ({
24
26
  getTransformerVersion: jest.fn().mockReturnValue(2),
25
27
  }));
28
+ jest.mock('@aws-amplify/amplify-category-api', () => ({
29
+ ...jest.requireActual('@aws-amplify/amplify-category-api'),
30
+ isDataStoreEnabled: jest.fn(),
31
+ }));
32
+ jest.mock('fs');
33
+ jest.mock('node-fetch');
26
34
 
27
35
  const awsMock = aws as any;
28
36
  const stateManagerMock = stateManager as any;
29
- const createUiBuilderComponentDependencyMock = createUiBuilderComponentDependency as any;
37
+ const isDataStoreEnabledMocked = jest.mocked(isDataStoreEnabled);
38
+ const mockWriteFileSync = jest.mocked(writeFileSync);
39
+ const mockExistsSync = jest.mocked(existsSync);
40
+ const mockNodeFetch = jest.mocked(fetch);
41
+
30
42
  describe('should sync amplify ui builder components', () => {
31
43
  let context: any;
44
+ const env = process.env;
45
+
46
+ const mockGenericDataSchema: GenericDataSchema = {
47
+ dataSourceType: 'DataStore',
48
+ models: {
49
+ Blog: {
50
+ fields: {
51
+ id: { dataType: 'ID', required: true, readOnly: false, isArray: false },
52
+ },
53
+ primaryKeys: ['id'],
54
+ },
55
+ },
56
+ enums: {
57
+ Status: { values: ['ENABLED', 'DISABLED', 'HIDDEN'] },
58
+ },
59
+ nonModels: {
60
+ Metadata: {
61
+ fields: {
62
+ name: {
63
+ dataType: 'String',
64
+ required: false,
65
+ readOnly: false,
66
+ isArray: false,
67
+ },
68
+ },
69
+ },
70
+ },
71
+ };
72
+
32
73
  beforeEach(() => {
74
+ mockNodeFetch.mockReset();
75
+ process.env = { ...env };
76
+
77
+ isDataStoreEnabledMocked.mockResolvedValue(true);
78
+ mockExistsSync.mockReturnValue(false);
33
79
  context = {
34
80
  exeInfo: {
35
81
  projectConfig: {
@@ -147,29 +193,10 @@ describe('should sync amplify ui builder components', () => {
147
193
  })),
148
194
  })),
149
195
  }));
150
-
151
- createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn();
152
- createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn();
153
- createUiBuilderComponentDependencyMock.createUiBuilderForm = jest.fn();
154
- createUiBuilderComponentDependencyMock.createUiBuilderView = jest.fn();
155
- });
156
-
157
- it('pulls components from aws-sdk and passes them to createUiBuilderComponent', () => {
158
- generateUiBuilderComponents(context, []);
159
196
  });
160
197
 
161
- it('does not throw an error when createUiBuilderComponent fails', () => {
162
- createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn(() => {
163
- throw new Error('ahhh!'); // eslint-disable-line spellcheck/spell-checker
164
- });
165
- expect(async () => generateUiBuilderComponents(context, [])).not.toThrow();
166
- });
167
-
168
- it('does not throw an error when createUiBuilderThemes fails', () => {
169
- createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn(() => {
170
- throw new Error('ahhh!'); // eslint-disable-line spellcheck/spell-checker
171
- });
172
- expect(async () => generateUiBuilderThemes(context, [])).not.toThrow();
198
+ afterEach(() => {
199
+ process.env = env;
173
200
  });
174
201
 
175
202
  it('can getAmplifyUIBuilderService', async () => {
@@ -190,110 +217,94 @@ describe('should sync amplify ui builder components', () => {
190
217
  }),
191
218
  );
192
219
  });
220
+
193
221
  it('can list components', async () => {
194
222
  const client = await AmplifyStudioClient.setClientInfo(context);
195
223
  const components = await client.listComponents();
196
224
  expect(components.entities).toHaveLength(1);
197
225
  });
226
+
198
227
  it('can list themes', async () => {
199
228
  const client = await AmplifyStudioClient.setClientInfo(context);
200
229
  const themes = await client.listThemes();
201
230
  expect(themes.entities).toHaveLength(1);
202
231
  });
232
+
203
233
  it('can list forms', async () => {
204
234
  const client = await AmplifyStudioClient.setClientInfo(context);
205
235
  const forms = await client.listForms();
206
236
  expect(forms.entities).toHaveLength(1);
207
237
  });
238
+
208
239
  it('can getAppId', async () => {
209
240
  const appId = getAppId(context);
210
241
  expect(appId).toBe('testAppId');
211
242
  });
243
+
212
244
  it('can getEnvName', () => {
213
245
  const envName = getEnvName(context);
214
246
  expect(envName).toBe('testEnvName');
215
247
  });
248
+
216
249
  it('can resolveAppId', async () => {
217
250
  const appId = resolveAppId();
218
251
  expect(appId).toBe('testAppId');
219
252
  });
253
+
220
254
  it('can throw on getAppId', async () => {
221
255
  context.input.options.appId = null;
222
256
  context.amplify.invokePluginMethod = () => null;
223
257
  stateManagerMock.getMeta = () => ({});
224
258
  expect(() => getAppId(context)).toThrowError();
225
259
  });
226
- it('can generate ui builder components', async () => {
227
- createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn().mockImplementation(() => ({}));
228
- const components = generateUiBuilderComponents(context, [{}, {}]);
229
- expect(components.every((component) => component.resultType === 'SUCCESS')).toBeTruthy();
230
- });
231
- it('can handle failed generation generate ui builder components', async () => {
232
- createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn().mockImplementation(() => {
233
- throw new Error('ahh!');
234
- });
235
- const components = generateUiBuilderComponents(context, [{}, {}]);
236
- expect(components.every((component) => component.resultType === 'FAILURE')).toBeTruthy();
237
- });
238
- it('can generate ui builder themes', async () => {
239
- createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => ({}));
240
- const themes = generateUiBuilderThemes(context, [{}, {}]);
241
- expect(themes.every((theme) => theme.resultType === 'SUCCESS')).toBeTruthy();
242
- });
243
- it('can handle failed generation generate ui builder themes', async () => {
244
- createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => {
245
- throw new Error('ahh!');
246
- });
247
- const themes = generateUiBuilderThemes(context, [{}, {}]);
248
- expect(themes.every((theme) => theme.resultType === 'FAILURE')).toBeTruthy();
249
- });
250
- it('can generate ui builder default theme when no themes are passed', async () => {
251
- createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => ({}));
252
- const themes = generateUiBuilderThemes(context, []);
253
- expect(themes.every((theme) => theme.resultType === 'SUCCESS')).toBeTruthy();
254
- });
255
- it('can handle failed generation generate ui builder default theme when no themes are passed', async () => {
256
- createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => {
257
- throw new Error('ahh!');
258
- });
259
- const themes = generateUiBuilderThemes(context, []);
260
- expect(themes.every((theme) => theme.resultType === 'FAILURE')).toBeTruthy();
260
+
261
+ it('can map generic data schema to start codegen job request', () => {
262
+ expect(mapGenericDataSchemaToCodegen(mockGenericDataSchema)).toMatchSnapshot();
261
263
  });
262
- it('can generate ui builder forms', async () => {
263
- createUiBuilderComponentDependencyMock.createUiBuilderForm = jest.fn().mockImplementation(() => ({}));
264
- const forms = generateUiBuilderForms(context, [{}, {}]);
265
- expect(forms.every((form) => form.resultType === 'SUCCESS')).toBeTruthy();
264
+
265
+ it('can wait for a succeeded job', async () => {
266
+ const succeededJob = { status: 'succeeded' };
267
+ const getJob = jest.fn().mockResolvedValueOnce({ status: 'in_progress' }).mockResolvedValueOnce(succeededJob);
268
+ const job = await waitForSucceededJob(getJob, { pollInterval: 1 });
269
+ expect(job).toEqual(succeededJob);
266
270
  });
267
- it('can handle failed generation generate ui builder forms', async () => {
268
- createUiBuilderComponentDependencyMock.createUiBuilderForm = jest.fn().mockImplementation(() => {
269
- throw new Error('ahh!');
270
- });
271
- const forms = generateUiBuilderForms(context, [{}, {}]);
272
- expect(forms.every((form) => form.resultType === 'FAILURE')).toBeTruthy();
271
+
272
+ it('can throw if job failed', async () => {
273
+ const statusMessage = 'failed status';
274
+ const getJob = jest.fn().mockResolvedValueOnce({ status: 'in_progress' }).mockResolvedValueOnce({ status: 'failed', statusMessage });
275
+ await expect(waitForSucceededJob(getJob, { pollInterval: 2 })).rejects.toThrow(statusMessage);
273
276
  });
274
- it('can generate uibuilder forms from data schema if autogenerate true', async () => {
275
- createUiBuilderComponentDependencyMock.createUiBuilderForm = jest.fn().mockImplementation(() => ({}));
276
- const forms = generateUiBuilderForms(context, [], exampleSchema, true);
277
- expect(forms.every((form) => form.resultType === 'SUCCESS')).toBeTruthy();
278
- // create & update form for author model
279
- expect(forms.length).toEqual(2);
277
+
278
+ it('can throw after timeout when waiting for a succeeded job', async () => {
279
+ process.env.UI_BUILDER_CODEGENJOB_TIMEOUT = '1';
280
+ const succeededJob = { status: 'succeeded' };
281
+ const getJob = jest.fn().mockResolvedValueOnce({ status: 'in_progress' }).mockResolvedValueOnce(succeededJob);
282
+ await expect(waitForSucceededJob(getJob, { pollInterval: 2 })).rejects.toThrow('Failed to return codegen job');
280
283
  });
281
284
 
282
- it('should not autogenerate forms for join tables or unsupported models', async () => {
283
- expect(Object.keys(exampleSchema.models)).toStrictEqual(['Author', 'JoinTable', 'EmptyModel']);
284
- createUiBuilderComponentDependencyMock.createUiBuilderForm = jest.fn().mockImplementation(
285
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
286
- (_ctx, schema, _dataSchema) => ({ name: schema.dataType.dataTypeName }),
287
- );
288
- const forms = generateUiBuilderForms(context, [], exampleSchema, true);
289
- expect(forms.every((form) => form.resultType === 'SUCCESS')).toBeTruthy();
290
- // only create & update form for author model
291
- expect(forms.map((f) => (f.schema as any).name)).toStrictEqual(['Author', 'Author']);
285
+ it('can extract ui components from archive', async () => {
286
+ const mockedFetchResponse = {
287
+ ok: true,
288
+ json: jest.fn().mockResolvedValue({
289
+ Output: [
290
+ {
291
+ downloadUrl: 'https://s3.com',
292
+ fileName: 'MyComponent.jsx',
293
+ schemaName: 'MyComponent',
294
+ },
295
+ ],
296
+ }),
297
+ text: jest.fn().mockResolvedValue('source code'),
298
+ } as unknown as Response;
299
+ mockNodeFetch.mockResolvedValue(mockedFetchResponse);
300
+ mockExistsSync.mockReturnValue(true);
301
+ await extractUIComponents('https:://example.aws', 'tmp/ui-components');
302
+ expect(mockWriteFileSync).toHaveBeenCalledWith('tmp/ui-components/MyComponent.jsx', 'source code');
292
303
  });
293
304
 
294
- it('does not generate uibuilder forms from data schema if autogenerate false', async () => {
295
- createUiBuilderComponentDependencyMock.createUiBuilderForm = jest.fn().mockImplementation(() => ({}));
296
- const forms = generateUiBuilderForms(context, [], exampleSchema, false);
297
- expect(forms.length).toEqual(0);
305
+ it('fetchWithRetries retries 3 times', async () => {
306
+ mockNodeFetch.mockRejectedValue(new Error('Server dropped the connection'));
307
+ await expect(fetchWithRetries('https://amazon.com')).rejects.toThrowError('Fetch reached max number of retries without succeeding');
308
+ expect(mockNodeFetch).toHaveBeenCalledTimes(3);
298
309
  });
299
310
  });