| 1 |
// Thin wrapper over /wp/v2/search; LinkControl in the inline editor calls |
| 2 |
// this via the settings prop. Tests pin the param shape, defaults, and the |
| 3 |
// result reshape to {id,url,title,type,kind}. |
| 4 |
|
| 5 |
jest.mock('@wordpress/api-fetch', () => ({ |
| 6 |
__esModule: true, |
| 7 |
default: jest.fn(), |
| 8 |
})); |
| 9 |
|
| 10 |
let apiFetch; |
| 11 |
let fetchLinkSuggestions; |
| 12 |
|
| 13 |
beforeEach(async () => { |
| 14 |
jest.resetModules(); |
| 15 |
jest.clearAllMocks(); |
| 16 |
apiFetch = require('@wordpress/api-fetch').default; |
| 17 |
apiFetch.mockReset(); |
| 18 |
apiFetch.mockResolvedValue([]); |
| 19 |
fetchLinkSuggestions = (await import('@quick-edit/lib/link-suggestions')) |
| 20 |
.fetchLinkSuggestions; |
| 21 |
}); |
| 22 |
|
| 23 |
const decodePath = () => { |
| 24 |
const path = apiFetch.mock.calls[0][0].path; |
| 25 |
const [base, qs] = path.split('?'); |
| 26 |
return { base, params: new URLSearchParams(qs) }; |
| 27 |
}; |
| 28 |
|
| 29 |
describe('fetchLinkSuggestions — request shape', () => { |
| 30 |
it('hits /wp/v2/search with search + per_page (default 10)', async () => { |
| 31 |
await fetchLinkSuggestions('hello'); |
| 32 |
const { base, params } = decodePath(); |
| 33 |
expect(base).toBe('/wp/v2/search'); |
| 34 |
expect(params.get('search')).toBe('hello'); |
| 35 |
expect(params.get('per_page')).toBe('10'); |
| 36 |
expect(params.has('type')).toBe(false); |
| 37 |
expect(params.has('subtype')).toBe(false); |
| 38 |
}); |
| 39 |
|
| 40 |
it('passes opts.perPage / type / subtype when supplied', async () => { |
| 41 |
await fetchLinkSuggestions('hi', { |
| 42 |
perPage: 5, |
| 43 |
type: 'post', |
| 44 |
subtype: 'page', |
| 45 |
}); |
| 46 |
const { params } = decodePath(); |
| 47 |
expect(params.get('per_page')).toBe('5'); |
| 48 |
expect(params.get('type')).toBe('post'); |
| 49 |
expect(params.get('subtype')).toBe('page'); |
| 50 |
}); |
| 51 |
|
| 52 |
it('coerces null/empty search to ""', async () => { |
| 53 |
await fetchLinkSuggestions(null); |
| 54 |
expect(decodePath().params.get('search')).toBe(''); |
| 55 |
await fetchLinkSuggestions(undefined); |
| 56 |
expect(decodePath().params.get('search')).toBe(''); |
| 57 |
}); |
| 58 |
|
| 59 |
it('encodes special characters in search', async () => { |
| 60 |
await fetchLinkSuggestions('a & b?'); |
| 61 |
expect(decodePath().params.get('search')).toBe('a & b?'); |
| 62 |
}); |
| 63 |
}); |
| 64 |
|
| 65 |
describe('fetchLinkSuggestions — result reshape', () => { |
| 66 |
it('maps each row to {id, url, title, type, kind} with subtype winning over type for type', async () => { |
| 67 |
apiFetch.mockResolvedValue([ |
| 68 |
{ id: 1, url: '/a', title: 'A', type: 'post', subtype: 'page' }, |
| 69 |
{ id: 2, url: '/b', title: 'B', type: 'post' }, |
| 70 |
]); |
| 71 |
|
| 72 |
const out = await fetchLinkSuggestions('x'); |
| 73 |
expect(out).toEqual([ |
| 74 |
{ id: 1, url: '/a', title: 'A', type: 'page', kind: 'post' }, |
| 75 |
{ id: 2, url: '/b', title: 'B', type: 'post', kind: 'post' }, |
| 76 |
]); |
| 77 |
}); |
| 78 |
|
| 79 |
it('falls back to row.url when title is missing or empty', async () => { |
| 80 |
apiFetch.mockResolvedValue([ |
| 81 |
{ id: 1, url: '/a', title: '', type: 'post' }, |
| 82 |
{ id: 2, url: '/b', type: 'post' }, |
| 83 |
]); |
| 84 |
const out = await fetchLinkSuggestions('x'); |
| 85 |
expect(out.map((r) => r.title)).toEqual(['/a', '/b']); |
| 86 |
}); |
| 87 |
|
| 88 |
it('returns [] when apiFetch resolves null/undefined', async () => { |
| 89 |
apiFetch.mockResolvedValue(null); |
| 90 |
await expect(fetchLinkSuggestions('x')).resolves.toEqual([]); |
| 91 |
apiFetch.mockResolvedValue(undefined); |
| 92 |
await expect(fetchLinkSuggestions('x')).resolves.toEqual([]); |
| 93 |
}); |
| 94 |
}); |
| 95 |
|
| 96 |
describe('fetchLinkSuggestions — error propagation', () => { |
| 97 |
it('propagates the apiFetch rejection unchanged', async () => { |
| 98 |
apiFetch.mockRejectedValue(new Error('boom')); |
| 99 |
await expect(fetchLinkSuggestions('x')).rejects.toThrow('boom'); |
| 100 |
}); |
| 101 |
}); |
| 102 |
|