diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/index.test-d.ts | 32 | ||||
| -rw-r--r-- | tests/index.test.ts | 391 |
2 files changed, 423 insertions, 0 deletions
diff --git a/tests/index.test-d.ts b/tests/index.test-d.ts new file mode 100644 index 0000000..52df93c --- /dev/null +++ b/tests/index.test-d.ts @@ -0,0 +1,32 @@ +import { expectType } from 'tsd' +import { Client } from '../src' + +interface User { + name: string +} + +;async () => { + const response = await Client.new().get('http://example.com') + expectType<WechatMiniprogram.RequestSuccessCallbackResult['data']>( + response.data() + ) +} + +;async () => { + const response = await Client.new().get<User>('http://example.com') + expectType<User>(response.data()) +} + +;async () => { + const response = await Client.new().get<string>('http://example.com/string') + expectType<string>(response.data()) +} + +;async () => { + const response = await Client.new().upload({ + url: 'http://example.com', + filePath: '/tmp/foo.txt', + name: 'file' + }) + expectType<string>(response.data()) +} diff --git a/tests/index.test.ts b/tests/index.test.ts new file mode 100644 index 0000000..c8e0a64 --- /dev/null +++ b/tests/index.test.ts @@ -0,0 +1,391 @@ +import { Factory, Response } from '../src' + +describe('HTTP Client', () => { + beforeEach(() => { + // @ts-expect-error: TypeScript complaints because 'wx' is not defined on + // the global object in Node.js. We're here to mock the global 'wx' object + // for testing purposes. + global.wx = { + request: jest.fn(), + uploadFile: jest.fn() + } + }) + + afterEach(() => { + // @ts-expect-error: Clean up the mocked global 'wx' object + delete global.wx + }) + + describe('Response', () => { + it.each([[200], [400]])( + 'retrieves the status code', + (statusCode: number) => { + const response = new Response({ + data: {}, + statusCode, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.status()).toBe(statusCode) + } + ) + + it.each([ + [200, true], + [201, false], + [299, false], + [300, false], + [304, false], + [400, false], + [500, false] + ])('should correctly determine if response is OK', (statusCode, ok) => { + const response = new Response({ + data: '', + statusCode, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.ok()).toBe(ok) + }) + + it.each([ + [200, true], + [201, true], + [299, true], + [300, false], + [304, false], + [400, false], + [500, false] + ])( + 'should correctly determine if response is successful', + (statusCode, successful) => { + const response = new Response({ + data: '', + statusCode, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.successful()).toBe(successful) + } + ) + + it.each([ + [200, false], + [201, false], + [300, false], + [304, false], + [400, true], + [401, true], + [402, true], + [499, true], + [500, false] + ])( + 'should correctly determine if response is a client error', + (statusCode, clientError) => { + const response = new Response({ + data: '', + statusCode, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.clientError()).toBe(clientError) + } + ) + + it.each([ + [200, false], + [201, false], + [300, false], + [304, false], + [400, false], + [500, true], + [503, true] + ])( + 'should correctly determine if response is a server error', + (statusCode, serverError) => { + const response = new Response({ + data: '', + statusCode, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.serverError()).toBe(serverError) + } + ) + + it.each([ + [{ foo: 'bar' }, { foo: 'bar' }], + ['foo', 'foo'] + ])('retrieves the data', (data, expected) => { + const response = new Response({ + data, + statusCode: 200, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.data()).toStrictEqual(expected) + }) + + it.each([ + ['{"foo":"bar"}', { foo: 'bar' }], + [{ foo: 'bar' }, { foo: 'bar' }] + ])('decodes the data as JSON string', (data, expected) => { + const response = new Response({ + data, + statusCode: 200, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.json()).toStrictEqual(expected) + }) + + it('returns an empty object when it fails to decode the data as JSON string', () => { + const response = new Response({ + data: '-', + statusCode: 200, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult) + expect(response.json()).toStrictEqual({}) + }) + + it('retrieves the raw response', () => { + const rawResponse = { + data: '', + statusCode: 200, + header: {}, + cookies: [] + } as unknown as WechatMiniprogram.RequestSuccessCallbackResult + const response = new Response(rawResponse) + expect(response.raw()).toStrictEqual(rawResponse) + }) + }) + + describe('PendingRequest', () => { + it.each([ + ['get', 'GET'], + ['post', 'POST'], + ['put', 'PUT'], + ['delete', 'DELETE'] + ] as const)('sends HTTP request', (method, httpMethod: string) => { + const factory = new Factory() + factory.new()[method]('http://example.com') + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + url: 'http://example.com', + method: httpMethod + }) + ) + }) + + it.each([ + [{ foo: 'bar' }, '?foo=bar'], + [{ foo: 'bar', baz: 'qux' }, '?foo=bar&baz=qux'], + [{}, ''] + ])('sends HTTP request with query string', (query, expected) => { + const factory = new Factory() + factory.new().get('http://example.com', query) + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + url: `http://example.com${expected}`, + method: 'GET', + data: {} + }) + ) + }) + + it('sends HTTP request with body', () => { + const factory = new Factory() + factory.new().post('http://example.com', { foo: 'bar' }) + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + url: 'http://example.com', + method: 'POST', + data: { foo: 'bar' }, + dataType: 'json' + }) + ) + }) + + it('sends a POST request with query string', () => { + const factory = new Factory() + factory + .new() + .withQuery({ greeting: 'world' }) + .post('http://example.com', { foo: 'bar' }) + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + url: 'http://example.com?greeting=world', + method: 'POST', + data: { foo: 'bar' } + }) + ) + }) + + it('uploads a file with POST request', () => { + const factory = new Factory() + factory.new().upload({ + url: 'http://example.com', + filePath: '/tmp/foo.jpg', + name: 'file' + }) + expect(wx.uploadFile).toHaveBeenCalledWith( + expect.objectContaining({ + url: 'http://example.com', + filePath: '/tmp/foo.jpg', + name: 'file', + header: expect.objectContaining({ + 'content-type': 'multipart/form-data' + }) + }) + ) + }) + + it('can customize HTTP header', () => { + const factory = new Factory() + factory.new().withHeader('X-Foo', 'bar').get('http://example.com') + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + header: expect.objectContaining({ + 'X-Foo': 'bar' + }) + }) + ) + }) + + it('can retrieve the request task', () => { + ;(wx.request as jest.Mock).mockImplementationOnce(() => ({ + abort: jest.fn() + })) + const factory = new Factory() + const { task } = factory.new().get('http://example.com') + expect(task.abort).toBeDefined() + }) + + it('can retrieve the HTTP response', async () => { + ;(wx.request as jest.Mock).mockImplementationOnce(({ success }) => { + success({ + data: {}, + statusCode: 200, + header: {}, + cookies: [] + }) + return { abort: jest.fn() } + }) + const factory = new Factory() + const response = await factory.new().get('http://example.com') + expect(response).toBeInstanceOf(Response) + }) + + it.each([ + ['http://example.com', '/', 'http://example.com'], + ['http://example.com/', '/', 'http://example.com'], + ['http://example.com/', '', 'http://example.com'], + ['http://example.com', '/api', 'http://example.com/api'], + ['http://example.com', 'http://other.com', 'http://other.com'], + ['http://example.com', 'https://other.com', 'https://other.com'], + ['http://example.com', 'http://other.com/api', 'http://other.com/api'] + ])('can customize base url', (baseUrl, path, url) => { + const factory = new Factory() + factory.new().baseUrl(baseUrl).get(path) + expect(wx.request).toHaveBeenCalledWith(expect.objectContaining({ url })) + }) + + test('the request builder is immutable', () => { + const factory = new Factory() + const request = factory.new() + request.withHeader('X-Foo', 'bar') + request.get('/') + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + header: expect.not.objectContaining({ + 'X-Foo': 'bar' + }) + }) + ) + }) + + it('sends HTTP request with HTML forms', () => { + const factory = new Factory() + factory.new().asForm().post('http://example.com', { foo: 'bar' }) + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + data: { foo: 'bar' }, + header: expect.objectContaining({ + 'content-type': 'application/x-www-form-urlencoded' + }) + }) + ) + }) + + describe('middleware', () => { + it('should be applied', () => { + const middleware = jest.fn((request, next) => next(request)) + const factory = new Factory() + factory.new().use(middleware).get('http://example.com') + expect(middleware).toHaveBeenCalled() + }) + + it('can modify HTTP request', () => { + const middleware = (request: any, next: any) => + next(request.withHeader('X-Foo', 'bar')) + const factory = new Factory() + factory.new().use(middleware).get('http://example.com') + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + header: expect.objectContaining({ 'X-Foo': 'bar' }) + }) + ) + }) + + it('can retrieve HTTP response', () => { + ;(wx.request as jest.Mock).mockImplementationOnce(({ success }) => { + success({ + data: {}, + statusCode: 200, + header: {}, + cookies: [] + }) + return { abort: jest.fn() } + }) + const middleware = (request: any, next: any) => { + return next(request.withHeader('X-Foo', 'bar')).then( + (response: any) => { + expect(response).toBeInstanceOf(Response) + } + ) + } + const factory = new Factory() + factory.new().use(middleware).get('http://example.com') + }) + }) + }) + + describe('Factory', () => { + it('sets global base url', () => { + const factory = new Factory() + factory.baseUrl('http://example.com') + factory.new().get('/') + factory.new().get('/api') + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ url: 'http://example.com' }) + ) + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ url: 'http://example.com/api' }) + ) + }) + + it('crafts a JSON request by default', () => { + const factory = new Factory() + factory.baseUrl('http://example.com') + factory.new().get('/') + expect(wx.request).toHaveBeenCalledWith( + expect.objectContaining({ + header: expect.objectContaining({ + 'content-type': 'application/json' + }) + }) + ) + }) + }) +}) |
