Skip to content

Commit

Permalink
fix: request action returns bad request if request method is specifie…
Browse files Browse the repository at this point in the history
…d in lowercase in native automation(closes #7609) (#7633)

## Purpose
Request action returns bad request if request method is specified in
lowercase. The issue is only in native automation mode, because the
request is not fully handled by hammerhead.

## Approach
Use uppercase
## References
#7609

## Pre-Merge TODO
- [ ] Write tests for your proposed changes
- [ ] Make sure that existing tests do not fail
  • Loading branch information
Artem-Babich authored Apr 17, 2023
1 parent e265b30 commit f84ded2
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 13 deletions.
27 changes: 14 additions & 13 deletions src/test-run/request/create-request-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { GetProxyUrlCommand } from '../commands/actions';
import { CallsiteRecord } from 'callsite-record';

const DEFAULT_ACCEPT = { [HTTP_HEADERS.accept]: `${CONTENT_TYPES.json}, ${CONTENT_TYPES.textPlain}, ${CONTENT_TYPES.all}` };
const METHODS_WITH_CONTENT_TYPE = ['post', 'put', 'patch'];
const METHODS_WITH_CONTENT_TYPE = ['POST', 'PUT', 'PATCH'];
const DEFAULT_REQUEST_METHOD = 'GET';
const DEFAULT_PROTOCOL = 'http:';

Expand Down Expand Up @@ -92,23 +92,23 @@ function changeHeaderNamesToLowercase (headers: OutgoingHttpHeaders): OutgoingHt
return lowerCaseHeaders;
}

async function prepareHeaders (headers: OutgoingHttpHeaders, currentPageUrl: URL, url: URL, body: Buffer, testRun: TestRun, withCredentials: boolean, options: ExternalRequestOptions): Promise<OutgoingHttpHeaders> {
const { host, origin } = url;

const preparedHeaders: OutgoingHttpHeaders = Object.assign({}, DEFAULT_ACCEPT, changeHeaderNamesToLowercase(headers));
async function prepareHeaders (options: ExternalRequestOptions, currentPageUrl: URL, url: URL, body: Buffer, testRun: TestRun, withCredentials: boolean): Promise<OutgoingHttpHeaders> {
const { host, origin } = url;
const { method, proxy, auth, headers = {} } = options;
const preparedHeaders: OutgoingHttpHeaders = Object.assign({}, DEFAULT_ACCEPT, changeHeaderNamesToLowercase(headers));

preparedHeaders[HTTP_HEADERS.host] = host;
preparedHeaders[HTTP_HEADERS.origin] = origin;
preparedHeaders[HTTP_HEADERS.contentLength] = body.length;

if (headers.method && METHODS_WITH_CONTENT_TYPE.includes(String(headers.method)))
preparedHeaders[HTTP_HEADERS.contentType] = CONTENT_TYPES.urlencoded;
if (method && METHODS_WITH_CONTENT_TYPE.includes(String(method)))
preparedHeaders[HTTP_HEADERS.contentType] = preparedHeaders[HTTP_HEADERS.contentType] || CONTENT_TYPES.urlencoded;

if (options.auth && withCredentials)
preparedHeaders[HTTP_HEADERS.authorization] = getAuthString(options.auth);
if (auth && withCredentials)
preparedHeaders[HTTP_HEADERS.authorization] = getAuthString(auth);

if (options.proxy?.auth)
preparedHeaders[HTTP_HEADERS.proxyAuthorization] = getAuthString(options.proxy.auth);
if (proxy?.auth)
preparedHeaders[HTTP_HEADERS.proxyAuthorization] = getAuthString(proxy.auth);

if (withCredentials) {
const currentPageCookies = await testRun.cookieProvider.getCookieHeader(currentPageUrl.href, currentPageUrl.hostname);
Expand Down Expand Up @@ -199,11 +199,12 @@ function resolveUrlParts (testRun: TestRun, url: URL, withCredentials: boolean):

export async function createRequestOptions (currentPageUrl: URL, testRun: TestRun, options: ExternalRequestOptions, callsite: CallsiteRecord | null): Promise<RequestOptions> {
options.headers = options.headers || {};
options.method = options.method?.toUpperCase() || DEFAULT_REQUEST_METHOD;

const url = await prepareUrl(testRun, currentPageUrl, options.url, callsite);
const withCredentials = !currentPageUrl.host || sameOriginCheck(currentPageUrl.href, url.href) || options.withCredentials || false;
const body = transformBody(options.headers, options.body);
const headers = await prepareHeaders(options.headers, currentPageUrl, url, body, testRun, withCredentials, options);
const headers = await prepareHeaders(options, currentPageUrl, url, body, testRun, withCredentials);
let auth = options.auth;

const {
Expand All @@ -222,7 +223,7 @@ export async function createRequestOptions (currentPageUrl: URL, testRun: TestRu
}

const requestParams: RequestOptionsParams = {
method: options.method || DEFAULT_REQUEST_METHOD,
method: options.method,
url: href,
protocol: protocol,
hostname: hostname,
Expand Down
8 changes: 8 additions & 0 deletions test/functional/fixtures/api/es-next/request/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ describe('Request', () => {
return runTests('testcafe-fixtures/request-test.js', 'Should execute a request with relative url');
});

it('Should execute a GET HTTPS request with method in lowercase', function () {
return runTests('testcafe-fixtures/request-test.js', 'Should execute a GET HTTPS request with method in lowercase');
});

it('Should set a content type for POST request', function () {
return runTests('testcafe-fixtures/request-test.js', 'Should set a content type for POST request');
});

if (config.useLocalBrowsers) {
it('Should rise request runtime error', function () {
return runTests('testcafe-fixtures/request-test.js', 'Should rise request runtime error', { shouldFail: true })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,36 @@ test('Should execute a GET HTTPS request', async (t) => {
position: 'CTO',
});
});


test('Should execute a GET HTTPS request with method in lowercase', async (t) => {
const {
status,
statusText,
headers,
body,
} = await t.request(HTTPS_API_URL, {
method: 'get',
});

await t
.expect(status).eql(200)
.expect(statusText).eql('OK')
.expect(headers).contains({ 'content-type': 'application/json; charset=utf-8' })
.expect(body).eql({
name: 'John Hearts',
position: 'CTO',
});
});

test('Should set a content type for POST request', async (t) => {
const options = {
method: 'POST',
};

const data = await t.request(`${API_URL}/request-info`, options);

await t.expect(data.body.headers['content-type']).eql('application/x-www-form-urlencoded');
});


6 changes: 6 additions & 0 deletions test/functional/site/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ router.post('/data', (req, res) => {
res.send(responses.handlePostResult(req.body));
});

router.post('/request-info', (req, res) => {
res.send({
headers: req.headers,
});
});

router.delete('/data/:dataId', (req, res) => {
res.send(responses.handleDeleteResult(req.params));
});
Expand Down

0 comments on commit f84ded2

Please sign in to comment.