@sanity/cli 6.0.0-alpha.3 → 6.0.0-alpha.4

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 (110) hide show
  1. package/dist/actions/auth/login/{index.js → login.js} +1 -1
  2. package/dist/actions/auth/login/{index.js.map → login.js.map} +1 -1
  3. package/dist/actions/organizations/getOrganizationChoices.d.ts +6 -0
  4. package/dist/actions/organizations/getOrganizationChoices.js +23 -0
  5. package/dist/actions/organizations/getOrganizationChoices.js.map +1 -0
  6. package/dist/actions/organizations/getOrganizationsWithAttachGrantInfo.d.ts +2 -0
  7. package/dist/actions/organizations/getOrganizationsWithAttachGrantInfo.js +9 -0
  8. package/dist/actions/organizations/getOrganizationsWithAttachGrantInfo.js.map +1 -0
  9. package/dist/actions/organizations/hasProjectAttachGrant.d.ts +1 -0
  10. package/dist/actions/organizations/hasProjectAttachGrant.js +24 -0
  11. package/dist/actions/organizations/hasProjectAttachGrant.js.map +1 -0
  12. package/dist/actions/schema/utils/schemaStoreValidation.js +2 -2
  13. package/dist/actions/schema/utils/schemaStoreValidation.js.map +1 -1
  14. package/dist/commands/__tests__/init/init.authentication.test.js +60 -0
  15. package/dist/commands/__tests__/init/init.authentication.test.js.map +1 -0
  16. package/dist/commands/__tests__/init/init.create-new-project.test.js +196 -0
  17. package/dist/commands/__tests__/init/init.create-new-project.test.js.map +1 -0
  18. package/dist/commands/__tests__/init/init.plan.test.js +220 -0
  19. package/dist/commands/__tests__/init/init.plan.test.js.map +1 -0
  20. package/dist/commands/__tests__/init/init.setup.test.js +279 -0
  21. package/dist/commands/__tests__/init/init.setup.test.js.map +1 -0
  22. package/dist/commands/__tests__/migration.test.js +119 -0
  23. package/dist/commands/__tests__/migration.test.js.map +1 -0
  24. package/dist/commands/backup/__tests__/download.test.js +3 -3
  25. package/dist/commands/backup/__tests__/download.test.js.map +1 -1
  26. package/dist/commands/dataset/__tests__/import.test.js +2 -2
  27. package/dist/commands/dataset/__tests__/import.test.js.map +1 -1
  28. package/dist/commands/documents/__tests__/query.test.js +3 -3
  29. package/dist/commands/documents/__tests__/query.test.js.map +1 -1
  30. package/dist/commands/init.d.ts +4 -0
  31. package/dist/commands/init.js +151 -18
  32. package/dist/commands/init.js.map +1 -1
  33. package/dist/commands/login.js +1 -1
  34. package/dist/commands/login.js.map +1 -1
  35. package/dist/services/organizations.d.ts +40 -0
  36. package/dist/services/organizations.js +41 -0
  37. package/dist/services/organizations.js.map +1 -0
  38. package/dist/services/projects.d.ts +20 -0
  39. package/dist/services/projects.js +30 -1
  40. package/dist/services/projects.js.map +1 -1
  41. package/dist/services/user.d.ts +2 -0
  42. package/dist/services/user.js +11 -0
  43. package/dist/services/user.js.map +1 -0
  44. package/oclif.config.js +6 -1
  45. package/oclif.manifest.json +33 -184
  46. package/package.json +7 -7
  47. package/dist/actions/migration/getMigrationRootDirectory.d.ts +0 -2
  48. package/dist/actions/migration/getMigrationRootDirectory.js +0 -14
  49. package/dist/actions/migration/getMigrationRootDirectory.js.map +0 -1
  50. package/dist/actions/migration/resolveMigrations.d.ts +0 -19
  51. package/dist/actions/migration/resolveMigrations.js +0 -43
  52. package/dist/actions/migration/resolveMigrations.js.map +0 -1
  53. package/dist/actions/migration/templates/__tests__/minimalAdvanced.test.js +0 -65
  54. package/dist/actions/migration/templates/__tests__/minimalAdvanced.test.js.map +0 -1
  55. package/dist/actions/migration/templates/__tests__/minimalSimple.test.js +0 -145
  56. package/dist/actions/migration/templates/__tests__/minimalSimple.test.js.map +0 -1
  57. package/dist/actions/migration/templates/__tests__/renameField.test.js +0 -63
  58. package/dist/actions/migration/templates/__tests__/renameField.test.js.map +0 -1
  59. package/dist/actions/migration/templates/__tests__/renameType.test.js +0 -61
  60. package/dist/actions/migration/templates/__tests__/renameType.test.js.map +0 -1
  61. package/dist/actions/migration/templates/__tests__/stringToPTE.test.js +0 -87
  62. package/dist/actions/migration/templates/__tests__/stringToPTE.test.js.map +0 -1
  63. package/dist/actions/migration/templates/index.d.ts +0 -5
  64. package/dist/actions/migration/templates/index.js +0 -7
  65. package/dist/actions/migration/templates/index.js.map +0 -1
  66. package/dist/actions/migration/templates/minimalAdvanced.d.ts +0 -4
  67. package/dist/actions/migration/templates/minimalAdvanced.js +0 -21
  68. package/dist/actions/migration/templates/minimalAdvanced.js.map +0 -1
  69. package/dist/actions/migration/templates/minimalSimple.d.ts +0 -4
  70. package/dist/actions/migration/templates/minimalSimple.js +0 -61
  71. package/dist/actions/migration/templates/minimalSimple.js.map +0 -1
  72. package/dist/actions/migration/templates/renameField.d.ts +0 -4
  73. package/dist/actions/migration/templates/renameField.js +0 -20
  74. package/dist/actions/migration/templates/renameField.js.map +0 -1
  75. package/dist/actions/migration/templates/renameType.d.ts +0 -4
  76. package/dist/actions/migration/templates/renameType.js +0 -19
  77. package/dist/actions/migration/templates/renameType.js.map +0 -1
  78. package/dist/actions/migration/templates/stringToPTE.d.ts +0 -4
  79. package/dist/actions/migration/templates/stringToPTE.js +0 -32
  80. package/dist/actions/migration/templates/stringToPTE.js.map +0 -1
  81. package/dist/commands/__tests__/init.test.js +0 -411
  82. package/dist/commands/__tests__/init.test.js.map +0 -1
  83. package/dist/commands/migration/__tests__/create.test.js +0 -296
  84. package/dist/commands/migration/__tests__/create.test.js.map +0 -1
  85. package/dist/commands/migration/__tests__/list.test.js +0 -166
  86. package/dist/commands/migration/__tests__/list.test.js.map +0 -1
  87. package/dist/commands/migration/__tests__/run.test.js +0 -481
  88. package/dist/commands/migration/__tests__/run.test.js.map +0 -1
  89. package/dist/commands/migration/create.d.ts +0 -17
  90. package/dist/commands/migration/create.js +0 -143
  91. package/dist/commands/migration/create.js.map +0 -1
  92. package/dist/commands/migration/list.d.ts +0 -9
  93. package/dist/commands/migration/list.js +0 -61
  94. package/dist/commands/migration/list.js.map +0 -1
  95. package/dist/commands/migration/run.d.ts +0 -26
  96. package/dist/commands/migration/run.js +0 -271
  97. package/dist/commands/migration/run.js.map +0 -1
  98. package/dist/util/migration/constants.d.ts +0 -3
  99. package/dist/util/migration/constants.js +0 -10
  100. package/dist/util/migration/constants.js.map +0 -1
  101. package/dist/util/migration/ensureApiVersionFormat.d.ts +0 -9
  102. package/dist/util/migration/ensureApiVersionFormat.js +0 -16
  103. package/dist/util/migration/ensureApiVersionFormat.js.map +0 -1
  104. package/dist/util/migration/prettyMutationFormatter.d.ts +0 -8
  105. package/dist/util/migration/prettyMutationFormatter.js +0 -141
  106. package/dist/util/migration/prettyMutationFormatter.js.map +0 -1
  107. package/dist/utils/migration/resolveMigrationScript.d.ts +0 -44
  108. package/dist/utils/migration/resolveMigrationScript.js +0 -74
  109. package/dist/utils/migration/resolveMigrationScript.js.map +0 -1
  110. /package/dist/actions/auth/login/{index.d.ts → login.d.ts} +0 -0
