diff --git a/lib/egg.js b/lib/egg.js index 9dfee22184..ad8df1a7e8 100644 --- a/lib/egg.js +++ b/lib/egg.js @@ -1,5 +1,6 @@ const { performance } = require('perf_hooks'); const path = require('path'); +const querystring = require('querystring'); const fs = require('fs'); const ms = require('ms'); const http = require('http'); @@ -588,6 +589,16 @@ class EggApplication extends EggCore { } } } + // Koa reads querystring from req.url, so normalize mocked query values. + if (req && Object.prototype.hasOwnProperty.call(req, 'url')) { + // An explicitly supplied URL takes precedence. + } else if (request.querystring) { + const requestPath = request.path || '/'; + const separator = requestPath.includes('?') ? '&' : '?'; + request.url = `${requestPath}${separator}${request.querystring}`; + } else if (req && req.query && Object.keys(req.query).length) { + request.url = `${request.path || '/'}?${querystring.stringify(req.query)}`; + } const response = new http.ServerResponse(request); return this.createContext(request, response); } diff --git a/test/lib/egg.test.js b/test/lib/egg.test.js index 672a1ae97e..cb988f23ac 100644 --- a/test/lib/egg.test.js +++ b/test/lib/egg.test.js @@ -481,6 +481,22 @@ describe('test/lib/egg.test.js', () => { ctx = app.agent.createAnonymousContext(); assert(ctx); }); + + it('should apply mocked querystring and query', () => { + let ctx = app.createAnonymousContext({ querystring: 'page=1&size=10' }); + assert(ctx.url === '/?page=1&size=10'); + assert.deepEqual(ctx.query, { page: '1', size: '10' }); + + ctx = app.createAnonymousContext({ query: { page: 1 } }); + assert(ctx.url === '/?page=1'); + assert.deepEqual(ctx.query, { page: '1' }); + }); + + it('should keep an explicitly supplied URL', () => { + const ctx = app.createAnonymousContext({ url: '/users?from=url', query: { page: 1 } }); + assert(ctx.url === '/users?from=url'); + assert.deepEqual(ctx.query, { from: 'url' }); + }); }); });