|
1 | 1 | import {searchService} from './search.js' |
2 | | -import {describe, expect, test, vi} from 'vitest' |
3 | | -import {openURL} from '@shopify/cli-kit/node/system' |
| 2 | +import {describe, expect, test, vi, beforeEach} from 'vitest' |
| 3 | +import {fetch} from '@shopify/cli-kit/node/http' |
| 4 | +import {outputResult} from '@shopify/cli-kit/node/output' |
| 5 | +import {AbortError} from '@shopify/cli-kit/node/error' |
4 | 6 |
|
5 | | -vi.mock('@shopify/cli-kit/node/system') |
| 7 | +vi.mock('@shopify/cli-kit/node/http') |
| 8 | +// Only stub `outputResult`; keep the rest of the module real. Blanket-mocking it |
| 9 | +// would also mock `stringifyMessage`, which `AbortError`'s constructor relies on — |
| 10 | +// that would silently empty out every thrown error message. |
| 11 | +vi.mock('@shopify/cli-kit/node/output', async (importOriginal) => ({ |
| 12 | + ...(await importOriginal<typeof import('@shopify/cli-kit/node/output')>()), |
| 13 | + outputResult: vi.fn(), |
| 14 | +})) |
| 15 | + |
| 16 | +const okResponse = (body: string) => |
| 17 | + ({ok: true, status: 200, statusText: 'OK', text: () => Promise.resolve(body)}) as any |
| 18 | + |
| 19 | +const errorResponse = (status: number, statusText: string, body: string) => |
| 20 | + ({ok: false, status, statusText, text: () => Promise.resolve(body)}) as any |
| 21 | + |
| 22 | +const resultsBody = |
| 23 | + '[{"score":0.99,"content":"About webhooks","url":"https://shopify.dev/x","title":"Webhooks","domain":null}]' |
| 24 | + |
| 25 | +beforeEach(() => { |
| 26 | + vi.mocked(fetch).mockResolvedValue(okResponse(resultsBody)) |
| 27 | +}) |
6 | 28 |
|
7 | 29 | describe('searchService', () => { |
8 | | - test('the right URL is open in the system when a query is passed', async () => { |
9 | | - await searchService('deploy app') |
| 30 | + test('requests the search endpoint with the query and prints the raw JSON body', async () => { |
| 31 | + await searchService('webhooks') |
| 32 | + |
| 33 | + expect(fetch).toHaveBeenCalledWith('https://shopify.dev/assistant/search?query=webhooks', { |
| 34 | + headers: {Accept: 'application/json'}, |
| 35 | + }) |
| 36 | + expect(outputResult).toHaveBeenCalledWith(resultsBody) |
| 37 | + }) |
| 38 | + |
| 39 | + test('includes api_name and api_version params when provided', async () => { |
| 40 | + await searchService('create a product', 'admin', 'latest') |
| 41 | + |
| 42 | + expect(fetch).toHaveBeenCalledWith( |
| 43 | + 'https://shopify.dev/assistant/search?query=create+a+product&api_name=admin&api_version=latest', |
| 44 | + {headers: {Accept: 'application/json'}}, |
| 45 | + ) |
| 46 | + }) |
| 47 | + |
| 48 | + test('URL-encodes queries with spaces and special characters', async () => { |
| 49 | + await searchService('a & b?') |
| 50 | + |
| 51 | + expect(fetch).toHaveBeenCalledWith('https://shopify.dev/assistant/search?query=a+%26+b%3F', { |
| 52 | + headers: {Accept: 'application/json'}, |
| 53 | + }) |
| 54 | + }) |
| 55 | + |
| 56 | + test('surfaces the server error message from a non-ok JSON response', async () => { |
| 57 | + vi.mocked(fetch).mockResolvedValue( |
| 58 | + errorResponse( |
| 59 | + 400, |
| 60 | + 'Bad Request', |
| 61 | + '{"error":"Invalid api_version \'2025-01\' for api_name \'admin\'. Available versions: 2026-07"}', |
| 62 | + ), |
| 63 | + ) |
10 | 64 |
|
11 | | - expect(openURL).toBeCalledWith('https://shopify.dev?search=deploy+app') |
| 65 | + await expect(searchService('products', 'admin', '2025-01')).rejects.toThrowError( |
| 66 | + /Invalid api_version '2025-01' for api_name 'admin'\. Available versions: 2026-07/, |
| 67 | + ) |
| 68 | + expect(outputResult).not.toHaveBeenCalled() |
12 | 69 | }) |
13 | 70 |
|
14 | | - test('the right URL is open in the system when a query is not passed', async () => { |
15 | | - await searchService() |
| 71 | + test('falls back to the status line when a non-ok response is not JSON', async () => { |
| 72 | + vi.mocked(fetch).mockResolvedValue(errorResponse(500, 'Internal Server Error', '<html>nope</html>')) |
16 | 73 |
|
17 | | - expect(openURL).toBeCalledWith('https://shopify.dev?search=') |
| 74 | + await expect(searchService('products')).rejects.toThrowError(AbortError) |
| 75 | + await expect(searchService('products')).rejects.toThrowError(/500 Internal Server Error/) |
| 76 | + expect(outputResult).not.toHaveBeenCalled() |
18 | 77 | }) |
19 | 78 | }) |
0 commit comments