From dbd5befe1f5a0301932c5217cf465c9fe2cf61ac Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Sat, 13 Feb 2021 15:19:57 +0100 Subject: [PATCH] Adds TypeScript usage validation --- package.json | 3 +- src/context/errors.ts | 2 +- src/context/json.ts | 4 +-- src/graphql.test-d.ts | 31 -------------------- src/graphql.ts | 4 +-- src/handlers/RestHandler.ts | 5 ++-- src/index.ts | 4 +++ src/rest.ts | 6 ++-- test/typings/graphql.test-d.ts | 52 ++++++++++++++++++++++++++++++++++ test/typings/rest.test-d.ts | 39 +++++++++++++++++++++++++ test/typings/tsconfig.json | 16 +++++++++++ 11 files changed, 124 insertions(+), 42 deletions(-) delete mode 100644 src/graphql.test-d.ts create mode 100644 test/typings/graphql.test-d.ts create mode 100644 test/typings/rest.test-d.ts create mode 100644 test/typings/tsconfig.json diff --git a/package.json b/package.json index 7da1522a6..ee50a76ae 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,12 @@ "start": "cross-env NODE_ENV=development rollup -c rollup.config.ts -w", "clean": "rimraf lib node/**/*.js", "lint": "eslint \"{cli,config,src,test}/**/*.ts\"", - "build": "yarn clean && cross-env NODE_ENV=production rollup -c rollup.config.ts", + "build": "yarn clean && yarn lint && cross-env NODE_ENV=production rollup -c rollup.config.ts && yarn test:ts", "test": "yarn test:unit && yarn test:integration", "test:unit": "cross-env BABEL_ENV=test jest --runInBand", "test:integration": "node --max_old_space_size=8000 node_modules/jest/bin/jest.js --config=test/jest.config.js --runInBand", "test:smoke": "config/scripts/smoke.sh", + "test:ts": "tsc -p test/typings/tsconfig.json", "test:focused": "node node_modules/ts-node/dist/bin.js --project=test/tsconfig.json test/focusedTest.ts", "prepublishOnly": "yarn lint && yarn test:unit && yarn build && yarn test:integration" }, diff --git a/src/context/errors.ts b/src/context/errors.ts index fa63a4107..49c4ded8d 100644 --- a/src/context/errors.ts +++ b/src/context/errors.ts @@ -22,6 +22,6 @@ export const errors = < const prevBody = jsonParse(res.body) || {} const nextBody = mergeRight(prevBody, { errors: errorsList }) - return json(nextBody)(res) + return json(nextBody)(res as any) as any } } diff --git a/src/context/json.ts b/src/context/json.ts index a6fb68110..ae67fd45d 100644 --- a/src/context/json.ts +++ b/src/context/json.ts @@ -12,10 +12,10 @@ import { ResponseTransformer } from '../response' */ export const json = ( body: BodyTypeJSON, -): ResponseTransformer => { +): ResponseTransformer => { return (res) => { res.headers.set('Content-Type', 'application/json') - res.body = JSON.stringify(body) + res.body = JSON.stringify(body) as any return res } diff --git a/src/graphql.test-d.ts b/src/graphql.test-d.ts deleted file mode 100644 index 0cd0a3b2b..000000000 --- a/src/graphql.test-d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { graphql } from './graphql' - -graphql.query<{ key: string }>('', (req, res, ctx) => { - // @ts-expect-error Response data doesn't match the query type. - return res(ctx.data({})) -}) - -// @ts-expect-error `null` is not a valid variables type. -graphql.query<{ key: string }, null>('', (req, res, ctx) => { - return res(ctx.data({ key: 'pass' })) -}) - -graphql.mutation<{ key: string }>('', (req, res, ctx) => - // @ts-expect-error Response data doesn't match the query type. - res(ctx.data({})), -) - -// @ts-expect-error `null` is not a valid variables type. -graphql.mutation<{ key: string }, null>('', (req, res, ctx) => { - return res(ctx.data({ key: 'pass' })) -}) - -graphql.operation<{ key: string }>((req, res, ctx) => { - // @ts-expect-error Response data doesn't match the query type. - return res(ctx.data({})) -}) - -// @ts-expect-error `null` is not a valid variables type. -graphql.operation<{ key: string }, null>('', (req, res, ctx) => { - return res(ctx.data({ key: 'pass' })) -}) diff --git a/src/graphql.ts b/src/graphql.ts index 316acdbac..afdaec3c4 100644 --- a/src/graphql.ts +++ b/src/graphql.ts @@ -14,7 +14,7 @@ function createScopedGraphQLHandler( url: Mask, ) { return < - QueryType, + QueryType extends Record, VariablesType extends GraphQLVariablesType = GraphQLVariablesType >( operationName: GraphQLHandlerNameSelector, @@ -29,7 +29,7 @@ function createScopedGraphQLHandler( function createGraphQLOperationHandler(url: Mask) { return < - QueryType, + QueryType extends Record, VariablesType extends GraphQLVariablesType = GraphQLVariablesType >( resolver: ResponseResolver< diff --git a/src/handlers/RestHandler.ts b/src/handlers/RestHandler.ts index 20c169756..1f51c060f 100644 --- a/src/handlers/RestHandler.ts +++ b/src/handlers/RestHandler.ts @@ -76,8 +76,9 @@ export type RequestQuery = { } export interface RestRequestType< + BodyType extends DefaultRequestBodyType = DefaultRequestBodyType, ParamsType extends RequestParams = Record -> extends MockedRequest { +> extends MockedRequest { params: ParamsType } @@ -146,7 +147,7 @@ ${queryParams protected getPublicRequest( request: RequestType, parsedResult: ParsedResult, - ): RestRequestType { + ): RestRequestType { return { ...request, params: parsedResult.params, diff --git a/src/index.ts b/src/index.ts index 317add723..456648d1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,10 @@ export { graphql } from './graphql' export { GraphQLHandler, GraphQLContext, + GraphQLVariablesType, + GraphQLRequestType, + GraphQLRequestBodyType, + GraphQLJsonRequestBody, graphqlContext, } from './handlers/GraphQLHandler' diff --git a/src/rest.ts b/src/rest.ts index 465e34c1b..c972f0660 100644 --- a/src/rest.ts +++ b/src/rest.ts @@ -16,13 +16,13 @@ function createRestHandler(method: RESTMethods) { /** * @fixme Propagate request body type generic to the handler. */ - RequestBodyType = DefaultRequestBodyType, - ResponseBodyType = any, + RequestBodyType extends DefaultRequestBodyType = DefaultRequestBodyType, + ResponseBodyType extends DefaultRequestBodyType = any, RequestParamsType extends RequestParams = RequestParams >( mask: Mask, resolver: ResponseResolver< - RestRequestType, + RestRequestType, RestContext, ResponseBodyType >, diff --git a/test/typings/graphql.test-d.ts b/test/typings/graphql.test-d.ts new file mode 100644 index 000000000..2b3dc2a9a --- /dev/null +++ b/test/typings/graphql.test-d.ts @@ -0,0 +1,52 @@ +import { graphql } from 'msw' + +graphql.query<{ key: string }>('', (req, res, ctx) => { + return res( + ctx.data( + // @ts-expect-error Response data doesn't match the query type. + {}, + ), + ) +}) + +graphql.query< + { key: string }, + // @ts-expect-error `null` is not a valid variables type. + null +>('', (req, res, ctx) => { + return res(ctx.data({ key: 'pass' })) +}) + +graphql.mutation<{ key: string }>('', (req, res, ctx) => + res( + ctx.data( + // @ts-expect-error Response data doesn't match the query type. + {}, + ), + ), +) + +graphql.mutation< + { key: string }, + // @ts-expect-error `null` is not a valid variables type. + null +>('', (req, res, ctx) => { + return res(ctx.data({ key: 'pass' })) +}) + +graphql.operation<{ key: string }>((req, res, ctx) => { + return res( + ctx.data( + // @ts-expect-error Response data doesn't match the query type. + {}, + ), + ) +}) + +graphql.operation< + { key: string }, + // @ts-expect-error `null` is not a valid variables type. + null +>((req, res, ctx) => { + return res(ctx.data({ key: 'pass' })) +}) diff --git a/test/typings/rest.test-d.ts b/test/typings/rest.test-d.ts new file mode 100644 index 000000000..e57226fa1 --- /dev/null +++ b/test/typings/rest.test-d.ts @@ -0,0 +1,39 @@ +import { rest } from 'msw' + +rest.get<{ userId: string }, { postCount: number }>( + '/user', + (req, res, ctx) => { + req.body.userId + + // @ts-expect-error `session` property is not defined on the request body type. + req.body.session + + res( + // @ts-expect-error JSON doesn't match given response body generic type. + ctx.json({ unknown: true }), + ) + + res( + // @ts-expect-error value types do not match + ctx.json({ postCount: 'this is not a number' }), + ) + + return res(ctx.json({ postCount: 2 })) + }, +) + +rest.get('/user/:userId', (req, res, ctx) => { + req.params.userId + + // @ts-expect-error `unknown` is not defined in the request params type. + req.params.unknown +}) + +rest.post('/submit', () => null) + +rest.get< + any, + // @ts-expect-error `null` is not a valid response body type. + null +>('/user', () => null) diff --git a/test/typings/tsconfig.json b/test/typings/tsconfig.json new file mode 100644 index 000000000..84350b3c7 --- /dev/null +++ b/test/typings/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "moduleResolution": "Node", + "lib": ["dom"], + "baseUrl": ".", + "paths": { + "msw": ["../../"] + } + }, + "include": ["../../global.d.ts", "**/*.test-d.ts"], + "exclude": ["node_modules"] +}