@@ -0,0 +1,220 @@
1
+ import { mockApi, testCommand } from '@sanity/cli-test';
2
+ import { afterEach, describe, expect, test, vi } from 'vitest';
3
+ import { InitCommand } from '../../init';
4
+ const mocks = vi.hoisted(()=>({
5
+ confirm: vi.fn()
6
+ }));
7
+ vi.mock('@sanity/cli-core/ux', async ()=>{
8
+ const actual = await vi.importActual('@sanity/cli-core/ux');
9
+ return {
10
+ ...actual,
11
+ confirm: mocks.confirm
12
+ };
13
+ });
14
+ vi.mock('@vercel/fs-detectors', ()=>({
15
+ detectFrameworkRecord: vi.fn().mockResolvedValue({
16
+ name: 'Next.js',
17
+ slug: 'nextjs'
18
+ }),
19
+ LocalFileSystemDetector: vi.fn()
20
+ }));
21
+ vi.mock('../../../../../cli-core/src/util/isInteractive.js', ()=>({
22
+ isInteractive: vi.fn().mockReturnValue(true)
23
+ }));
24
+ vi.mock('../../../../../cli-core/src/services/getCliToken.js', ()=>({
25
+ getCliToken: vi.fn().mockResolvedValue('test-token')
26
+ }));
27
+ vi.mock('../../../services/user.js', ()=>({
28
+ getCliUser: vi.fn().mockResolvedValue({
29
+ email: 'test@example.com',
30
+ id: 'user-123',
31
+ name: 'Test User',
32
+ provider: 'saml-123'
33
+ })
34
+ }));
35
+ describe('#init: retrieving plan', ()=>{
36
+ afterEach(()=>{
37
+ vi.clearAllMocks();
38
+ });
39
+ test('validates coupon when --coupon flag is provided', async ()=>{
40
+ mockApi({
41
+ apiVersion: 'v2025-06-01',
42
+ method: 'get',
43
+ uri: '/plans/coupon/TESTCOUPON123'
44
+ }).reply(200, [
45
+ {
46
+ id: 'test-plan-id'
47
+ }
48
+ ]);
49
+ const { error, stdout } = await testCommand(InitCommand, [
50
+ '--coupon=TESTCOUPON123'
51
+ ]);
52
+ expect(error).toBeUndefined();
53
+ expect(stdout).toContain('Coupon "TESTCOUPON123" validated!');
54
+ });
55
+ test('throws error if coupon not found with provided code', async ()=>{
56
+ mockApi({
57
+ apiVersion: 'v2025-06-01',
58
+ method: 'get',
59
+ uri: '/plans/coupon/TESTCOUPON123'
60
+ }).reply(200, []);
61
+ const { error } = await testCommand(InitCommand, [
62
+ '--coupon=TESTCOUPON123',
63
+ '--bare'
64
+ ]);
65
+ expect(error?.message).toContain('Unable to validate coupon, please try again later:');
66
+ expect(error?.message).toContain('No plans found for coupon code "TESTCOUPON123"');
67
+ });
68
+ test('throws error if coupon does not have attached plan id', async ()=>{
69
+ mockApi({
70
+ apiVersion: 'v2025-06-01',
71
+ method: 'get',
72
+ uri: '/plans/coupon/TESTCOUPON123'
73
+ }).reply(200, [
74
+ {
75
+ id: undefined
76
+ }
77
+ ]);
78
+ const { error } = await testCommand(InitCommand, [
79
+ '--coupon=TESTCOUPON123',
80
+ '--bare'
81
+ ]);
82
+ expect(error?.message).toContain('Unable to validate coupon, please try again later:');
83
+ expect(error?.message).toContain('Unable to find a plan from coupon code');
84
+ });
85
+ test('uses default plan when coupon does not exist and cli in unattended mode', async ()=>{
86
+ mockApi({
87
+ apiVersion: 'v2025-06-01',
88
+ method: 'get',
89
+ uri: '/plans/coupon/INVALID123'
90
+ }).reply(404, {
91
+ message: 'Coupon not found'
92
+ });
93
+ const { error, stderr, stdout } = await testCommand(InitCommand, [
94
+ '--coupon=INVALID123',
95
+ '--yes',
96
+ '--dataset=test',
97
+ '--project=test'
98
+ ]);
99
+ expect(error).toBe(undefined);
100
+ expect(stderr).toContain('Warning: Coupon "INVALID123" is not available - using default plan');
101
+ expect(stdout).toContain('Using default plan.');
102
+ });
103
+ test('uses default plan when coupon invalid and user confirms default plan', async ()=>{
104
+ mockApi({
105
+ apiVersion: 'v2025-06-01',
106
+ method: 'get',
107
+ uri: '/plans/coupon/INVALID123'
108
+ }).reply(404, {
109
+ message: 'Coupon not found'
110
+ });
111
+ mocks.confirm.mockResolvedValue(true);
112
+ const { error, stdout } = await testCommand(InitCommand, [
113
+ '--coupon=INVALID123'
114
+ ]);
115
+ expect(error).toBeUndefined();
116
+ expect(mocks.confirm).toHaveBeenCalledWith({
117
+ default: true,
118
+ message: 'Coupon "INVALID123" is not available, use default plan instead?'
119
+ });
120
+ expect(stdout).toContain('Using default plan.');
121
+ });
122
+ test('throws error when coupon invalid and user declines the default plan', async ()=>{
123
+ mockApi({
124
+ apiVersion: 'v2025-06-01',
125
+ method: 'get',
126
+ uri: '/plans/coupon/INVALID123'
127
+ }).reply(404, {
128
+ message: 'Coupon not found'
129
+ });
130
+ mocks.confirm.mockResolvedValue(false);
131
+ const { error } = await testCommand(InitCommand, [
132
+ '--coupon=INVALID123'
133
+ ]);
134
+ expect(error?.message).toContain('Coupon "INVALID123" does not exist');
135
+ });
136
+ test('returns when client request for plan is successful', async ()=>{
137
+ mockApi({
138
+ apiVersion: 'v2025-06-01',
139
+ method: 'get',
140
+ uri: '/plans/growth'
141
+ }).reply(200, [
142
+ {
143
+ id: 'test-plan-id'
144
+ }
145
+ ]);
146
+ const { error } = await testCommand(InitCommand, [
147
+ '--project-plan=growth'
148
+ ]);
149
+ expect(error).toBeUndefined();
150
+ });
151
+ test('throw error when no plan id is returned by request', async ()=>{
152
+ mockApi({
153
+ apiVersion: 'v2025-06-01',
154
+ method: 'get',
155
+ uri: '/plans/growth'
156
+ }).reply(200, [
157
+ {
158
+ id: undefined
159
+ }
160
+ ]);
161
+ const { error } = await testCommand(InitCommand, [
162
+ '--project-plan=growth'
163
+ ]);
164
+ expect(error?.message).toContain('Unable to validate plan, please try again later:');
165
+ expect(error?.message).toContain('Unable to find a plan with id growth');
166
+ });
167
+ test('uses default plan when plan id does not exist and cli in unattended mode', async ()=>{
168
+ mockApi({
169
+ apiVersion: 'v2025-06-01',
170
+ method: 'get',
171
+ uri: '/plans/growth'
172
+ }).reply(404, {
173
+ message: 'Plan not found'
174
+ });
175
+ const { error, stderr, stdout } = await testCommand(InitCommand, [
176
+ '--project-plan=growth',
177
+ '--yes',
178
+ '--dataset=test',
179
+ '--project==test'
180
+ ]);
181
+ expect(error).toBe(undefined);
182
+ expect(stderr).toContain('Warning: Project plan "growth" does not exist - using default plan');
183
+ expect(stdout).toContain('Using default plan.');
184
+ });
185
+ test('uses default plan when plan ID not found and user confirms default plan', async ()=>{
186
+ mockApi({
187
+ apiVersion: 'v2025-06-01',
188
+ method: 'get',
189
+ uri: '/plans/growth'
190
+ }).reply(404, {
191
+ message: 'Plan not found'
192
+ });
193
+ mocks.confirm.mockResolvedValue(true);
194
+ const { error, stdout } = await testCommand(InitCommand, [
195
+ '--project-plan=growth'
196
+ ]);
197
+ expect(error).toBeUndefined();
198
+ expect(mocks.confirm).toHaveBeenCalledWith({
199
+ default: true,
200
+ message: 'Project plan "growth" does not exist, use default plan instead?'
201
+ });
202
+ expect(stdout).toContain('Using default plan.');
203
+ });
204
+ test('throws error when plan ID not found and user declines default plan', async ()=>{
205
+ mockApi({
206
+ apiVersion: 'v2025-06-01',
207
+ method: 'get',
208
+ uri: '/plans/growth'
209
+ }).reply(404, {
210
+ message: 'Plan not found'
211
+ });
212
+ mocks.confirm.mockResolvedValue(false);
213
+ const { error } = await testCommand(InitCommand, [
214
+ '--project-plan=growth'
215
+ ]);
216
+ expect(error?.message).toContain('Plan id "growth" does not exist');
217
+ });
218
+ });
219
+
220
+ //# sourceMappingURL=init.plan.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/__tests__/init/init.plan.test.ts"],"sourcesContent":["import {mockApi, testCommand} from '@sanity/cli-test'\nimport {afterEach, describe, expect, test, vi} from 'vitest'\n\nimport {InitCommand} from '../../init'\n\nconst mocks = vi.hoisted(() => ({\n confirm: vi.fn(),\n}))\n\nvi.mock('@sanity/cli-core/ux', async () => {\n const actual = await vi.importActual('@sanity/cli-core/ux')\n\n return {\n ...actual,\n confirm: mocks.confirm,\n }\n})\n\nvi.mock('@vercel/fs-detectors', () => ({\n detectFrameworkRecord: vi.fn().mockResolvedValue({\n name: 'Next.js',\n slug: 'nextjs',\n }),\n LocalFileSystemDetector: vi.fn(),\n}))\n\nvi.mock('../../../../../cli-core/src/util/isInteractive.js', () => ({\n isInteractive: vi.fn().mockReturnValue(true),\n}))\n\nvi.mock('../../../../../cli-core/src/services/getCliToken.js', () => ({\n getCliToken: vi.fn().mockResolvedValue('test-token'),\n}))\n\nvi.mock('../../../services/user.js', () => ({\n getCliUser: vi.fn().mockResolvedValue({\n email: 'test@example.com',\n id: 'user-123',\n name: 'Test User',\n provider: 'saml-123',\n }),\n}))\n\ndescribe('#init: retrieving plan', () => {\n afterEach(() => {\n vi.clearAllMocks()\n })\n\n test('validates coupon when --coupon flag is provided', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/coupon/TESTCOUPON123',\n }).reply(200, [{id: 'test-plan-id'}])\n\n const {error, stdout} = await testCommand(InitCommand, ['--coupon=TESTCOUPON123'])\n\n expect(error).toBeUndefined()\n expect(stdout).toContain('Coupon \"TESTCOUPON123\" validated!')\n })\n\n test('throws error if coupon not found with provided code', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/coupon/TESTCOUPON123',\n }).reply(200, [])\n\n const {error} = await testCommand(InitCommand, ['--coupon=TESTCOUPON123', '--bare'])\n\n expect(error?.message).toContain('Unable to validate coupon, please try again later:')\n expect(error?.message).toContain('No plans found for coupon code \"TESTCOUPON123\"')\n })\n\n test('throws error if coupon does not have attached plan id', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/coupon/TESTCOUPON123',\n }).reply(200, [{id: undefined}])\n\n const {error} = await testCommand(InitCommand, ['--coupon=TESTCOUPON123', '--bare'])\n\n expect(error?.message).toContain('Unable to validate coupon, please try again later:')\n expect(error?.message).toContain('Unable to find a plan from coupon code')\n })\n\n test('uses default plan when coupon does not exist and cli in unattended mode', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/coupon/INVALID123',\n }).reply(404, {message: 'Coupon not found'})\n\n const {error, stderr, stdout} = await testCommand(InitCommand, [\n '--coupon=INVALID123',\n '--yes',\n '--dataset=test',\n '--project=test',\n ])\n\n expect(error).toBe(undefined)\n expect(stderr).toContain('Warning: Coupon \"INVALID123\" is not available - using default plan')\n expect(stdout).toContain('Using default plan.')\n })\n\n test('uses default plan when coupon invalid and user confirms default plan', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/coupon/INVALID123',\n }).reply(404, {message: 'Coupon not found'})\n\n mocks.confirm.mockResolvedValue(true)\n\n const {error, stdout} = await testCommand(InitCommand, ['--coupon=INVALID123'])\n\n expect(error).toBeUndefined()\n expect(mocks.confirm).toHaveBeenCalledWith({\n default: true,\n message: 'Coupon \"INVALID123\" is not available, use default plan instead?',\n })\n expect(stdout).toContain('Using default plan.')\n })\n\n test('throws error when coupon invalid and user declines the default plan', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/coupon/INVALID123',\n }).reply(404, {message: 'Coupon not found'})\n mocks.confirm.mockResolvedValue(false)\n\n const {error} = await testCommand(InitCommand, ['--coupon=INVALID123'])\n\n expect(error?.message).toContain('Coupon \"INVALID123\" does not exist')\n })\n\n test('returns when client request for plan is successful', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/growth',\n }).reply(200, [{id: 'test-plan-id'}])\n\n const {error} = await testCommand(InitCommand, ['--project-plan=growth'])\n\n expect(error).toBeUndefined()\n })\n\n test('throw error when no plan id is returned by request', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/growth',\n }).reply(200, [{id: undefined}])\n\n const {error} = await testCommand(InitCommand, ['--project-plan=growth'])\n expect(error?.message).toContain('Unable to validate plan, please try again later:')\n expect(error?.message).toContain('Unable to find a plan with id growth')\n })\n\n test('uses default plan when plan id does not exist and cli in unattended mode', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/growth',\n }).reply(404, {message: 'Plan not found'})\n\n const {error, stderr, stdout} = await testCommand(InitCommand, [\n '--project-plan=growth',\n '--yes',\n '--dataset=test',\n '--project==test',\n ])\n\n expect(error).toBe(undefined)\n expect(stderr).toContain('Warning: Project plan \"growth\" does not exist - using default plan')\n expect(stdout).toContain('Using default plan.')\n })\n\n test('uses default plan when plan ID not found and user confirms default plan', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/growth',\n }).reply(404, {message: 'Plan not found'})\n mocks.confirm.mockResolvedValue(true)\n\n const {error, stdout} = await testCommand(InitCommand, ['--project-plan=growth'])\n\n expect(error).toBeUndefined()\n expect(mocks.confirm).toHaveBeenCalledWith({\n default: true,\n message: 'Project plan \"growth\" does not exist, use default plan instead?',\n })\n expect(stdout).toContain('Using default plan.')\n })\n\n test('throws error when plan ID not found and user declines default plan', async () => {\n mockApi({\n apiVersion: 'v2025-06-01',\n method: 'get',\n uri: '/plans/growth',\n }).reply(404, {message: 'Plan not found'})\n mocks.confirm.mockResolvedValue(false)\n\n const {error} = await testCommand(InitCommand, ['--project-plan=growth'])\n\n expect(error?.message).toContain('Plan id \"growth\" does not exist')\n })\n})\n"],"names":["mockApi","testCommand","afterEach","describe","expect","test","vi","InitCommand","mocks","hoisted","confirm","fn","mock","actual","importActual","detectFrameworkRecord","mockResolvedValue","name","slug","LocalFileSystemDetector","isInteractive","mockReturnValue","getCliToken","getCliUser","email","id","provider","clearAllMocks","apiVersion","method","uri","reply","error","stdout","toBeUndefined","toContain","message","undefined","stderr","toBe","toHaveBeenCalledWith","default"],"mappings":"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"}
@@ -0,0 +1,279 @@
1
+ import { runCommand } from '@oclif/test';
2
+ import { testCommand } from '@sanity/cli-test';
3
+ import { afterEach, describe, expect, test, vi } from 'vitest';
4
+ import { InitCommand } from '../../init';
5
+ const mocks = vi.hoisted(()=>({
6
+ checkIsRemoteTemplate: vi.fn().mockReturnValue(false),
7
+ detectFrameworkRecord: vi.fn(),
8
+ getGitHubRepoInfo: vi.fn()
9
+ }));
10
+ vi.mock('@vercel/fs-detectors', ()=>({
11
+ detectFrameworkRecord: mocks.detectFrameworkRecord,
12
+ LocalFileSystemDetector: vi.fn()
13
+ }));
14
+ vi.mock('../../../../../cli-core/src/util/isInteractive.js', ()=>({
15
+ isInteractive: vi.fn().mockReturnValue(true)
16
+ }));
17
+ vi.mock('../../../actions/init/remoteTemplate.js', ()=>({
18
+ checkIsRemoteTemplate: mocks.checkIsRemoteTemplate,
19
+ getGitHubRepoInfo: mocks.getGitHubRepoInfo
20
+ }));
21
+ vi.mock('../../../../../cli-core/src/services/getCliToken.js', ()=>({
22
+ getCliToken: vi.fn().mockResolvedValue('test-token')
23
+ }));
24
+ vi.mock('../../../services/user.js', ()=>({
25
+ getCliUser: vi.fn().mockResolvedValue({
26
+ email: 'test@example.com',
27
+ id: 'user-123',
28
+ name: 'Test User',
29
+ provider: 'saml-123'
30
+ })
31
+ }));
32
+ describe('#init: oclif command setup', ()=>{
33
+ afterEach(()=>{
34
+ vi.clearAllMocks();
35
+ });
36
+ test('--help works', async ()=>{
37
+ const { stdout } = await runCommand('init --help');
38
+ expect(stdout).toMatchInlineSnapshot(`
39
+ "Initialize a new Sanity Studio, project and/or app
40
+
41
+ USAGE
42
+ $ sanity init [--json] [--auto-updates | --bare] [--coupon
43
+ <code> | --project-plan <name>] [--dataset <name> | --dataset-default]
44
+ [--env <filename> | ] [--git <message> | ] [--mcp]
45
+ [--nextjs-add-config-files] [--nextjs-append-env] [--nextjs-embed-studio]
46
+ [--organization <id>] [--output-path <path> | ] [--overwrite-files]
47
+ [--package-manager <manager> | ] [--project <id> | --create-project <name>]
48
+ [--provider <provider>] [--template <template> | ] [--typescript | ]
49
+ [--visibility <mode>] [-y]
50
+
51
+ FLAGS
52
+ -y, --yes Unattended mode, answers "yes" to any
53
+ "yes/no" prompt and otherwise uses defaults
54
+ --[no-]auto-updates Enable auto updates of studio versions
55
+ --bare Skip the Studio initialization and only print
56
+ the selected project ID and dataset name to
57
+ stdout
58
+ --coupon=<code> Optionally select a coupon for a new project
59
+ (cannot be used with --project-plan)
60
+ --create-project=<name> Create a new project with the given name
61
+ --dataset=<name> Dataset name for the studio
62
+ --dataset-default Set up a project with a public dataset named
63
+ "production"
64
+ --env=<filename> Write environment variables to file
65
+ --[no-]git=<message> Specify a commit message for initial commit,
66
+ or disable git init
67
+ --[no-]mcp Enable AI editor integration (MCP) setup
68
+ --organization=<id> Organization ID to use for the project
69
+ --output-path=<path> Path to write studio project to
70
+ --overwrite-files Overwrite existing files
71
+ --package-manager=<manager> Specify which package manager to use
72
+ [allowed: npm, yarn, pnpm]
73
+ --project=<id> Project ID to use for the studio
74
+ --project-plan=<name> Optionally select a plan for a new project
75
+ --provider=<provider> Login provider to use
76
+ --template=<template> [default: clean] Project template to use
77
+ [default: "clean"]
78
+ --[no-]typescript Enable TypeScript support
79
+ --visibility=<mode> Visibility mode for dataset
80
+
81
+ GLOBAL FLAGS
82
+ --json Format output as json.
83
+
84
+ NEXT.JS FLAGS
85
+ --[no-]nextjs-add-config-files Add config files to Next.js project
86
+ --[no-]nextjs-append-env Append project ID and dataset to .env file
87
+ --[no-]nextjs-embed-studio Embed the Studio in Next.js application
88
+
89
+ DESCRIPTION
90
+ Initialize a new Sanity Studio, project and/or app
91
+
92
+ EXAMPLES
93
+ $ sanity init
94
+
95
+ Initialize a new project with a public dataset named "production"
96
+
97
+ $ sanity init --dataset-default
98
+
99
+ Initialize a project with the given project ID and dataset to the given path
100
+
101
+ $ sanity init -y --project abc123 --dataset production --output-path \\
102
+ ~/myproj
103
+
104
+ Initialize a project with the given project ID and dataset using the moviedb
105
+ template to the given path
106
+
107
+ $ sanity init -y --project abc123 --dataset staging --template moviedb \\
108
+ --output-path .
109
+
110
+ Create a brand new project with name "Movies Unlimited"
111
+
112
+ $ sanity init -y --create-project "Movies Unlimited" --dataset moviedb \\
113
+ --visibility private --template moviedb --output-path \\
114
+ /Users/espenh/movies-unlimited
115
+
116
+ "
117
+ `);
118
+ });
119
+ test.each([
120
+ {
121
+ flag1: 'auto-updates',
122
+ flag2: 'bare'
123
+ },
124
+ {
125
+ flag1: 'coupon=123',
126
+ flag2: 'project-plan=123'
127
+ },
128
+ {
129
+ flag1: 'dataset="123',
130
+ flag2: 'dataset-default'
131
+ },
132
+ {
133
+ flag1: 'env=.env',
134
+ flag2: 'bare'
135
+ },
136
+ {
137
+ flag1: 'git=test',
138
+ flag2: 'bare'
139
+ },
140
+ {
141
+ flag1: 'no-git',
142
+ flag2: 'git=test'
143
+ },
144
+ {
145
+ flag1: 'output-path=/test-path',
146
+ flag2: 'bare'
147
+ },
148
+ {
149
+ flag1: 'package-manager=pnpm',
150
+ flag2: 'bare'
151
+ },
152
+ {
153
+ flag1: 'template=test',
154
+ flag2: 'bare'
155
+ },
156
+ {
157
+ flag1: 'typescript',
158
+ flag2: 'bare'
159
+ },
160
+ {
161
+ flag1: 'project=test',
162
+ flag2: 'create-project=test'
163
+ }
164
+ ])('throws error when `$flag1` and `$flag2` flags are both passed', async ({ flag1, flag2 })=>{
165
+ const { error } = await testCommand(InitCommand, [
166
+ `--${flag1}`,
167
+ `--${flag2}`
168
+ ]);
169
+ const [name1] = flag1.split('=');
170
+ const [name2, value2 = 'true'] = flag2.split('=');
171
+ expect(error?.message).toContain(`--${name2}=${value2} cannot also be provided when using --${name1}`);
172
+ });
173
+ test.each([
174
+ {
175
+ flag: 'env',
176
+ message: 'Env filename (`--env`) must start with `.env`',
177
+ value: 'invalid.txt'
178
+ },
179
+ {
180
+ flag: 'visibility',
181
+ message: 'Expected --visibility=opaque to be one of: public, private',
182
+ value: 'opaque'
183
+ },
184
+ {
185
+ flag: 'package-manager',
186
+ message: 'Expected --package-manager=pnm to be one of: npm, yarn, pnpm',
187
+ value: 'pnm'
188
+ }
189
+ ])('throws error when `$flag` value is invalid', async ({ flag, message, value })=>{
190
+ const { error } = await testCommand(InitCommand, [
191
+ `--${flag}=${value}`
192
+ ]);
193
+ expect(error?.message).toContain(message);
194
+ });
195
+ test('throws error when type argument is passed', async ()=>{
196
+ const { error } = await testCommand(InitCommand, [
197
+ 'bad-argument'
198
+ ]);
199
+ expect(error?.message).toContain('Unknown init type "bad-argument"');
200
+ });
201
+ test('throws deprecation error when type argument is passed with `plugin`', async ()=>{
202
+ const { error } = await testCommand(InitCommand, [
203
+ 'plugin'
204
+ ]);
205
+ expect(error?.message).toContain('Initializing plugins through the CLI is no longer supported');
206
+ });
207
+ test('throws error when `reconfigure` flag is passed', async ()=>{
208
+ const { error } = await testCommand(InitCommand, [
209
+ '--reconfigure'
210
+ ]);
211
+ expect(error?.message).toContain('--reconfigure is deprecated - manual configuration is now required');
212
+ });
213
+ test('throws error when framework and remote template are used together', async ()=>{
214
+ mocks.detectFrameworkRecord.mockResolvedValueOnce({
215
+ name: 'Next.js',
216
+ slug: 'nextjs'
217
+ });
218
+ mocks.checkIsRemoteTemplate.mockReturnValueOnce(true);
219
+ mocks.getGitHubRepoInfo.mockResolvedValueOnce({
220
+ branch: 'main',
221
+ owner: 'sanity-io',
222
+ repo: 'sanity'
223
+ });
224
+ const { error } = await testCommand(InitCommand, [
225
+ '--template=https://github.com/sanity-io/sanity'
226
+ ]);
227
+ expect(error?.message).toContain('A remote template cannot be used with a detected framework. Detected: Next.js');
228
+ });
229
+ test('throws error when in unattended mode and `dataset` is not set', async ()=>{
230
+ const { error } = await testCommand(InitCommand, [
231
+ '--yes'
232
+ ]);
233
+ expect(error?.message).toContain('`--dataset` must be specified in unattended mode');
234
+ });
235
+ test('throws error when `output-path` is not used in unattended mode with non-nextjs project', async ()=>{
236
+ // Mock no framework or a non-Next.js framework
237
+ mocks.detectFrameworkRecord.mockResolvedValueOnce(null);
238
+ const { error } = await testCommand(InitCommand, [
239
+ '--yes',
240
+ '--dataset=production',
241
+ '--project=test-project'
242
+ ]);
243
+ // Should throw output-path error for non-Next.js projects
244
+ expect(error?.message).toContain('`--output-path` must be specified in unattended mode');
245
+ });
246
+ test('throws error when in unattended mode and `project` and `create-project` not set', async ()=>{
247
+ mocks.detectFrameworkRecord.mockResolvedValueOnce({
248
+ name: 'Next.js',
249
+ slug: 'nextjs'
250
+ });
251
+ const { error } = await testCommand(InitCommand, [
252
+ '--yes',
253
+ '--dataset=production'
254
+ ]);
255
+ expect(error?.message).toContain('`--project <id>` or `--create-project <name>` must be specified in unattended mode');
256
+ });
257
+ test('throws error when in unattended mode and `create-project` not set with `organization`', async ()=>{
258
+ mocks.detectFrameworkRecord.mockResolvedValueOnce({
259
+ name: 'Next.js',
260
+ slug: 'nextjs'
261
+ });
262
+ const { error } = await testCommand(InitCommand, [
263
+ '--yes',
264
+ '--dataset=production',
265
+ '--create-project=test'
266
+ ]);
267
+ expect(error?.message).toContain('--create-project is not supported in unattended mode without an organization, please specify an organization with `--organization <id>`');
268
+ });
269
+ test('logs properly if app template flag is not valid', async ()=>{
270
+ mocks.detectFrameworkRecord.mockResolvedValueOnce(null);
271
+ const { stdout } = await testCommand(InitCommand, [
272
+ '--template=invalid-template-name'
273
+ ]);
274
+ // When template is not an app template, it should log "Fetching existing projects"
275
+ expect(stdout).toContain('Fetching existing projects');
276
+ });
277
+ });
278
+
279
+ //# sourceMappingURL=init.setup.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/__tests__/init/init.setup.test.ts"],"sourcesContent":["import {runCommand} from '@oclif/test'\nimport {testCommand} from '@sanity/cli-test'\nimport {afterEach, describe, expect, test, vi} from 'vitest'\n\nimport {InitCommand} from '../../init'\n\nconst mocks = vi.hoisted(() => ({\n checkIsRemoteTemplate: vi.fn().mockReturnValue(false),\n detectFrameworkRecord: vi.fn(),\n getGitHubRepoInfo: vi.fn(),\n}))\n\nvi.mock('@vercel/fs-detectors', () => ({\n detectFrameworkRecord: mocks.detectFrameworkRecord,\n LocalFileSystemDetector: vi.fn(),\n}))\n\nvi.mock('../../../../../cli-core/src/util/isInteractive.js', () => ({\n isInteractive: vi.fn().mockReturnValue(true),\n}))\n\nvi.mock('../../../actions/init/remoteTemplate.js', () => ({\n checkIsRemoteTemplate: mocks.checkIsRemoteTemplate,\n getGitHubRepoInfo: mocks.getGitHubRepoInfo,\n}))\n\nvi.mock('../../../../../cli-core/src/services/getCliToken.js', () => ({\n getCliToken: vi.fn().mockResolvedValue('test-token'),\n}))\n\nvi.mock('../../../services/user.js', () => ({\n getCliUser: vi.fn().mockResolvedValue({\n email: 'test@example.com',\n id: 'user-123',\n name: 'Test User',\n provider: 'saml-123',\n }),\n}))\n\ndescribe('#init: oclif command setup', () => {\n afterEach(() => {\n vi.clearAllMocks()\n })\n\n test('--help works', async () => {\n const {stdout} = await runCommand('init --help')\n\n expect(stdout).toMatchInlineSnapshot(`\n \"Initialize a new Sanity Studio, project and/or app\n\n USAGE\n $ sanity init [--json] [--auto-updates | --bare] [--coupon\n <code> | --project-plan <name>] [--dataset <name> | --dataset-default]\n [--env <filename> | ] [--git <message> | ] [--mcp]\n [--nextjs-add-config-files] [--nextjs-append-env] [--nextjs-embed-studio]\n [--organization <id>] [--output-path <path> | ] [--overwrite-files]\n [--package-manager <manager> | ] [--project <id> | --create-project <name>]\n [--provider <provider>] [--template <template> | ] [--typescript | ]\n [--visibility <mode>] [-y]\n\n FLAGS\n -y, --yes Unattended mode, answers \"yes\" to any\n \"yes/no\" prompt and otherwise uses defaults\n --[no-]auto-updates Enable auto updates of studio versions\n --bare Skip the Studio initialization and only print\n the selected project ID and dataset name to\n stdout\n --coupon=<code> Optionally select a coupon for a new project\n (cannot be used with --project-plan)\n --create-project=<name> Create a new project with the given name\n --dataset=<name> Dataset name for the studio\n --dataset-default Set up a project with a public dataset named\n \"production\"\n --env=<filename> Write environment variables to file\n --[no-]git=<message> Specify a commit message for initial commit,\n or disable git init\n --[no-]mcp Enable AI editor integration (MCP) setup\n --organization=<id> Organization ID to use for the project\n --output-path=<path> Path to write studio project to\n --overwrite-files Overwrite existing files\n --package-manager=<manager> Specify which package manager to use\n [allowed: npm, yarn, pnpm]\n --project=<id> Project ID to use for the studio\n --project-plan=<name> Optionally select a plan for a new project\n --provider=<provider> Login provider to use\n --template=<template> [default: clean] Project template to use\n [default: \"clean\"]\n --[no-]typescript Enable TypeScript support\n --visibility=<mode> Visibility mode for dataset\n\n GLOBAL FLAGS\n --json Format output as json.\n\n NEXT.JS FLAGS\n --[no-]nextjs-add-config-files Add config files to Next.js project\n --[no-]nextjs-append-env Append project ID and dataset to .env file\n --[no-]nextjs-embed-studio Embed the Studio in Next.js application\n\n DESCRIPTION\n Initialize a new Sanity Studio, project and/or app\n\n EXAMPLES\n $ sanity init\n\n Initialize a new project with a public dataset named \"production\"\n\n $ sanity init --dataset-default\n\n Initialize a project with the given project ID and dataset to the given path\n\n $ sanity init -y --project abc123 --dataset production --output-path \\\\\n ~/myproj\n\n Initialize a project with the given project ID and dataset using the moviedb\n template to the given path\n\n $ sanity init -y --project abc123 --dataset staging --template moviedb \\\\\n --output-path .\n\n Create a brand new project with name \"Movies Unlimited\"\n\n $ sanity init -y --create-project \"Movies Unlimited\" --dataset moviedb \\\\\n --visibility private --template moviedb --output-path \\\\\n /Users/espenh/movies-unlimited\n\n \"\n `)\n })\n\n test.each([\n {flag1: 'auto-updates', flag2: 'bare'},\n {flag1: 'coupon=123', flag2: 'project-plan=123'},\n {flag1: 'dataset=\"123', flag2: 'dataset-default'},\n {flag1: 'env=.env', flag2: 'bare'},\n {flag1: 'git=test', flag2: 'bare'},\n {flag1: 'no-git', flag2: 'git=test'},\n {flag1: 'output-path=/test-path', flag2: 'bare'},\n {flag1: 'package-manager=pnpm', flag2: 'bare'},\n {flag1: 'template=test', flag2: 'bare'},\n {flag1: 'typescript', flag2: 'bare'},\n {flag1: 'project=test', flag2: 'create-project=test'},\n ])('throws error when `$flag1` and `$flag2` flags are both passed', async ({flag1, flag2}) => {\n const {error} = await testCommand(InitCommand, [`--${flag1}`, `--${flag2}`])\n\n const [name1] = flag1.split('=')\n const [name2, value2 = 'true'] = flag2.split('=')\n\n expect(error?.message).toContain(\n `--${name2}=${value2} cannot also be provided when using --${name1}`,\n )\n })\n\n test.each([\n {flag: 'env', message: 'Env filename (`--env`) must start with `.env`', value: 'invalid.txt'},\n {\n flag: 'visibility',\n message: 'Expected --visibility=opaque to be one of: public, private',\n value: 'opaque',\n },\n {\n flag: 'package-manager',\n message: 'Expected --package-manager=pnm to be one of: npm, yarn, pnpm',\n value: 'pnm',\n },\n ])('throws error when `$flag` value is invalid', async ({flag, message, value}) => {\n const {error} = await testCommand(InitCommand, [`--${flag}=${value}`])\n\n expect(error?.message).toContain(message)\n })\n\n test('throws error when type argument is passed', async () => {\n const {error} = await testCommand(InitCommand, ['bad-argument'])\n\n expect(error?.message).toContain('Unknown init type \"bad-argument\"')\n })\n\n test('throws deprecation error when type argument is passed with `plugin`', async () => {\n const {error} = await testCommand(InitCommand, ['plugin'])\n\n expect(error?.message).toContain('Initializing plugins through the CLI is no longer supported')\n })\n\n test('throws error when `reconfigure` flag is passed', async () => {\n const {error} = await testCommand(InitCommand, ['--reconfigure'])\n\n expect(error?.message).toContain(\n '--reconfigure is deprecated - manual configuration is now required',\n )\n })\n\n test('throws error when framework and remote template are used together', async () => {\n mocks.detectFrameworkRecord.mockResolvedValueOnce({\n name: 'Next.js',\n slug: 'nextjs',\n })\n mocks.checkIsRemoteTemplate.mockReturnValueOnce(true)\n mocks.getGitHubRepoInfo.mockResolvedValueOnce({\n branch: 'main',\n owner: 'sanity-io',\n repo: 'sanity',\n })\n\n const {error} = await testCommand(InitCommand, [\n '--template=https://github.com/sanity-io/sanity',\n ])\n\n expect(error?.message).toContain(\n 'A remote template cannot be used with a detected framework. Detected: Next.js',\n )\n })\n\n test('throws error when in unattended mode and `dataset` is not set', async () => {\n const {error} = await testCommand(InitCommand, ['--yes'])\n\n expect(error?.message).toContain('`--dataset` must be specified in unattended mode')\n })\n\n test('throws error when `output-path` is not used in unattended mode with non-nextjs project', async () => {\n // Mock no framework or a non-Next.js framework\n mocks.detectFrameworkRecord.mockResolvedValueOnce(null)\n\n const {error} = await testCommand(InitCommand, [\n '--yes',\n '--dataset=production',\n '--project=test-project',\n ])\n\n // Should throw output-path error for non-Next.js projects\n expect(error?.message).toContain('`--output-path` must be specified in unattended mode')\n })\n\n test('throws error when in unattended mode and `project` and `create-project` not set', async () => {\n mocks.detectFrameworkRecord.mockResolvedValueOnce({\n name: 'Next.js',\n slug: 'nextjs',\n })\n\n const {error} = await testCommand(InitCommand, [\n '--yes',\n '--dataset=production',\n // Deliberately omitting --project and --create-project\n ])\n\n expect(error?.message).toContain(\n '`--project <id>` or `--create-project <name>` must be specified in unattended mode',\n )\n })\n\n test('throws error when in unattended mode and `create-project` not set with `organization`', async () => {\n mocks.detectFrameworkRecord.mockResolvedValueOnce({\n name: 'Next.js',\n slug: 'nextjs',\n })\n\n const {error} = await testCommand(InitCommand, [\n '--yes',\n '--dataset=production',\n '--create-project=test',\n ])\n\n expect(error?.message).toContain(\n '--create-project is not supported in unattended mode without an organization, please specify an organization with `--organization <id>`',\n )\n })\n\n test('logs properly if app template flag is not valid', async () => {\n mocks.detectFrameworkRecord.mockResolvedValueOnce(null)\n\n const {stdout} = await testCommand(InitCommand, [\n '--template=invalid-template-name', // Not a valid app template\n ])\n\n // When template is not an app template, it should log \"Fetching existing projects\"\n expect(stdout).toContain('Fetching existing projects')\n })\n})\n"],"names":["runCommand","testCommand","afterEach","describe","expect","test","vi","InitCommand","mocks","hoisted","checkIsRemoteTemplate","fn","mockReturnValue","detectFrameworkRecord","getGitHubRepoInfo","mock","LocalFileSystemDetector","isInteractive","getCliToken","mockResolvedValue","getCliUser","email","id","name","provider","clearAllMocks","stdout","toMatchInlineSnapshot","each","flag1","flag2","error","name1","split","name2","value2","message","toContain","flag","value","mockResolvedValueOnce","slug","mockReturnValueOnce","branch","owner","repo"],"mappings":"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"}