|
1 | 1 | import { |
2 | 2 | AppSchema, |
3 | 3 | CurrentAppConfiguration, |
4 | | - LegacyAppConfiguration, |
5 | | - TemplateConfigSchema, |
6 | 4 | getAppScopes, |
7 | 5 | getAppScopesArray, |
8 | | - getTemplateScopesArray, |
9 | 6 | getUIExtensionRendererVersion, |
10 | | - isCurrentAppSchema, |
11 | | - isLegacyAppSchema, |
12 | 7 | validateExtensionsHandlesInCollection, |
13 | 8 | validateFunctionExtensionsWithUiHandle, |
14 | 9 | } from './app.js' |
@@ -63,69 +58,32 @@ const CORRECT_CURRENT_APP_SCHEMA: CurrentAppConfiguration = { |
63 | 58 | }, |
64 | 59 | } |
65 | 60 |
|
66 | | -const CORRECT_LEGACY_APP_SCHEMA: LegacyAppConfiguration = { |
67 | | - path: '', |
68 | | - extension_directories: [], |
69 | | - web_directories: [], |
70 | | - scopes: 'write_products', |
71 | | -} |
72 | | - |
73 | 61 | describe('app schema validation', () => { |
74 | | - describe('legacy schema validator', () => { |
75 | | - test('checks whether legacy app schema is valid -- pass', () => { |
76 | | - expect(isLegacyAppSchema(CORRECT_LEGACY_APP_SCHEMA)).toBe(true) |
77 | | - }) |
78 | | - test('checks whether legacy app schema is valid -- fail', () => { |
79 | | - const config = { |
80 | | - ...CORRECT_LEGACY_APP_SCHEMA, |
81 | | - some_other_key: 'i am not valid, i will fail', |
82 | | - } |
83 | | - expect(isLegacyAppSchema(config)).toBe(false) |
84 | | - }) |
| 62 | + test('extension_directories should be transformed to double asterisks', () => { |
| 63 | + const config = { |
| 64 | + ...CORRECT_CURRENT_APP_SCHEMA, |
| 65 | + extension_directories: ['extensions/*'], |
| 66 | + } |
| 67 | + const parsed = AppSchema.parse(config) |
| 68 | + expect(parsed.extension_directories).toEqual(['extensions/**']) |
85 | 69 | }) |
86 | 70 |
|
87 | | - describe('current schema validator', () => { |
88 | | - test('checks whether current app schema is valid -- pass', () => { |
89 | | - expect(isCurrentAppSchema(CORRECT_CURRENT_APP_SCHEMA)).toBe(true) |
90 | | - }) |
91 | | - test('checks whether current app schema is valid -- fail', () => { |
92 | | - const config = { |
93 | | - ...CORRECT_CURRENT_APP_SCHEMA, |
94 | | - } |
95 | | - |
96 | | - // eslint-disable-next-line @typescript-eslint/ban-ts-comment |
97 | | - // @ts-ignore |
98 | | - delete config.client_id |
99 | | - |
100 | | - expect(isCurrentAppSchema(config)).toBe(false) |
101 | | - }) |
102 | | - |
103 | | - test('extension_directories should be transformed to double asterisks', () => { |
104 | | - const config = { |
105 | | - ...CORRECT_CURRENT_APP_SCHEMA, |
106 | | - extension_directories: ['extensions/*'], |
107 | | - } |
108 | | - const parsed = AppSchema.parse(config) |
109 | | - expect(parsed.extension_directories).toEqual(['extensions/**']) |
110 | | - }) |
111 | | - |
112 | | - test('extension_directories is not transformed if it ends with double asterisks', () => { |
113 | | - const config = { |
114 | | - ...CORRECT_CURRENT_APP_SCHEMA, |
115 | | - extension_directories: ['extensions/**'], |
116 | | - } |
117 | | - const parsed = AppSchema.parse(config) |
118 | | - expect(parsed.extension_directories).toEqual(['extensions/**']) |
119 | | - }) |
| 71 | + test('extension_directories is not transformed if it ends with double asterisks', () => { |
| 72 | + const config = { |
| 73 | + ...CORRECT_CURRENT_APP_SCHEMA, |
| 74 | + extension_directories: ['extensions/**'], |
| 75 | + } |
| 76 | + const parsed = AppSchema.parse(config) |
| 77 | + expect(parsed.extension_directories).toEqual(['extensions/**']) |
| 78 | + }) |
120 | 79 |
|
121 | | - test('extension_directories is not transformed if it doesnt end with a wildcard', () => { |
122 | | - const config = { |
123 | | - ...CORRECT_CURRENT_APP_SCHEMA, |
124 | | - extension_directories: ['extensions'], |
125 | | - } |
126 | | - const parsed = AppSchema.parse(config) |
127 | | - expect(parsed.extension_directories).toEqual(['extensions']) |
128 | | - }) |
| 80 | + test('extension_directories is not transformed if it doesnt end with a wildcard', () => { |
| 81 | + const config = { |
| 82 | + ...CORRECT_CURRENT_APP_SCHEMA, |
| 83 | + extension_directories: ['extensions'], |
| 84 | + } |
| 85 | + const parsed = AppSchema.parse(config) |
| 86 | + expect(parsed.extension_directories).toEqual(['extensions']) |
129 | 87 | }) |
130 | 88 | }) |
131 | 89 |
|
@@ -212,117 +170,19 @@ describe('getUIExtensionRendererVersion', () => { |
212 | 170 | }) |
213 | 171 |
|
214 | 172 | describe('getAppScopes', () => { |
215 | | - test('returns the scopes key when schema is legacy', () => { |
216 | | - const config = {path: '', scopes: 'read_themes,read_products'} |
217 | | - expect(getAppScopes(config)).toEqual('read_themes,read_products') |
218 | | - }) |
219 | | - |
220 | | - test('returns the access_scopes.scopes key when schema is current', () => { |
| 173 | + test('returns the access_scopes.scopes key', () => { |
221 | 174 | const config = {...DEFAULT_CONFIG, access_scopes: {scopes: 'read_themes,read_themes'}} |
222 | 175 | expect(getAppScopes(config)).toEqual('read_themes,read_themes') |
223 | 176 | }) |
224 | 177 | }) |
225 | 178 |
|
226 | 179 | describe('getAppScopesArray', () => { |
227 | | - test('returns the scopes key when schema is legacy', () => { |
228 | | - const config = {path: '', scopes: 'read_themes, read_order ,write_products'} |
229 | | - expect(getAppScopesArray(config)).toEqual(['read_themes', 'read_order', 'write_products']) |
230 | | - }) |
231 | | - |
232 | | - test('returns the access_scopes.scopes key when schema is current', () => { |
| 180 | + test('returns the access_scopes.scopes key', () => { |
233 | 181 | const config = {...DEFAULT_CONFIG, access_scopes: {scopes: 'read_themes, read_order ,write_products'}} |
234 | 182 | expect(getAppScopesArray(config)).toEqual(['read_themes', 'read_order', 'write_products']) |
235 | 183 | }) |
236 | 184 | }) |
237 | 185 |
|
238 | | -describe('TemplateConfigSchema', () => { |
239 | | - test('parses config with legacy scopes format', () => { |
240 | | - const config = {scopes: 'read_products,write_products'} |
241 | | - const result = TemplateConfigSchema.parse(config) |
242 | | - expect(result.scopes).toEqual('read_products,write_products') |
243 | | - }) |
244 | | - |
245 | | - test('parses config with access_scopes format', () => { |
246 | | - const config = {access_scopes: {scopes: 'read_products,write_products'}} |
247 | | - const result = TemplateConfigSchema.parse(config) |
248 | | - expect(result.access_scopes?.scopes).toEqual('read_products,write_products') |
249 | | - }) |
250 | | - |
251 | | - test('preserves extra keys like metafields via passthrough', () => { |
252 | | - const config = { |
253 | | - scopes: 'write_products', |
254 | | - product: { |
255 | | - metafields: { |
256 | | - app: { |
257 | | - demo_info: { |
258 | | - type: 'single_line_text_field', |
259 | | - name: 'Demo Source Info', |
260 | | - }, |
261 | | - }, |
262 | | - }, |
263 | | - }, |
264 | | - webhooks: { |
265 | | - api_version: '2025-07', |
266 | | - subscriptions: [{uri: '/webhooks', topics: ['app/uninstalled']}], |
267 | | - }, |
268 | | - } |
269 | | - const result = TemplateConfigSchema.parse(config) |
270 | | - expect(result.product).toEqual(config.product) |
271 | | - expect(result.webhooks).toEqual(config.webhooks) |
272 | | - }) |
273 | | - |
274 | | - test('parses empty config', () => { |
275 | | - const config = {} |
276 | | - const result = TemplateConfigSchema.parse(config) |
277 | | - expect(result).toEqual({}) |
278 | | - }) |
279 | | -}) |
280 | | - |
281 | | -describe('getTemplateScopesArray', () => { |
282 | | - test('returns scopes from legacy format', () => { |
283 | | - const config = {scopes: 'read_themes,write_products'} |
284 | | - expect(getTemplateScopesArray(config)).toEqual(['read_themes', 'write_products']) |
285 | | - }) |
286 | | - |
287 | | - test('returns scopes from access_scopes format', () => { |
288 | | - const config = {access_scopes: {scopes: 'read_themes,write_products'}} |
289 | | - expect(getTemplateScopesArray(config)).toEqual(['read_themes', 'write_products']) |
290 | | - }) |
291 | | - |
292 | | - test('trims whitespace from scopes and sorts', () => { |
293 | | - const config = {scopes: ' write_products , read_themes '} |
294 | | - expect(getTemplateScopesArray(config)).toEqual(['read_themes', 'write_products']) |
295 | | - }) |
296 | | - |
297 | | - test('includes empty strings from consecutive commas (caller should handle)', () => { |
298 | | - const config = {scopes: 'read_themes,write_products'} |
299 | | - expect(getTemplateScopesArray(config)).toEqual(['read_themes', 'write_products']) |
300 | | - }) |
301 | | - |
302 | | - test('returns empty array when no scopes defined', () => { |
303 | | - const config = {} |
304 | | - expect(getTemplateScopesArray(config)).toEqual([]) |
305 | | - }) |
306 | | - |
307 | | - test('returns empty array when scopes is empty string', () => { |
308 | | - const config = {scopes: ''} |
309 | | - expect(getTemplateScopesArray(config)).toEqual([]) |
310 | | - }) |
311 | | - |
312 | | - test('returns empty array when access_scopes.scopes is empty', () => { |
313 | | - const config = {access_scopes: {scopes: ''}} |
314 | | - expect(getTemplateScopesArray(config)).toEqual([]) |
315 | | - }) |
316 | | - |
317 | | - test('prefers legacy scopes over access_scopes when both present', () => { |
318 | | - const config = { |
319 | | - scopes: 'read_themes', |
320 | | - access_scopes: {scopes: 'write_products'}, |
321 | | - } |
322 | | - expect(getTemplateScopesArray(config)).toEqual(['read_themes']) |
323 | | - }) |
324 | | -}) |
325 | | - |
326 | 186 | describe('preDeployValidation', () => { |
327 | 187 | test('throws an error when app-specific webhooks are used with legacy install flow', async () => { |
328 | 188 | // Given |
@@ -418,18 +278,6 @@ Learn more: https://shopify.dev/docs/apps/build/authentication-authorization/app |
418 | 278 | await expect(app.preDeployValidation()).resolves.not.toThrow() |
419 | 279 | }) |
420 | 280 |
|
421 | | - test('does not throw an error for legacy schema apps', async () => { |
422 | | - // Given |
423 | | - const configuration: LegacyAppConfiguration = { |
424 | | - ...CORRECT_LEGACY_APP_SCHEMA, |
425 | | - scopes: 'read_orders', |
426 | | - } |
427 | | - const app = testApp(configuration, 'legacy') |
428 | | - |
429 | | - // When/Then |
430 | | - await expect(app.preDeployValidation()).resolves.not.toThrow() |
431 | | - }) |
432 | | - |
433 | 281 | test('handles null/undefined subscriptions safely', async () => { |
434 | 282 | // Given |
435 | 283 | const configuration: CurrentAppConfiguration = { |
|
0 commit comments