|
1 | | -import {getOrGenerateSchemaPath, inFunctionContext} from './common.js' |
| 1 | +import {getOrGenerateSchemaPath, inFunctionContext, chooseFunction} from './common.js' |
2 | 2 | import { |
3 | 3 | testAppLinked, |
4 | 4 | testDeveloperPlatformClient, |
@@ -42,59 +42,51 @@ beforeEach(async () => { |
42 | 42 | vi.mocked(isTerminalInteractive).mockReturnValue(true) |
43 | 43 | }) |
44 | 44 |
|
45 | | -describe('ensure we are within a function context', () => { |
46 | | - test('runs callback when we are inside a function directory', async () => { |
| 45 | +describe('inFunctionContext integration', () => { |
| 46 | + test('passes correct parameters to callback when function is found', async () => { |
47 | 47 | // Given |
48 | | - let ranCallback = false |
| 48 | + const callback = vi.fn().mockResolvedValue(app) |
49 | 49 |
|
50 | 50 | // When |
51 | 51 | await inFunctionContext({ |
52 | 52 | path: joinPath(app.directory, 'extensions/my-function'), |
53 | | - callback: async (_app, _fun) => { |
54 | | - ranCallback = true |
55 | | - return _app |
56 | | - }, |
| 53 | + callback, |
57 | 54 | }) |
58 | 55 |
|
59 | 56 | // Then |
60 | | - expect(ranCallback).toBe(true) |
61 | | - expect(renderFatalError).not.toHaveBeenCalled() |
| 57 | + expect(callback).toHaveBeenCalledWith( |
| 58 | + app, |
| 59 | + // developerPlatformClient |
| 60 | + expect.any(Object), |
| 61 | + ourFunction, |
| 62 | + // orgId |
| 63 | + expect.any(String), |
| 64 | + ) |
62 | 65 | }) |
63 | 66 |
|
64 | | - test('displays function prompt when we are not inside a function directory', async () => { |
| 67 | + test('calls linkedAppContext with correct parameters', async () => { |
65 | 68 | // Given |
66 | | - const callback = vi.fn() |
| 69 | + const callback = vi.fn().mockResolvedValue(app) |
| 70 | + const path = 'some/path' |
| 71 | + const apiKey = 'test-api-key' |
| 72 | + const userProvidedConfigName = 'test-config' |
67 | 73 |
|
68 | 74 | // When |
69 | 75 | await inFunctionContext({ |
70 | | - path: 'random/dir', |
| 76 | + path, |
| 77 | + apiKey, |
| 78 | + userProvidedConfigName, |
| 79 | + reset: true, |
71 | 80 | callback, |
72 | 81 | }) |
73 | 82 |
|
74 | 83 | // Then |
75 | | - expect(callback).toHaveBeenCalledOnce() |
76 | | - expect(renderAutocompletePrompt).toHaveBeenCalledOnce() |
77 | | - expect(renderFatalError).not.toHaveBeenCalled() |
78 | | - }) |
79 | | - |
80 | | - test('displays an error when terminal is not interactive and we are not inside a function directory', async () => { |
81 | | - // Given |
82 | | - let ranCallback = false |
83 | | - vi.mocked(isTerminalInteractive).mockReturnValue(false) |
84 | | - |
85 | | - // When |
86 | | - await expect( |
87 | | - inFunctionContext({ |
88 | | - path: 'random/dir', |
89 | | - callback: async (_app, _fun) => { |
90 | | - ranCallback = true |
91 | | - return _app |
92 | | - }, |
93 | | - }), |
94 | | - ).rejects.toThrowError() |
95 | | - |
96 | | - // Then |
97 | | - expect(ranCallback).toBe(false) |
| 84 | + expect(linkedAppContext).toHaveBeenCalledWith({ |
| 85 | + directory: path, |
| 86 | + clientId: apiKey, |
| 87 | + forceRelink: true, |
| 88 | + userProvidedConfigName, |
| 89 | + }) |
98 | 90 | }) |
99 | 91 | }) |
100 | 92 |
|
@@ -140,3 +132,117 @@ describe('getOrGenerateSchemaPath', () => { |
140 | 132 | expect(fileExists).toHaveBeenCalledWith(expectedPath) |
141 | 133 | }) |
142 | 134 | }) |
| 135 | + |
| 136 | +describe('chooseFunction', () => { |
| 137 | + let app: AppLinkedInterface |
| 138 | + let functionExtension1: ExtensionInstance |
| 139 | + let functionExtension2: ExtensionInstance |
| 140 | + let nonFunctionExtension: ExtensionInstance |
| 141 | + |
| 142 | + beforeEach(async () => { |
| 143 | + functionExtension1 = await testFunctionExtension({ |
| 144 | + dir: '/path/to/app/extensions/function-1', |
| 145 | + config: { |
| 146 | + name: 'function-1', |
| 147 | + type: 'product_discounts', |
| 148 | + description: 'Test function 1', |
| 149 | + build: { |
| 150 | + command: 'echo "hello world"', |
| 151 | + watch: ['src/**/*.rs'], |
| 152 | + wasm_opt: true, |
| 153 | + }, |
| 154 | + api_version: '2022-07', |
| 155 | + configuration_ui: true, |
| 156 | + }, |
| 157 | + }) |
| 158 | + |
| 159 | + functionExtension2 = await testFunctionExtension({ |
| 160 | + dir: '/path/to/app/extensions/function-2', |
| 161 | + config: { |
| 162 | + name: 'function-2', |
| 163 | + type: 'product_discounts', |
| 164 | + description: 'Test function 2', |
| 165 | + build: { |
| 166 | + command: 'echo "hello world"', |
| 167 | + watch: ['src/**/*.rs'], |
| 168 | + wasm_opt: true, |
| 169 | + }, |
| 170 | + api_version: '2022-07', |
| 171 | + configuration_ui: true, |
| 172 | + }, |
| 173 | + }) |
| 174 | + |
| 175 | + nonFunctionExtension = { |
| 176 | + directory: '/path/to/app/extensions/theme', |
| 177 | + isFunctionExtension: false, |
| 178 | + handle: 'theme-extension', |
| 179 | + } as ExtensionInstance |
| 180 | + }) |
| 181 | + |
| 182 | + test('returns the function when path matches a function directory', async () => { |
| 183 | + // Given |
| 184 | + app = testAppLinked({allExtensions: [functionExtension1, functionExtension2, nonFunctionExtension]}) |
| 185 | + |
| 186 | + // When |
| 187 | + const result = await chooseFunction(app, '/path/to/app/extensions/function-1') |
| 188 | + |
| 189 | + // Then |
| 190 | + expect(result).toBe(functionExtension1) |
| 191 | + expect(renderAutocompletePrompt).not.toHaveBeenCalled() |
| 192 | + }) |
| 193 | + |
| 194 | + test('returns the only function when app has single function and path does not match', async () => { |
| 195 | + // Given |
| 196 | + app = testAppLinked({allExtensions: [functionExtension1, nonFunctionExtension]}) |
| 197 | + |
| 198 | + // When |
| 199 | + const result = await chooseFunction(app, '/some/other/path') |
| 200 | + |
| 201 | + // Then |
| 202 | + expect(result).toBe(functionExtension1) |
| 203 | + expect(renderAutocompletePrompt).not.toHaveBeenCalled() |
| 204 | + }) |
| 205 | + |
| 206 | + test('prompts user to select function when multiple functions exist and path does not match', async () => { |
| 207 | + // Given |
| 208 | + app = testAppLinked({allExtensions: [functionExtension1, functionExtension2, nonFunctionExtension]}) |
| 209 | + vi.mocked(isTerminalInteractive).mockReturnValue(true) |
| 210 | + vi.mocked(renderAutocompletePrompt).mockResolvedValue(functionExtension2) |
| 211 | + |
| 212 | + // When |
| 213 | + const result = await chooseFunction(app, '/some/other/path') |
| 214 | + |
| 215 | + // Then |
| 216 | + expect(result).toBe(functionExtension2) |
| 217 | + expect(renderAutocompletePrompt).toHaveBeenCalledWith({ |
| 218 | + message: 'Which function?', |
| 219 | + choices: [ |
| 220 | + {label: functionExtension1.handle, value: functionExtension1}, |
| 221 | + {label: functionExtension2.handle, value: functionExtension2}, |
| 222 | + ], |
| 223 | + }) |
| 224 | + }) |
| 225 | + |
| 226 | + test('throws error when terminal is not interactive and cannot determine function', async () => { |
| 227 | + // Given |
| 228 | + app = testAppLinked({allExtensions: [functionExtension1, functionExtension2]}) |
| 229 | + vi.mocked(isTerminalInteractive).mockReturnValue(false) |
| 230 | + |
| 231 | + // When/Then |
| 232 | + await expect(chooseFunction(app, '/some/other/path')).rejects.toThrowError( |
| 233 | + 'Run this command from a function directory or use `--path` to specify a function directory.', |
| 234 | + ) |
| 235 | + expect(renderAutocompletePrompt).not.toHaveBeenCalled() |
| 236 | + }) |
| 237 | + |
| 238 | + test('filters out non-function extensions', async () => { |
| 239 | + // Given |
| 240 | + app = testAppLinked({allExtensions: [nonFunctionExtension]}) |
| 241 | + vi.mocked(isTerminalInteractive).mockReturnValue(false) |
| 242 | + |
| 243 | + // When/Then |
| 244 | + await expect(chooseFunction(app, '/some/path')).rejects.toThrowError( |
| 245 | + 'Run this command from a function directory or use `--path` to specify a function directory.', |
| 246 | + ) |
| 247 | + }) |
| 248 | +}) |
0 commit comments