Skip to content

Commit fe1f0b4

Browse files
committed
Specific methods for patch app configuration file scenarios
1 parent aeb56fc commit fe1f0b4

File tree

7 files changed

+390
-302
lines changed

7 files changed

+390
-302
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import {patchAppConfigurationFile, patchAppHiddenConfigFile} from './patch-app-configuration-file.js'
1+
import {
2+
patchAppHiddenConfigFile,
3+
setAppConfigValue,
4+
unsetAppConfigValue,
5+
setManyAppConfigValues,
6+
} from './patch-app-configuration-file.js'
27
import {getAppVersionedSchema} from '../../models/app/app.js'
38
import {loadLocalExtensionsSpecifications} from '../../models/extensions/load-specifications.js'
49
import {readFile, writeFileSync, inTemporaryDirectory} from '@shopify/cli-kit/node/fs'
@@ -33,150 +38,6 @@ function writeDefaulToml(tmpDir: string) {
3338
return configPath
3439
}
3540

36-
describe('patchAppConfigurationFile', () => {
37-
test('updates existing configuration with new values and adds new top-levelfields, replaces arrays', async () => {
38-
await inTemporaryDirectory(async (tmpDir) => {
39-
const configPath = writeDefaulToml(tmpDir)
40-
const patch = {
41-
name: 'Updated App Name',
42-
application_url: 'https://example.com',
43-
access_scopes: {
44-
use_legacy_install_flow: false,
45-
},
46-
auth: {
47-
redirect_urls: ['https://example.com/redirect3', 'https://example.com/redirect4'],
48-
},
49-
}
50-
51-
await patchAppConfigurationFile({path: configPath, patch, schema})
52-
53-
const updatedTomlFile = await readFile(configPath)
54-
expect(updatedTomlFile)
55-
.toEqual(`# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
56-
57-
client_id = "12345"
58-
name = "Updated App Name"
59-
application_url = "https://example.com"
60-
embedded = true
61-
62-
[access_scopes]
63-
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
64-
use_legacy_install_flow = false
65-
66-
[auth]
67-
redirect_urls = [
68-
"https://example.com/redirect3",
69-
"https://example.com/redirect4"
70-
]
71-
72-
[webhooks]
73-
api_version = "2023-04"
74-
`)
75-
})
76-
})
77-
78-
test('Adds new table to the toml file', async () => {
79-
await inTemporaryDirectory(async (tmpDir) => {
80-
const configPath = writeDefaulToml(tmpDir)
81-
const patch = {
82-
application_url: 'https://example.com',
83-
build: {
84-
dev_store_url: 'example.myshopify.com',
85-
},
86-
}
87-
88-
await patchAppConfigurationFile({path: configPath, patch, schema})
89-
90-
const updatedTomlFile = await readFile(configPath)
91-
expect(updatedTomlFile)
92-
.toEqual(`# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
93-
94-
client_id = "12345"
95-
name = "app1"
96-
application_url = "https://example.com"
97-
embedded = true
98-
99-
[access_scopes]
100-
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
101-
use_legacy_install_flow = true
102-
103-
[auth]
104-
redirect_urls = [
105-
"https://example.com/redirect",
106-
"https://example.com/redirect2"
107-
]
108-
109-
[webhooks]
110-
api_version = "2023-04"
111-
112-
[build]
113-
dev_store_url = "example.myshopify.com"
114-
`)
115-
})
116-
})
117-
118-
test('Adds a new field to a toml table, merging with exsisting values', async () => {
119-
await inTemporaryDirectory(async (tmpDir) => {
120-
const configPath = writeDefaulToml(tmpDir)
121-
const patch = {
122-
application_url: 'https://example.com',
123-
access_scopes: {
124-
scopes: 'read_products',
125-
},
126-
}
127-
128-
await patchAppConfigurationFile({path: configPath, patch, schema})
129-
130-
const updatedTomlFile = await readFile(configPath)
131-
expect(updatedTomlFile)
132-
.toEqual(`# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
133-
134-
client_id = "12345"
135-
name = "app1"
136-
application_url = "https://example.com"
137-
embedded = true
138-
139-
[access_scopes]
140-
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
141-
use_legacy_install_flow = true
142-
scopes = "read_products"
143-
144-
[auth]
145-
redirect_urls = [
146-
"https://example.com/redirect",
147-
"https://example.com/redirect2"
148-
]
149-
150-
[webhooks]
151-
api_version = "2023-04"
152-
`)
153-
})
154-
})
155-
156-
test('does not validate the toml if no schema is provided', async () => {
157-
await inTemporaryDirectory(async (tmpDir) => {
158-
const configPath = joinPath(tmpDir, 'shopify.app.toml')
159-
writeFileSync(
160-
configPath,
161-
`
162-
random_toml_field = "random_value"
163-
`,
164-
)
165-
const patch = {name: 123}
166-
167-
await patchAppConfigurationFile({path: configPath, patch, schema: undefined})
168-
169-
const updatedTomlFile = await readFile(configPath)
170-
expect(updatedTomlFile)
171-
.toEqual(`# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
172-
173-
random_toml_field = "random_value"
174-
name = 123
175-
`)
176-
})
177-
})
178-
})
179-
18041
describe('patchAppHiddenConfigFile', () => {
18142
test('creates a new hidden config file when it does not exist', async () => {
18243
await inTemporaryDirectory(async (tmpDir) => {
@@ -257,3 +118,176 @@ describe('patchAppHiddenConfigFile', () => {
257118
})
258119
})
259120
})
121+
122+
describe('setAppConfigValue', () => {
123+
test('sets a top-level value in the configuration', async () => {
124+
await inTemporaryDirectory(async (tmpDir) => {
125+
const configPath = writeDefaulToml(tmpDir)
126+
127+
await setAppConfigValue(configPath, 'name', 'Updated App Name', schema)
128+
129+
const updatedTomlFile = await readFile(configPath)
130+
expect(updatedTomlFile).toContain('name = "Updated App Name"')
131+
})
132+
})
133+
134+
test('sets a nested value in the configuration', async () => {
135+
await inTemporaryDirectory(async (tmpDir) => {
136+
const configPath = writeDefaulToml(tmpDir)
137+
138+
await setAppConfigValue(configPath, 'build.dev_store_url', 'example.myshopify.com', schema)
139+
140+
const updatedTomlFile = await readFile(configPath)
141+
expect(updatedTomlFile).toContain('[build]')
142+
expect(updatedTomlFile).toContain('dev_store_url = "example.myshopify.com"')
143+
})
144+
})
145+
146+
test('sets a deeply nested value in the configuration', async () => {
147+
await inTemporaryDirectory(async (tmpDir) => {
148+
const configPath = writeDefaulToml(tmpDir)
149+
150+
await setAppConfigValue(configPath, 'build.auth.settings', true, schema)
151+
152+
const updatedTomlFile = await readFile(configPath)
153+
expect(updatedTomlFile).toContain('[build.auth]')
154+
expect(updatedTomlFile).toContain('settings = true')
155+
})
156+
})
157+
})
158+
159+
describe('unsetAppConfigValue', () => {
160+
test('unsets a top-level value in the configuration', async () => {
161+
await inTemporaryDirectory(async (tmpDir) => {
162+
const configPath = writeDefaulToml(tmpDir)
163+
164+
await unsetAppConfigValue(configPath, 'name', schema)
165+
166+
const updatedTomlFile = await readFile(configPath)
167+
expect(updatedTomlFile).not.toContain('name = "app1"')
168+
})
169+
})
170+
171+
test('unsets a nested value in existing table in the configuration', async () => {
172+
await inTemporaryDirectory(async (tmpDir) => {
173+
const configPath = writeDefaulToml(tmpDir)
174+
175+
// Add a value first
176+
await setAppConfigValue(configPath, 'build.dev_store_url', 'example.myshopify.com', schema)
177+
178+
// Now unset it
179+
await unsetAppConfigValue(configPath, 'build.dev_store_url', schema)
180+
181+
const updatedTomlFile = await readFile(configPath)
182+
expect(updatedTomlFile).toContain('[build]')
183+
expect(updatedTomlFile).not.toContain('dev_store_url = "example.myshopify.com"')
184+
})
185+
})
186+
})
187+
188+
describe('setManyAppConfigValues', () => {
189+
test('sets multiple top-level values in the configuration', async () => {
190+
await inTemporaryDirectory(async (tmpDir) => {
191+
const configPath = writeDefaulToml(tmpDir)
192+
193+
await setManyAppConfigValues(
194+
configPath,
195+
[
196+
{keyPath: 'name', value: 'Updated App Name'},
197+
{keyPath: 'client_id', value: '67890'},
198+
],
199+
schema,
200+
)
201+
202+
const updatedTomlFile = await readFile(configPath)
203+
expect(updatedTomlFile).toContain('name = "Updated App Name"')
204+
expect(updatedTomlFile).toContain('client_id = "67890"')
205+
})
206+
})
207+
208+
test('sets a mix of top-level and nested values in the configuration', async () => {
209+
await inTemporaryDirectory(async (tmpDir) => {
210+
const configPath = writeDefaulToml(tmpDir)
211+
212+
await setManyAppConfigValues(
213+
configPath,
214+
[
215+
{keyPath: 'name', value: 'Updated App Name'},
216+
{keyPath: 'build.dev_store_url', value: 'example.myshopify.com'},
217+
],
218+
schema,
219+
)
220+
221+
const updatedTomlFile = await readFile(configPath)
222+
expect(updatedTomlFile).toContain('name = "Updated App Name"')
223+
expect(updatedTomlFile).toContain('[build]')
224+
expect(updatedTomlFile).toContain('dev_store_url = "example.myshopify.com"')
225+
})
226+
})
227+
228+
test('properly handles array values in the configuration', async () => {
229+
await inTemporaryDirectory(async (tmpDir) => {
230+
const configPath = writeDefaulToml(tmpDir)
231+
232+
await setManyAppConfigValues(
233+
configPath,
234+
[{keyPath: 'auth.redirect_urls', value: ['https://example.com/redirect3', 'https://example.com/redirect4']}],
235+
schema,
236+
)
237+
238+
const updatedTomlFile = await readFile(configPath)
239+
expect(updatedTomlFile).toContain('[auth]')
240+
expect(updatedTomlFile).toContain('redirect_urls = [')
241+
expect(updatedTomlFile).toContain('"https://example.com/redirect3"')
242+
expect(updatedTomlFile).toContain('"https://example.com/redirect4"')
243+
expect(updatedTomlFile).not.toContain('"https://example.com/redirect"')
244+
expect(updatedTomlFile).not.toContain('"https://example.com/redirect2"')
245+
})
246+
})
247+
248+
test('combines multiple nested keys into a single object structure', async () => {
249+
await inTemporaryDirectory(async (tmpDir) => {
250+
const configPath = writeDefaulToml(tmpDir)
251+
252+
await setManyAppConfigValues(
253+
configPath,
254+
[
255+
{keyPath: 'build.dev_store_url', value: 'example.myshopify.com'},
256+
{keyPath: 'build.automatically_update_urls_on_dev', value: true},
257+
],
258+
schema,
259+
)
260+
261+
const updatedTomlFile = await readFile(configPath)
262+
expect(updatedTomlFile).toContain('[build]')
263+
expect(updatedTomlFile).toContain('dev_store_url = "example.myshopify.com"')
264+
expect(updatedTomlFile).toContain('automatically_update_urls_on_dev = true')
265+
})
266+
})
267+
268+
test('updates existing configuration with new values and replaces arrays', async () => {
269+
await inTemporaryDirectory(async (tmpDir) => {
270+
const configPath = writeDefaulToml(tmpDir)
271+
272+
await setManyAppConfigValues(
273+
configPath,
274+
[
275+
{keyPath: 'name', value: 'Updated App Name'},
276+
{keyPath: 'application_url', value: 'https://example.com'},
277+
{keyPath: 'access_scopes.use_legacy_install_flow', value: false},
278+
{keyPath: 'auth.redirect_urls', value: ['https://example.com/redirect3', 'https://example.com/redirect4']},
279+
],
280+
schema,
281+
)
282+
283+
const updatedTomlFile = await readFile(configPath)
284+
expect(updatedTomlFile).toContain('name = "Updated App Name"')
285+
expect(updatedTomlFile).toContain('application_url = "https://example.com"')
286+
expect(updatedTomlFile).toContain('use_legacy_install_flow = false')
287+
expect(updatedTomlFile).toContain('redirect_urls = [')
288+
expect(updatedTomlFile).toContain('"https://example.com/redirect3"')
289+
expect(updatedTomlFile).toContain('"https://example.com/redirect4"')
290+
expect(updatedTomlFile).not.toContain('"https://example.com/redirect"')
291+
})
292+
})
293+
})

0 commit comments

Comments
 (0)