Testing JSON APIs and endpoints
Use Sounding's request helpers for endpoint and JSON API behavior.
When a repo separates browser E2E from faster app checks, these trials usually live under tests/functional/api/.
test() for JSON and endpoint behavior
Use test() for real HTTP behavior:
- status codes
- headers
- redirects
- JSON bodies
- guest vs authenticated access
- policy interaction
import { test } from 'sounding'
test('guest gets 401 on a private JSON endpoint', async ({ get, expect }) => {
const response = await get('/api/issues')
expect(response).toHaveStatus(401)
})Most endpoint trials should use Sounding's virtual request transport by default. That means get() and post() are usually powered by sails.request().
When the trial needs the real HTTP stack, opt into it explicitly:
test(
'csrf-sensitive signup works over real http',
{ transport: 'http' },
async ({ post, expect }) => {
const response = await post('/signup', {
fullName: 'Kelvin O',
emailAddress: '[email protected]'
})
expect(response).toHaveStatus(200)
}
)If only one part of the trial needs that stricter path, scope it locally instead:
test('mix virtual speed with one real-http check', async ({
sails,
expect
}) => {
const response = await sails.sounding.request.using('http').get('/health')
expect(response).toHaveStatus(200)
})Authenticated endpoint trials
import { test } from 'sounding'
test('publisher can create an issue', async ({ sails, expect }) => {
const current = await sails.sounding.world.use('publisher-team')
const response = await sails.sounding.request
.as(current.users.publisher)
.post('/api/issues', {
title: 'New issue'
})
expect(response).toHaveStatus(201)
expect(response).toHaveJsonPath('title', 'New issue')
})When the app's real password login action matters, use the first-party request auth helper instead of inventing session setup by hand:
test('creator password login redirects to invoices', async ({
auth,
expect
}) => {
const result = await auth.request.withPassword('[email protected]', {
password: 'secret123',
returnUrl: '/invoices'
})
expect(result.response).toHaveStatus(302)
expect(result.response).toRedirectTo('/invoices')
})request.as(actor) also follows the app's auth conventions, including User / userId and Creator / creatorId.
The two request surfaces
Sounding gives you the same request engine in two shapes:
- top-level aliases like
get()andpost()for the common path - the fuller
sails.sounding.requestclient when you want to scope headers, sessions, actors, or transports
Use this rule:
- start with
get()/post()when the trial is simple - reach for
sails.sounding.requestwhen you need more control
If you want the full request client API, visit transport behavior, and scoping helpers like withSession() and as(actor), read Request clients and transport.
Useful endpoint matchers
Common matchers include:
toHaveStatus()toRedirectTo()toHaveJsonPath()toHaveHeader()