@strapi/strapi 4.10.0 → 4.10.1-experimental.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 (126) hide show
  1. package/coverage/clover.xml +1613 -0
  2. package/coverage/coverage-final.json +48 -0
  3. package/coverage/lcov-report/base.css +224 -0
  4. package/coverage/lcov-report/block-navigation.js +87 -0
  5. package/coverage/lcov-report/commands/__tests__/data-transfer/shared/index.html +116 -0
  6. package/coverage/lcov-report/commands/__tests__/data-transfer/shared/transfer.test.utils.js.html +133 -0
  7. package/coverage/lcov-report/commands/admin-create.js.html +424 -0
  8. package/coverage/lcov-report/commands/admin-reset.js.html +241 -0
  9. package/coverage/lcov-report/commands/generate-template.js.html +373 -0
  10. package/coverage/lcov-report/commands/index.html +146 -0
  11. package/coverage/lcov-report/commands/transfer/export.js.html +619 -0
  12. package/coverage/lcov-report/commands/transfer/import.js.html +562 -0
  13. package/coverage/lcov-report/commands/transfer/index.html +146 -0
  14. package/coverage/lcov-report/commands/transfer/transfer.js.html +532 -0
  15. package/coverage/lcov-report/commands/utils/helpers.js.html +430 -0
  16. package/coverage/lcov-report/commands/utils/index.html +116 -0
  17. package/coverage/lcov-report/core/registries/custom-fields.js.html +301 -0
  18. package/coverage/lcov-report/core/registries/index.html +116 -0
  19. package/coverage/lcov-report/core-api/controller/collection-type.js.html +418 -0
  20. package/coverage/lcov-report/core-api/controller/index.html +161 -0
  21. package/coverage/lcov-report/core-api/controller/index.js.html +220 -0
  22. package/coverage/lcov-report/core-api/controller/single-type.js.html +274 -0
  23. package/coverage/lcov-report/core-api/controller/transform.js.html +376 -0
  24. package/coverage/lcov-report/core-api/service/collection-type.js.html +325 -0
  25. package/coverage/lcov-report/core-api/service/index.html +161 -0
  26. package/coverage/lcov-report/core-api/service/index.js.html +220 -0
  27. package/coverage/lcov-report/core-api/service/pagination.js.html +460 -0
  28. package/coverage/lcov-report/core-api/service/single-type.js.html +301 -0
  29. package/coverage/lcov-report/favicon.png +0 -0
  30. package/coverage/lcov-report/index.html +386 -0
  31. package/coverage/lcov-report/load/filepath-to-prop-path.js.html +151 -0
  32. package/coverage/lcov-report/load/index.html +116 -0
  33. package/coverage/lcov-report/prettify.css +1 -0
  34. package/coverage/lcov-report/prettify.js +2 -0
  35. package/coverage/lcov-report/services/content-api/index.html +116 -0
  36. package/coverage/lcov-report/services/content-api/index.js.html +307 -0
  37. package/coverage/lcov-report/services/content-api/permissions/engine.js.html +100 -0
  38. package/coverage/lcov-report/services/content-api/permissions/index.html +131 -0
  39. package/coverage/lcov-report/services/content-api/permissions/index.js.html +529 -0
  40. package/coverage/lcov-report/services/content-api/permissions/providers/action.js.html +142 -0
  41. package/coverage/lcov-report/services/content-api/permissions/providers/condition.js.html +142 -0
  42. package/coverage/lcov-report/services/content-api/permissions/providers/index.html +146 -0
  43. package/coverage/lcov-report/services/content-api/permissions/providers/index.js.html +112 -0
  44. package/coverage/lcov-report/services/core-store.js.html +520 -0
  45. package/coverage/lcov-report/services/entity-service/attributes/index.html +131 -0
  46. package/coverage/lcov-report/services/entity-service/attributes/index.js.html +178 -0
  47. package/coverage/lcov-report/services/entity-service/attributes/transforms.js.html +145 -0
  48. package/coverage/lcov-report/services/entity-service/components.js.html +1246 -0
  49. package/coverage/lcov-report/services/entity-service/index.html +146 -0
  50. package/coverage/lcov-report/services/entity-service/index.js.html +1120 -0
  51. package/coverage/lcov-report/services/entity-service/params.js.html +112 -0
  52. package/coverage/lcov-report/services/entity-validator/__tests__/relations/utils/index.html +116 -0
  53. package/coverage/lcov-report/services/entity-validator/__tests__/relations/utils/relations.testdata.js.html +544 -0
  54. package/coverage/lcov-report/services/entity-validator/index.html +131 -0
  55. package/coverage/lcov-report/services/entity-validator/index.js.html +1231 -0
  56. package/coverage/lcov-report/services/entity-validator/validators.js.html +733 -0
  57. package/coverage/lcov-report/services/event-hub.js.html +319 -0
  58. package/coverage/lcov-report/services/fs.js.html +259 -0
  59. package/coverage/lcov-report/services/index.html +161 -0
  60. package/coverage/lcov-report/services/metrics/admin-user-hash.js.html +148 -0
  61. package/coverage/lcov-report/services/metrics/index.html +206 -0
  62. package/coverage/lcov-report/services/metrics/index.js.html +265 -0
  63. package/coverage/lcov-report/services/metrics/is-truthy.js.html +112 -0
  64. package/coverage/lcov-report/services/metrics/middleware.js.html +184 -0
  65. package/coverage/lcov-report/services/metrics/rate-limiter.js.html +166 -0
  66. package/coverage/lcov-report/services/metrics/sender.js.html +394 -0
  67. package/coverage/lcov-report/services/metrics/stringify-deep.js.html +151 -0
  68. package/coverage/lcov-report/services/utils/index.html +116 -0
  69. package/coverage/lcov-report/services/utils/upload-files.js.html +322 -0
  70. package/coverage/lcov-report/services/worker-queue.js.html +262 -0
  71. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  72. package/coverage/lcov-report/sorter.js +196 -0
  73. package/coverage/lcov-report/utils/convert-custom-field-type.js.html +151 -0
  74. package/coverage/lcov-report/utils/index.html +146 -0
  75. package/coverage/lcov-report/utils/machine-id.js.html +127 -0
  76. package/coverage/lcov-report/utils/url-from-segments.js.html +121 -0
  77. package/lib/commands/__tests__/commands.test.js +20 -0
  78. package/lib/commands/__tests__/commands.test.utils.js +16 -0
  79. package/lib/commands/actions/admin/create-user/__tests__/admin.create-user.test.js +450 -0
  80. package/lib/commands/actions/admin/reset-user-password/__tests__/admin.reset-user-password.test.js +145 -0
  81. package/lib/commands/actions/build/action.js +18 -0
  82. package/lib/commands/actions/build/command.js +15 -0
  83. package/lib/commands/actions/export/__tests__/export.test.js +175 -0
  84. package/lib/commands/actions/import/__tests__/import.test.js +143 -0
  85. package/lib/commands/actions/templates/generate/__tests__/templates.generate.js +118 -0
  86. package/lib/commands/actions/transfer/__tests__/transfer.test.js +178 -0
  87. package/lib/core/registries/__tests__/custom-fields.test.js +152 -0
  88. package/lib/core-api/__tests__/controller.test.js +39 -0
  89. package/lib/core-api/controller/__tests__/transform.test.js +226 -0
  90. package/lib/core-api/service/__tests__/index.test.js +127 -0
  91. package/lib/core-api/service/__tests__/pagination.test.js +275 -0
  92. package/lib/load/__tests__/filepath-to-prop-path.test.js +30 -0
  93. package/lib/middlewares/__tests__/errors.test.js +21 -0
  94. package/lib/services/__tests__/content-api-permissions.test.js +291 -0
  95. package/lib/services/__tests__/core-store.test.js +148 -0
  96. package/lib/services/__tests__/event-hub.test.js +126 -0
  97. package/lib/services/__tests__/fs.test.js +78 -0
  98. package/lib/services/__tests__/worker-queue.test.js +47 -0
  99. package/lib/services/entity-service/__tests__/entity-service-events.test.js +117 -0
  100. package/lib/services/entity-service/__tests__/entity-service.test.js +587 -0
  101. package/lib/services/entity-validator/__tests__/biginteger-validators.test.js +220 -0
  102. package/lib/services/entity-validator/__tests__/date-validators.test.js +183 -0
  103. package/lib/services/entity-validator/__tests__/datetime-validators.test.js +183 -0
  104. package/lib/services/entity-validator/__tests__/email-validators.test.js +56 -0
  105. package/lib/services/entity-validator/__tests__/enumeration-validators.test.js +43 -0
  106. package/lib/services/entity-validator/__tests__/float-validators.test.js +278 -0
  107. package/lib/services/entity-validator/__tests__/index.test.js +609 -0
  108. package/lib/services/entity-validator/__tests__/integer-validators.test.js +278 -0
  109. package/lib/services/entity-validator/__tests__/relations/attribute-level.test.js +123 -0
  110. package/lib/services/entity-validator/__tests__/relations/component-level.test.js +275 -0
  111. package/lib/services/entity-validator/__tests__/relations/dynamic-zone-level.test.js +159 -0
  112. package/lib/services/entity-validator/__tests__/relations/media-level.test.js +74 -0
  113. package/lib/services/entity-validator/__tests__/relations/utils/relations.testdata.js +153 -0
  114. package/lib/services/entity-validator/__tests__/string-validators.test.js +374 -0
  115. package/lib/services/entity-validator/__tests__/time-validators.test.js +183 -0
  116. package/lib/services/entity-validator/__tests__/timestamp-validators.test.js +204 -0
  117. package/lib/services/entity-validator/__tests__/uid-validators.test.js +229 -0
  118. package/lib/services/metrics/__tests__/admin-user-hash.test.js +41 -0
  119. package/lib/services/metrics/__tests__/index.test.js +157 -0
  120. package/lib/services/metrics/__tests__/is-truthy.js +33 -0
  121. package/lib/services/metrics/__tests__/middleware.test.js +60 -0
  122. package/lib/services/metrics/__tests__/rate-limiter.test.js +50 -0
  123. package/lib/services/metrics/__tests__/stringify-deep.test.js +27 -0
  124. package/lib/utils/__tests__/convert-custom-field-type.test.js +69 -0
  125. package/lib/utils/__tests__/url-from-segments.test.js +40 -0
  126. package/package.json +15 -15
