summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLi Zhineng <[email protected]>2025-07-23 21:15:21 +0800
committerLi Zhineng <[email protected]>2025-07-23 21:15:21 +0800
commitb2584c0e805476a971d24e22350caf3875b42dd0 (patch)
tree258f2679b2d2aa85802ee6284e5faf0ad45f562b /src
parent2f6bfbcaff5b35d9fd14563f1e2892dbf040646e (diff)
downloadwave-b2584c0e805476a971d24e22350caf3875b42dd0.tar.gz
wave-b2584c0e805476a971d24e22350caf3875b42dd0.zip
test types
Diffstat (limited to 'src')
-rw-r--r--src/index.test.ts391
-rw-r--r--src/index.ts25
2 files changed, 17 insertions, 399 deletions
diff --git a/src/index.test.ts b/src/index.test.ts
deleted file mode 100644
index 8d73700..0000000
--- a/src/index.test.ts
+++ /dev/null
@@ -1,391 +0,0 @@
-import { Factory, Response } from './'
-
-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'
- })
- })
- )
- })
- })
-})
diff --git a/src/index.ts b/src/index.ts
index 04a1870..de0ad68 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -3,7 +3,9 @@ type QueryString = Record<string, string>
type HttpMethod = WechatMiniprogram.RequestOption['method']
type RequestTask = WechatMiniprogram.RequestTask | WechatMiniprogram.UploadTask
type RequestData = Record<string, unknown>
-type ResponseData = WechatMiniprogram.RequestSuccessCallbackResult['data']
+type ResponseData =
+ | WechatMiniprogram.RequestSuccessCallbackResult['data']
+ | WechatMiniprogram.UploadFileSuccessCallbackResult['data']
type MiddlewareNext<T extends ResponseData> = (
request: PendingRequest
) => RequestPromise<Response<SuccessCallbackResult<T>, T>>
@@ -87,7 +89,7 @@ class PendingRequest {
#data: RequestData = {}
- #middlewares: Middleware<any>[] = []
+ #middlewares: Middleware<ResponseData>[] = []
method(method: HttpMethod) {
const request = this.#clone()
@@ -192,7 +194,12 @@ class PendingRequest {
return this.#throughMiddlewares(request)(
this.#dispatchUpload(option.name, option.filePath)
- )
+ ) as RequestPromise<
+ Response<
+ WechatMiniprogram.UploadFileSuccessCallbackResult,
+ WechatMiniprogram.UploadFileSuccessCallbackResult['data']
+ >
+ >
}
#dispatchUpload(name: string, filePath: string) {
@@ -232,10 +239,12 @@ class PendingRequest {
}
}
- #send<T extends ResponseData>(): RequestPromise<
- Response<SuccessCallbackResult<T>, T>
- > {
- return this.#throughMiddlewares<T>(this)(this.#dispatchRequest<T>)
+ #send<T extends ResponseData>() {
+ return this.#throughMiddlewares<T>(this)(
+ this.#dispatchRequest<T>
+ ) as RequestPromise<
+ Response<WechatMiniprogram.RequestSuccessCallbackResult<T>, T>
+ >
}
#dispatchRequest<T extends ResponseData>(request: PendingRequest) {
@@ -274,7 +283,7 @@ class PendingRequest {
return (handler: MiddlewareNext<T>) =>
this.#middlewares.reduceRight(
(next: MiddlewareNext<T>, middleware) => (request: PendingRequest) =>
- middleware(request, next),
+ (middleware as Middleware<T>)(request, next),
handler
)(request)
}