forked from DevExpress/testcafe
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into DevExpressgh-1956
- Loading branch information
Showing
58 changed files
with
1,259 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { assertType, is } from '../../errors/runtime/type-assertions'; | ||
|
||
export default function assertRequestHookType (hooks) { | ||
hooks.forEach(hook => assertType(is.requestHookSubclass, 'requestHooks', `Hook`, hook)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { RequestFilterRule } from 'testcafe-hammerhead'; | ||
import { castArray } from 'lodash'; | ||
|
||
export default class RequestHook { | ||
constructor (requestFilterRules, responseEventConfigureOpts) { | ||
this.requestFilterRules = this._prepareRequestFilterRules(requestFilterRules); | ||
this._instantiatedRequestFilterRules = []; | ||
this.responseEventConfigureOpts = responseEventConfigureOpts; | ||
} | ||
|
||
_prepareRequestFilterRules (rules) { | ||
if (rules) | ||
return castArray(rules); | ||
|
||
return [RequestFilterRule.ANY]; | ||
} | ||
|
||
_instantiateRequestFilterRules () { | ||
this.requestFilterRules.forEach(rule => { | ||
if (rule instanceof RequestFilterRule) | ||
this._instantiatedRequestFilterRules.push(rule); | ||
else | ||
this._instantiatedRequestFilterRules.push(new RequestFilterRule(rule)); | ||
}); | ||
} | ||
|
||
onRequest (/*RequestEvent event*/) { | ||
throw new Error('Not implemented'); | ||
} | ||
|
||
_onConfigureResponse (event) { | ||
if (!this.responseEventConfigureOpts) | ||
return; | ||
|
||
event.opts.includeHeaders = this.responseEventConfigureOpts.includeHeaders; | ||
event.opts.includeBody = this.responseEventConfigureOpts.includeBody; | ||
} | ||
|
||
onResponse (/*ResponseEvent event*/) { | ||
throw new Error('Not implemented'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { ConfigureResponseEventOptions } from 'testcafe-hammerhead'; | ||
import RequestHook from './hook'; | ||
import { parse as parseUserAgent } from 'useragent'; | ||
import testRunTracker from '../test-run-tracker'; | ||
import ReExecutablePromise from '../../utils/re-executable-promise'; | ||
import { RequestHookConfigureAPIError } from '../../errors/test-run/index'; | ||
|
||
const DEFAULT_OPTIONS = { | ||
logRequestHeaders: false, | ||
logRequestBody: false, | ||
stringifyRequestBody: false, | ||
logResponseHeaders: false, | ||
logResponseBody: false, | ||
stringifyResponseBody: false | ||
}; | ||
|
||
class RequestLogger extends RequestHook { | ||
constructor (requestFilterRuleInit, options) { | ||
options = Object.assign({}, DEFAULT_OPTIONS, options); | ||
RequestLogger._assertLogOptions(options); | ||
|
||
const configureResponseEventOptions = new ConfigureResponseEventOptions(options.logResponseHeaders, options.logResponseBody); | ||
|
||
super(requestFilterRuleInit, configureResponseEventOptions); | ||
|
||
this.options = options; | ||
|
||
this._internalRequests = {}; | ||
} | ||
|
||
static _assertLogOptions (logOptions) { | ||
if (!logOptions.logRequestBody && logOptions.stringifyRequestBody) | ||
throw new RequestHookConfigureAPIError(RequestLogger.name, 'Cannot stringify the request body because it is not logged. Specify { logRequestBody: true } in log options.'); | ||
|
||
if (!logOptions.logResponseBody && logOptions.stringifyResponseBody) | ||
throw new RequestHookConfigureAPIError(RequestLogger.name, 'Cannot stringify the response body because it is not logged. Specify { logResponseBody: true } in log options.'); | ||
} | ||
|
||
onRequest (event) { | ||
const userAgent = parseUserAgent(event._requestInfo.userAgent).toString(); | ||
|
||
const loggedReq = { | ||
id: event._requestInfo.requestId, | ||
testRunId: event._requestInfo.sessionId, | ||
userAgent, | ||
request: { | ||
url: event._requestInfo.url, | ||
method: event._requestInfo.method, | ||
} | ||
}; | ||
|
||
if (this.options.logRequestHeaders) | ||
loggedReq.request.headers = Object.assign({}, event._requestInfo.headers); | ||
|
||
if (this.options.logRequestBody) | ||
loggedReq.request.body = this.options.stringifyRequestBody ? event._requestInfo.body.toString() : event._requestInfo.body; | ||
|
||
this._internalRequests[loggedReq.id] = loggedReq; | ||
} | ||
|
||
onResponse (event) { | ||
const loggerReq = this._internalRequests[event.requestId]; | ||
|
||
if (!loggerReq) | ||
throw new TypeError(`Cannot find a recorded request with id=${event.id}. This is an internal TestCafe problem. Please contact the TestCafe team and provide an example to reproduce the problem.`); | ||
|
||
loggerReq.response = {}; | ||
loggerReq.response.statusCode = event.statusCode; | ||
|
||
if (this.options.logResponseHeaders) | ||
loggerReq.response.headers = Object.assign({}, event.headers); | ||
|
||
if (this.options.logResponseBody) | ||
loggerReq.response.body = this.options.stringifyResponseBody ? event.body.toString() : event.body; | ||
} | ||
|
||
_prepareInternalRequestInfo () { | ||
const testRun = testRunTracker.resolveContextTestRun(); | ||
let preparedRequests = Object.values(this._internalRequests); | ||
|
||
if (testRun) | ||
preparedRequests = preparedRequests.filter(r => r.testRunId === testRun.id); | ||
|
||
return preparedRequests; | ||
} | ||
|
||
// API | ||
contains (predicate) { | ||
return ReExecutablePromise.fromFn(async () => { | ||
return !!this._prepareInternalRequestInfo().find(predicate); | ||
}); | ||
} | ||
|
||
count (predicate) { | ||
return ReExecutablePromise.fromFn(async () => { | ||
return this._prepareInternalRequestInfo().filter(predicate).length; | ||
}); | ||
} | ||
|
||
clear () { | ||
const testRun = testRunTracker.resolveContextTestRun(); | ||
|
||
if (testRun) { | ||
Object.keys(this._internalRequests).forEach(id => { | ||
if (this._internalRequests[id].testRunId === testRun.id) | ||
delete this._internalRequests[id]; | ||
}); | ||
} | ||
else | ||
this._internalRequests = {}; | ||
} | ||
|
||
get requests () { | ||
return this._prepareInternalRequestInfo(); | ||
} | ||
} | ||
|
||
export default function createRequestLogger (requestFilterRuleInit, logOptions) { | ||
return new RequestLogger(requestFilterRuleInit, logOptions); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import RequestHook from './hook'; | ||
import { ResponseMock, RequestFilterRule } from 'testcafe-hammerhead'; | ||
import { RequestHookConfigureAPIError } from '../../errors/test-run/index'; | ||
|
||
class RequestMock extends RequestHook { | ||
constructor () { | ||
super(); | ||
|
||
this.pendingRequestFilterRuleInit = null; | ||
this.mocks = new Map(); | ||
} | ||
|
||
onRequest (event) { | ||
const mock = this.mocks.get(event._requestFilterRule); | ||
|
||
event.setMock(mock); | ||
} | ||
|
||
onResponse () {} | ||
|
||
// API | ||
onRequestTo (requestFilterRuleInit) { | ||
if (this.pendingRequestFilterRuleInit) | ||
throw new RequestHookConfigureAPIError(RequestMock.name, "The 'respond' method was not called after 'onRequestTo'. You must call the 'respond' method to provide the mocked response."); | ||
|
||
this.pendingRequestFilterRuleInit = requestFilterRuleInit; | ||
|
||
return this; | ||
} | ||
|
||
respond (body, statusCode, headers) { | ||
if (!this.pendingRequestFilterRuleInit) | ||
throw new RequestHookConfigureAPIError(RequestMock.name, "The 'onRequestTo' method was not called before 'respond'. You must call the 'onRequestTo' method to provide the URL requests to which are mocked."); | ||
|
||
const mock = new ResponseMock(body, statusCode, headers); | ||
const rule = new RequestFilterRule(this.pendingRequestFilterRuleInit); | ||
|
||
this.requestFilterRules.push(rule); | ||
this.mocks.set(rule, mock); | ||
this.pendingRequestFilterRuleInit = null; | ||
|
||
return this; | ||
} | ||
} | ||
|
||
export default function createRequestMock () { | ||
return new RequestMock(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.