@@ -0,0 +1,175 @@
1
+ 'use strict';
2
+
3
+ const { expectExit } = require('../../../__tests__/commands.test.utils');
4
+
5
+ describe('Export', () => {
6
+ const defaultFileName = 'defaultFilename';
7
+
8
+ jest.mock('fs-extra', () => ({
9
+ pathExists: jest.fn(() => Promise.resolve(true)),
10
+ }));
11
+
12
+ const mockDataTransfer = {
13
+ file: {
14
+ providers: {
15
+ createLocalFileDestinationProvider: jest.fn().mockReturnValue({ name: 'testDest' }),
16
+ },
17
+ },
18
+ strapi: {
19
+ providers: {
20
+ createLocalStrapiSourceProvider: jest.fn().mockReturnValue({ name: 'testSource' }),
21
+ },
22
+ },
23
+ engine: {
24
+ ...jest.requireActual('@strapi/data-transfer').engine,
25
+ errors: {},
26
+ createTransferEngine() {
27
+ return {
28
+ transfer: jest.fn(() => {
29
+ return {
30
+ engine: {},
31
+ destination: {
32
+ file: {
33
+ path: 'path',
34
+ },
35
+ },
36
+ };
37
+ }),
38
+ progress: {
39
+ on: jest.fn(),
40
+ stream: {
41
+ on: jest.fn(),
42
+ },
43
+ },
44
+ sourceProvider: { name: 'testSource' },
45
+ destinationProvider: { name: 'testDestination' },
46
+ diagnostics: {
47
+ on: jest.fn().mockReturnThis(),
48
+ onDiagnostic: jest.fn().mockReturnThis(),
49
+ },
50
+ };
51
+ },
52
+ },
53
+ };
54
+
55
+ jest.mock('@strapi/data-transfer', () => mockDataTransfer);
56
+
57
+ // command utils
58
+ const mockUtils = {
59
+ getTransferTelemetryPayload: jest.fn().mockReturnValue({}),
60
+ loadersFactory: jest.fn().mockReturnValue({ updateLoader: jest.fn() }),
61
+ formatDiagnostic: jest.fn(),
62
+ createStrapiInstance() {
63
+ return {
64
+ telemetry: {
65
+ send: jest.fn(),
66
+ },
67
+ };
68
+ },
69
+ getDefaultExportName: jest.fn(() => defaultFileName),
70
+ buildTransferTable: jest.fn(() => {
71
+ return {
72
+ toString() {
73
+ return 'table';
74
+ },
75
+ };
76
+ }),
77
+ exitMessageText: jest.fn(),
78
+ };
79
+ jest.mock(
80
+ '../../../utils/data-transfer.js',
81
+ () => {
82
+ return mockUtils;
83
+ },
84
+ { virtual: true }
85
+ );
86
+
87
+ // console spies
88
+ jest.spyOn(console, 'log').mockImplementation(() => {});
89
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
90
+ jest.spyOn(console, 'info').mockImplementation(() => {});
91
+ jest.spyOn(console, 'error').mockImplementation(() => {});
92
+
93
+ // Now that everything is mocked, load the 'export' command
94
+ const exportAction = require('../action');
95
+
96
+ beforeEach(() => {
97
+ jest.clearAllMocks();
98
+ });
99
+
100
+ it('uses path provided by user', async () => {
101
+ const filename = 'test';
102
+
103
+ await expectExit(0, async () => {
104
+ await exportAction({ file: filename });
105
+ });
106
+
107
+ expect(console.error).not.toHaveBeenCalled();
108
+ expect(mockDataTransfer.file.providers.createLocalFileDestinationProvider).toHaveBeenCalledWith(
109
+ expect.objectContaining({
110
+ file: { path: filename },
111
+ })
112
+ );
113
+ expect(mockUtils.getDefaultExportName).not.toHaveBeenCalled();
114
+ });
115
+
116
+ it('uses default path if not provided by user', async () => {
117
+ await expectExit(0, async () => {
118
+ await exportAction({});
119
+ });
120
+
121
+ expect(mockUtils.getDefaultExportName).toHaveBeenCalledTimes(1);
122
+ expect(mockDataTransfer.file.providers.createLocalFileDestinationProvider).toHaveBeenCalledWith(
123
+ expect.objectContaining({
124
+ file: { path: defaultFileName },
125
+ })
126
+ );
127
+ });
128
+
129
+ it('encrypts the output file if specified', async () => {
130
+ const encrypt = true;
131
+ await expectExit(0, async () => {
132
+ await exportAction({ encrypt });
133
+ });
134
+
135
+ expect(mockDataTransfer.file.providers.createLocalFileDestinationProvider).toHaveBeenCalledWith(
136
+ expect.objectContaining({
137
+ encryption: { enabled: encrypt },
138
+ })
139
+ );
140
+ });
141
+
142
+ it('encrypts the output file with the given key', async () => {
143
+ const key = 'secret-key';
144
+ const encrypt = true;
145
+ await expectExit(0, async () => {
146
+ await exportAction({ encrypt, key });
147
+ });
148
+
149
+ expect(mockDataTransfer.file.providers.createLocalFileDestinationProvider).toHaveBeenCalledWith(
150
+ expect.objectContaining({
151
+ encryption: { enabled: encrypt, key },
152
+ })
153
+ );
154
+ });
155
+
156
+ it('uses compress option', async () => {
157
+ await expectExit(0, async () => {
158
+ await exportAction({ compress: false });
159
+ });
160
+
161
+ expect(mockDataTransfer.file.providers.createLocalFileDestinationProvider).toHaveBeenCalledWith(
162
+ expect.objectContaining({
163
+ compression: { enabled: false },
164
+ })
165
+ );
166
+ await expectExit(0, async () => {
167
+ await exportAction({ compress: true });
168
+ });
169
+ expect(mockDataTransfer.file.providers.createLocalFileDestinationProvider).toHaveBeenCalledWith(
170
+ expect.objectContaining({
171
+ compression: { enabled: true },
172
+ })
173
+ );
174
+ });
175
+ });
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ strapi: {
5
+ providers: { DEFAULT_CONFLICT_STRATEGY },
6
+ },
7
+ engine: { DEFAULT_SCHEMA_STRATEGY, DEFAULT_VERSION_STRATEGY },
8
+ } = require('@strapi/data-transfer');
9
+
10
+ const { expectExit } = require('../../../__tests__/commands.test.utils');
11
+
12
+ const createTransferEngine = jest.fn(() => {
13
+ return {
14
+ transfer: jest.fn(() => {
15
+ return {
16
+ engine: {},
17
+ };
18
+ }),
19
+ progress: {
20
+ on: jest.fn(),
21
+ stream: {
22
+ on: jest.fn(),
23
+ },
24
+ },
25
+ sourceProvider: { name: 'testFileSource', type: 'source', getMetadata: jest.fn() },
26
+ destinationProvider: {
27
+ name: 'testStrapiDest',
28
+ type: 'destination',
29
+ getMetadata: jest.fn(),
30
+ },
31
+ diagnostics: {
32
+ on: jest.fn().mockReturnThis(),
33
+ onDiagnostic: jest.fn().mockReturnThis(),
34
+ },
35
+ };
36
+ });
37
+
38
+ describe('Import', () => {
39
+ const mockDataTransfer = {
40
+ file: {
41
+ providers: {
42
+ createLocalFileSourceProvider: jest
43
+ .fn()
44
+ .mockReturnValue({ name: 'testFileSource', type: 'source', getMetadata: jest.fn() }),
45
+ },
46
+ },
47
+ strapi: {
48
+ providers: {
49
+ DEFAULT_CONFLICT_STRATEGY,
50
+ createLocalStrapiDestinationProvider: jest
51
+ .fn()
52
+ .mockReturnValue({ name: 'testStrapiDest', type: 'destination', getMetadata: jest.fn() }),
53
+ },
54
+ },
55
+ engine: {
56
+ ...jest.requireActual('@strapi/data-transfer').engine,
57
+ DEFAULT_SCHEMA_STRATEGY,
58
+ DEFAULT_VERSION_STRATEGY,
59
+ createTransferEngine,
60
+ },
61
+ };
62
+
63
+ jest.mock('@strapi/data-transfer', () => mockDataTransfer);
64
+
65
+ // command utils
66
+ const mockUtils = {
67
+ getTransferTelemetryPayload: jest.fn().mockReturnValue({}),
68
+ loadersFactory: jest.fn().mockReturnValue({ updateLoader: jest.fn() }),
69
+ formatDiagnostic: jest.fn(),
70
+ createStrapiInstance: jest.fn().mockReturnValue({
71
+ telemetry: {
72
+ send: jest.fn(),
73
+ },
74
+ destroy: jest.fn(),
75
+ }),
76
+ buildTransferTable: jest.fn(() => {
77
+ return {
78
+ toString() {
79
+ return 'table';
80
+ },
81
+ };
82
+ }),
83
+ exitMessageText: jest.fn(),
84
+ };
85
+ jest.mock(
86
+ '../../../utils/data-transfer.js',
87
+ () => {
88
+ return mockUtils;
89
+ },
90
+ { virtual: true }
91
+ );
92
+
93
+ // console spies
94
+ jest.spyOn(console, 'log').mockImplementation(() => {});
95
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
96
+ jest.spyOn(console, 'info').mockImplementation(() => {});
97
+ jest.spyOn(console, 'error').mockImplementation(() => {});
98
+
99
+ // Now that everything is mocked, load the 'import' command
100
+ const importAction = require('../action');
101
+
102
+ beforeEach(() => {
103
+ jest.clearAllMocks();
104
+ });
105
+
106
+ it('creates providers with correct options ', async () => {
107
+ const options = {
108
+ file: 'test.tar.gz.enc',
109
+ decrypt: true,
110
+ decompress: true,
111
+ exclude: [],
112
+ only: [],
113
+ };
114
+
115
+ await expectExit(0, async () => {
116
+ await importAction(options);
117
+ });
118
+
119
+ // strapi options
120
+ expect(
121
+ mockDataTransfer.strapi.providers.createLocalStrapiDestinationProvider
122
+ ).toHaveBeenCalledWith(expect.objectContaining({ strategy: DEFAULT_CONFLICT_STRATEGY }));
123
+
124
+ // file options
125
+ expect(mockDataTransfer.file.providers.createLocalFileSourceProvider).toHaveBeenCalledWith(
126
+ expect.objectContaining({
127
+ file: { path: 'test.tar.gz.enc' },
128
+ encryption: { enabled: options.decrypt },
129
+ compression: { enabled: options.decompress },
130
+ })
131
+ );
132
+
133
+ // engine options
134
+ expect(mockDataTransfer.engine.createTransferEngine).toHaveBeenCalledWith(
135
+ expect.objectContaining({ name: 'testFileSource' }),
136
+ expect.objectContaining({ name: 'testStrapiDest' }),
137
+ expect.objectContaining({
138
+ schemaStrategy: DEFAULT_SCHEMA_STRATEGY,
139
+ versionStrategy: DEFAULT_VERSION_STRATEGY,
140
+ })
141
+ );
142
+ });
143
+ });
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ jest.mock('fs-extra', () => ({
4
+ ensureDir: jest.fn(() => Promise.resolve()),
5
+ copy: jest.fn(() => Promise.resolve()),
6
+ pathExists: jest.fn(() => Promise.resolve()),
7
+ writeJSON: jest.fn(() => Promise.resolve()),
8
+ }));
9
+
10
+ const { resolve, join } = require('path');
11
+ const fse = require('fs-extra');
12
+ const inquirer = require('inquirer');
13
+
14
+ const exportTemplate = require('../action');
15
+
16
+ describe('templates:generate command', () => {
17
+ beforeEach(() => {
18
+ jest.spyOn(console, 'log').mockImplementation(() => {});
19
+ jest.clearAllMocks();
20
+ });
21
+
22
+ it('creates a new template directory', async () => {
23
+ fse.pathExists.mockReturnValue(false);
24
+ const directory = '../test-dir';
25
+ const rootPath = resolve(directory);
26
+ const templatePath = join(rootPath, 'template');
27
+
28
+ await exportTemplate(directory);
29
+
30
+ expect(fse.pathExists).toHaveBeenCalledWith(templatePath);
31
+ expect(fse.ensureDir).toHaveBeenCalledWith(templatePath);
32
+ });
33
+
34
+ it.each(['src', 'data'])('copies folder %s', async (item) => {
35
+ // Mock the empty directory arg
36
+ fse.pathExists.mockReturnValueOnce(false);
37
+ // Mock the folder exists
38
+ fse.pathExists.mockReturnValue(true);
39
+ const directory = '../test-dir';
40
+ const rootPath = resolve(directory);
41
+ const templatePath = join(rootPath, 'template');
42
+
43
+ await exportTemplate(directory);
44
+
45
+ expect(fse.pathExists).toHaveBeenCalledWith(join(process.cwd(), item));
46
+ expect(fse.copy).toHaveBeenCalledWith(join(process.cwd(), item), join(templatePath, item));
47
+ });
48
+
49
+ it('creates a json config file', async () => {
50
+ fse.pathExists.mockReturnValue(false);
51
+ const directory = '../test-dir';
52
+ const rootPath = resolve(directory);
53
+
54
+ await exportTemplate(directory);
55
+
56
+ expect(fse.pathExists).toHaveBeenCalledWith(join(rootPath, 'template.json'));
57
+ expect(fse.writeJSON).toHaveBeenCalledWith(join(rootPath, 'template.json'), {});
58
+ });
59
+
60
+ describe('handles prompt input', () => {
61
+ it('replaces directory if confirmed', async () => {
62
+ fse.pathExists.mockReturnValue(true);
63
+ const mockInquiry = jest
64
+ .spyOn(inquirer, 'prompt')
65
+ .mockImplementationOnce(() => ({ confirm: true }));
66
+ const directory = '../test-dir';
67
+ const rootPath = resolve(directory);
68
+ const templatePath = join(rootPath, 'template');
69
+
70
+ await exportTemplate(directory);
71
+
72
+ expect(fse.pathExists).toHaveBeenCalledWith(templatePath);
73
+ expect(mockInquiry).toHaveBeenLastCalledWith(
74
+ expect.objectContaining({ message: expect.any(String), name: 'confirm', type: 'confirm' })
75
+ );
76
+ expect(fse.ensureDir).toHaveBeenCalled();
77
+ expect(fse.copy).toHaveBeenCalled();
78
+ });
79
+
80
+ it('does not replace existing config file', async () => {
81
+ fse.pathExists.mockReturnValue(true);
82
+ jest.spyOn(inquirer, 'prompt').mockImplementationOnce(() => ({ confirm: true }));
83
+ const directory = '../test-dir';
84
+ const rootPath = resolve(directory);
85
+
86
+ await exportTemplate(directory);
87
+ expect(fse.pathExists).toHaveBeenCalledWith(join(rootPath, 'template.json'));
88
+ expect(fse.writeJSON).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it('exits if not confirmed', async () => {
92
+ fse.pathExists.mockReturnValue(true);
93
+ jest.spyOn(console, 'error').mockImplementation(() => {});
94
+ const mockInquiry = jest
95
+ .spyOn(inquirer, 'prompt')
96
+ .mockImplementationOnce(() => ({ confirm: false }));
97
+
98
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
99
+ throw new Error('exit');
100
+ });
101
+ const directory = '../test-dir';
102
+ const rootPath = resolve(directory);
103
+ const templatePath = join(rootPath, 'template');
104
+
105
+ await exportTemplate(directory).catch((err) => {
106
+ expect(err).toEqual(new Error('exit'));
107
+ });
108
+
109
+ expect(fse.pathExists).toHaveBeenCalledWith(templatePath);
110
+ expect(mockInquiry).toHaveBeenLastCalledWith(
111
+ expect.objectContaining({ message: expect.any(String), name: 'confirm', type: 'confirm' })
112
+ );
113
+ expect(mockExit).toHaveBeenCalledWith(0);
114
+ expect(fse.ensureDir).not.toHaveBeenCalled();
115
+ expect(fse.copy).not.toHaveBeenCalled();
116
+ });
117
+ });
118
+ });
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ const { expectExit } = require('../../../__tests__/commands.test.utils');
4
+
5
+ describe('Transfer', () => {
6
+ // command utils
7
+ const mockUtils = {
8
+ getTransferTelemetryPayload: jest.fn().mockReturnValue({}),
9
+ loadersFactory: jest.fn().mockReturnValue({ updateLoader: jest.fn() }),
10
+ formatDiagnostic: jest.fn(),
11
+ createStrapiInstance() {
12
+ return {
13
+ telemetry: {
14
+ send: jest.fn(),
15
+ },
16
+ };
17
+ },
18
+ getDefaultExportName: jest.fn(() => 'default'),
19
+ buildTransferTable: jest.fn(() => {
20
+ return {
21
+ toString() {
22
+ return 'table';
23
+ },
24
+ };
25
+ }),
26
+ exitMessageText: jest.fn(),
27
+ };
28
+ jest.mock(
29
+ '../../../utils/data-transfer.js',
30
+ () => {
31
+ return mockUtils;
32
+ },
33
+ { virtual: true }
34
+ );
35
+
36
+ const mockDataTransfer = {
37
+ strapi: {
38
+ providers: {
39
+ createLocalStrapiSourceProvider: jest.fn().mockReturnValue({ name: 'testLocalSource' }),
40
+ createLocalStrapiDestinationProvider: jest
41
+ .fn()
42
+ .mockReturnValue({ name: 'testLocalDestination' }),
43
+ createRemoteStrapiDestinationProvider: jest
44
+ .fn()
45
+ .mockReturnValue({ name: 'testRemoteDest' }),
46
+ },
47
+ },
48
+ engine: {
49
+ ...jest.requireActual('@strapi/data-transfer').engine,
50
+ createTransferEngine() {
51
+ return {
52
+ transfer: jest.fn(() => {
53
+ return {
54
+ engine: {},
55
+ };
56
+ }),
57
+ progress: {
58
+ on: jest.fn(),
59
+ stream: {
60
+ on: jest.fn(),
61
+ },
62
+ },
63
+ sourceProvider: { name: 'testSource' },
64
+ destinationProvider: { name: 'testDestination' },
65
+ diagnostics: {
66
+ on: jest.fn().mockReturnThis(),
67
+ onDiagnostic: jest.fn().mockReturnThis(),
68
+ },
69
+ };
70
+ },
71
+ },
72
+ };
73
+
74
+ jest.mock('@strapi/data-transfer', () => mockDataTransfer);
75
+
76
+ const transferAction = require('../action');
77
+
78
+ // console spies
79
+ jest.spyOn(console, 'log').mockImplementation(() => {});
80
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
81
+ jest.spyOn(console, 'info').mockImplementation(() => {});
82
+ jest.spyOn(console, 'error').mockImplementation(() => {});
83
+
84
+ const destinationUrl = new URL('http://one.localhost/admin');
85
+ const destinationToken = 'test-token';
86
+
87
+ const sourceUrl = new URL('http://two.localhost/admin');
88
+
89
+ beforeEach(() => {
90
+ jest.clearAllMocks();
91
+ });
92
+
93
+ it('exits with error when no --to or --from is provided', async () => {
94
+ await expectExit(1, async () => {
95
+ await transferAction({ from: undefined, to: undefined });
96
+ });
97
+
98
+ expect(console.error).toHaveBeenCalledWith(expect.stringMatching(/one source/i));
99
+
100
+ expect(
101
+ mockDataTransfer.strapi.providers.createRemoteStrapiDestinationProvider
102
+ ).not.toHaveBeenCalled();
103
+ });
104
+
105
+ it('exits with error when both --to and --from are provided', async () => {
106
+ await expectExit(1, async () => {
107
+ await transferAction({ from: sourceUrl, to: destinationUrl });
108
+ });
109
+
110
+ expect(console.error).toHaveBeenCalledWith(expect.stringMatching(/one source/i));
111
+
112
+ expect(
113
+ mockDataTransfer.strapi.providers.createRemoteStrapiDestinationProvider
114
+ ).not.toHaveBeenCalled();
115
+ });
116
+
117
+ describe('--to', () => {
118
+ it('exits with error when auth is not provided', async () => {
119
+ await expectExit(1, async () => {
120
+ await transferAction({ from: undefined, to: destinationUrl });
121
+ });
122
+
123
+ expect(console.error).toHaveBeenCalledWith(expect.stringMatching(/missing token/i));
124
+
125
+ expect(
126
+ mockDataTransfer.strapi.providers.createRemoteStrapiDestinationProvider
127
+ ).not.toHaveBeenCalled();
128
+ });
129
+
130
+ it('uses destination url and token provided by user', async () => {
131
+ await expectExit(0, async () => {
132
+ await transferAction({ from: undefined, to: destinationUrl, toToken: destinationToken });
133
+ });
134
+
135
+ expect(console.error).not.toHaveBeenCalled();
136
+ expect(
137
+ mockDataTransfer.strapi.providers.createRemoteStrapiDestinationProvider
138
+ ).toHaveBeenCalledWith(
139
+ expect.objectContaining({
140
+ url: destinationUrl,
141
+ auth: {
142
+ type: 'token',
143
+ token: destinationToken,
144
+ },
145
+ })
146
+ );
147
+ });
148
+
149
+ it('uses local Strapi source when from is not specified', async () => {
150
+ await expectExit(0, async () => {
151
+ await transferAction({ from: undefined, to: destinationUrl, toToken: destinationToken });
152
+ });
153
+
154
+ expect(console.error).not.toHaveBeenCalled();
155
+ expect(mockDataTransfer.strapi.providers.createLocalStrapiSourceProvider).toHaveBeenCalled();
156
+ expect(
157
+ mockDataTransfer.strapi.providers.createRemoteStrapiDestinationProvider
158
+ ).toHaveBeenCalled();
159
+ });
160
+ });
161
+
162
+ it.todo('uses local Strapi destination when to is not specified');
163
+
164
+ it('uses restore as the default strategy', async () => {
165
+ await expectExit(0, async () => {
166
+ await transferAction({ from: undefined, to: destinationUrl, toToken: destinationToken });
167
+ });
168
+
169
+ expect(console.error).not.toHaveBeenCalled();
170
+ expect(
171
+ mockDataTransfer.strapi.providers.createRemoteStrapiDestinationProvider
172
+ ).toHaveBeenCalledWith(
173
+ expect.objectContaining({
174
+ strategy: 'restore',
175
+ })
176
+ );
177
+ });
178
+ });