Skip to content

Commit

Permalink
native automation changes (#2909)
Browse files Browse the repository at this point in the history
  • Loading branch information
miherlosev authored Jun 22, 2023
1 parent 1193737 commit c1b8058
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 130 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "testcafe-hammerhead",
"description": "A powerful web-proxy used as a core for the TestCafe testing framework (https://github.com/DevExpress/testcafe).",
"version": "31.4.5",
"version": "31.4.6",
"homepage": "https://github.com/DevExpress/testcafe-hammerhead",
"bugs": {
"url": "https://github.com/DevExpress/testcafe-hammerhead/issues"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import RequestFilterRule from '../request-filter-rule';
import RequestPipelineContext from '../../context';
import ConfigureResponseEventOptions from './configure-response-event-options';
import generateUniqueId from '../../../utils/generate-unique-id';

interface SerializedConfigureResponseEvent {
requestFilterRule: RequestFilterRule;
_requestContext: RequestPipelineContext;
opts: ConfigureResponseEventOptions;
id: string;
}

interface ModifyResponseFunctions {
setHeader: (name: string, value: string) => void;
Expand Down Expand Up @@ -41,14 +34,4 @@ export default class ConfigureResponseEvent {

this._modifyResponseFunctions.removeHeader(name);
}

public static from (data: unknown): ConfigureResponseEvent {
const { id, opts, requestFilterRule } = data as SerializedConfigureResponseEvent;

const configureResponseEvent = new ConfigureResponseEvent(requestFilterRule, null, opts);

configureResponseEvent.id = id;

return configureResponseEvent;
}
}
31 changes: 11 additions & 20 deletions src/request-pipeline/request-hooks/events/request-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import generateUniqueId from '../../../utils/generate-unique-id';
import RequestOptions from '../../request-options';
import { RequestInfoInit } from '../typings';

interface SerializedRequestEvent {
requestFilterRule: RequestFilterRule;
_requestInfo: RequestInfo;
id: string;
}

export default class RequestEvent {
public readonly requestFilterRule: RequestFilterRule;
Expand All @@ -20,10 +15,21 @@ export default class RequestEvent {

public constructor (init: RequestInfoInit) {
Object.assign(this, init);
this._setRequestOptionsTracking(this.reqOpts);

this.id = generateUniqueId();
}

private _setRequestOptionsTracking (reqOpts: RequestOptions): void {
reqOpts.on('headerChanged', changedHeader => {
this._requestInfo.headers[changedHeader.name] = changedHeader.value;
});

reqOpts.on('headerRemoved', name => {
delete this._requestInfo.headers[name];
});
}

public async setMock (mock: ResponseMock): Promise<void> {
await this.setMockFn(this.id, mock);
}
Expand All @@ -35,19 +41,4 @@ export default class RequestEvent {
public get isAjax (): boolean {
return this._requestInfo.isAjax;
}

public static from (data: unknown): RequestEvent {
const { id, requestFilterRule, _requestInfo } = data as SerializedRequestEvent;

const requestEvent = new RequestEvent({
requestFilterRule: requestFilterRule,
reqOpts: {} as RequestOptions,
setMockFn: () => Promise.resolve(),
_requestInfo,
});

requestEvent.id = id;

return requestEvent;
}
}
37 changes: 0 additions & 37 deletions src/request-pipeline/request-hooks/events/response-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,6 @@ import { PreparedResponseInfo } from './info';
import generateUniqueId from '../../../utils/generate-unique-id';
import { OutgoingHttpHeaders } from 'http';

interface SerializedResponseEvent {
requestFilterRule: RequestFilterRule;
id: string;
requestId: string;
statusCode: number;
sessionId: string;
isSameOriginPolicyFailed: boolean;
headers?: OutgoingHttpHeaders;
body?: Buffer;
}

export default class ResponseEvent {
public requestFilterRule: RequestFilterRule;
public readonly requestId: string;
Expand Down Expand Up @@ -44,30 +33,4 @@ export default class ResponseEvent {
this.id = generateUniqueId();
this.requestFilterRule = requestFilterRule;
}

public static from (data: unknown): ResponseEvent {
const {
requestFilterRule,
id,
requestId,
statusCode,
sessionId,
isSameOriginPolicyFailed,
headers,
body,
} = data as SerializedResponseEvent;

const responseEvent = new ResponseEvent(requestFilterRule, {
requestId,
statusCode,
sessionId,
isSameOriginPolicyFailed,
headers,
body,
});

responseEvent.id = id;

return responseEvent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export default async function (mock: ResponseMock): Promise<IncomingMessageLike>

try {
response = Object.assign(response, await mock.body(mock.requestOptions, response));

if (typeof response.statusCode !== 'number')
response.statusCode = Number(response.statusCode);
}
catch (err) {
response.statusCode = 500;
Expand Down
67 changes: 66 additions & 1 deletion src/request-pipeline/request-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import matchUrl from 'match-url-wildcard';
import { RequestTimeout } from '../typings/proxy';
import generateUniqueId from '../utils/generate-unique-id';
import { addAuthorizationPrefix } from '../utils/headers';
import { EventEmitter } from 'events';

const DEFAULT_REQUEST_OPTIONS = {
isAjax: false,
Expand All @@ -22,6 +23,11 @@ const DEFAULT_REQUEST_OPTIONS = {
},
};

interface ChangedProperty {
name: string;
value: unknown;
}

export default class RequestOptions {
url: string;
protocol: string;
Expand All @@ -46,12 +52,67 @@ export default class RequestOptions {
requestTimeout?: RequestTimeout;
isWebSocket: boolean;
disableHttp2: boolean;
_changedUrlProperties: ChangedProperty[] = [];
_changedHeaders: ChangedProperty[] = [];
_removedHeaders: string[] = [];
_eventEmitter: EventEmitter = new EventEmitter();

constructor (params: RequestOptionsInit) {
constructor (params: RequestOptionsInit, trackChanges = false) {
Object.assign(this, DEFAULT_REQUEST_OPTIONS, params);

this._applyExternalProxySettings();
this.prepare();

if (trackChanges)
return this._setTrackChanges(this);
}

private _setTrackChanges (obj: RequestOptions): RequestOptions {
// NOTE: this code is necessary to support request modification inside RequestHooks
// in the 'nativeAutomation' mode.
const self = obj;

obj.headers = new Proxy(obj.headers, {
set (target: OutgoingHttpHeaders, propName: string, newValue: any): boolean {
if (target[propName] !== newValue) {
const changedHeader = {
name: propName,
value: newValue,
};

self._changedHeaders.push(changedHeader);
self._eventEmitter.emit('headerChanged', changedHeader);
}

return Reflect.set(target, propName, newValue);
},
deleteProperty (target: OutgoingHttpHeaders, propName: string): boolean {
if (propName in target) {
self._removedHeaders.push(propName);
self._eventEmitter.emit('headerRemoved', propName);
}

return Reflect.deleteProperty(target, propName);
},
});

obj = new Proxy(obj, {
set (target: RequestOptions, propName: string, newValue: any): boolean {
if (target[propName] !== newValue) {
const changedUrlProperty = {
name: propName,
value: newValue,
};

self._changedUrlProperties.push(changedUrlProperty);
self._eventEmitter.emit('urlPropertyChange', changedUrlProperty);
}

return Reflect.set(target, propName, newValue);
},
});

return obj;
}

public static createFrom (ctx: RequestPipelineContext): RequestOptions {
Expand Down Expand Up @@ -129,4 +190,8 @@ export default class RequestOptions {

return clonedReqOptions;
}

on (event: string, listener: (...args: any[]) => void): void {
this._eventEmitter.on(event, listener);
}
}
Loading

0 comments on commit c1b8058

Please sign in to comment.