This repository has been archived by the owner on Sep 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Get wrapper working. FIXME: button is not swapping wrapper modes
- Loading branch information
Showing
6 changed files
with
149 additions
and
120 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 |
---|---|---|
@@ -1,103 +1,80 @@ | ||
import { | ||
ActionType, | ||
sendToBackground, | ||
} from "../services/background_interactor"; | ||
import { ActionType, sendToBackground } from "./background_interactor"; | ||
|
||
class ApiRecorder { | ||
private XHR: typeof XMLHttpRequest; | ||
private originalXHR: typeof XMLHttpRequest; | ||
// TODO: do these need to be protected or private? | ||
protected requestHeaders: Record<string, string>; | ||
protected _method: string; | ||
protected _url: string; | ||
class APIRecorder extends XMLHttpRequest { | ||
private _method: string; | ||
private _url: string; | ||
private _requestHeaders: Record<string, string> = {}; | ||
// biome-ignore lint/suspicious/noExplicitAny: it's fine | ||
private _postData: any; | ||
private _startTime: string; | ||
|
||
constructor() { | ||
this.XHR = XMLHttpRequest; | ||
this.originalXHR = XMLHttpRequest; | ||
this.requestHeaders = {}; | ||
this.setupXHR(); | ||
open( | ||
method: string, | ||
url: string | URL, | ||
async = true, | ||
username?: string, | ||
password?: string, | ||
): void { | ||
this._method = method; | ||
this._url = url.toString(); | ||
this._requestHeaders = {}; | ||
this._startTime = new Date().toISOString(); | ||
this._postData = {}; | ||
super.open(method, url.toString(), async, username, password); | ||
} | ||
|
||
setupXHR() { | ||
const open = this.originalXHR.prototype.open; | ||
const send = this.originalXHR.prototype.send; | ||
const setRequestHeader = this.originalXHR.prototype.setRequestHeader; | ||
|
||
this.XHR.prototype.open = function ( | ||
method: string, | ||
url: string | URL, | ||
...args | ||
) { | ||
this._method = method; | ||
this._url = url.toString(); | ||
return open.call(this, method, url, ...args); | ||
}; | ||
|
||
this.XHR.prototype.setRequestHeader = function ( | ||
header: string, | ||
value: string, | ||
...args | ||
) { | ||
this.requestHeaders = this.requestHeaders || {}; | ||
this._requestHeaders[header] = value; | ||
return setRequestHeader.call(this, header, value, ...args); | ||
}; | ||
setRequestHeader(header: string, value: string): void { | ||
this._requestHeaders[header] = value; | ||
super.setRequestHeader(header, value); | ||
} | ||
|
||
this.XHR.prototype.send = function (postData?: Document | BodyInit | null) { | ||
this._postData = postData; | ||
this.addEventListener("load", () => { | ||
sendToBackground(this._method, ActionType.RequestMethod); | ||
sendToBackground(this._url.toLowerCase(), ActionType.RequestUrl); | ||
sendToBackground( | ||
JSON.stringify(this._postData), | ||
ActionType.RequestBody, | ||
); | ||
sendToBackground( | ||
JSON.stringify(this._requestHeaders), | ||
ActionType.RequestHeaders, | ||
); | ||
send(postData?: Document | BodyInit | null): void { | ||
this._postData = postData; | ||
this.addEventListener("load", () => { | ||
sendToBackground(this._method, ActionType.RequestMethod); | ||
sendToBackground(this._url.toLowerCase(), ActionType.RequestUrl); | ||
sendToBackground(JSON.stringify(this._postData), ActionType.RequestBody); | ||
sendToBackground( | ||
JSON.stringify(this._requestHeaders), | ||
ActionType.RequestHeaders, | ||
); | ||
|
||
const responseHeaders = this.getAllResponseHeaders(); | ||
sendToBackground( | ||
JSON.stringify(responseHeaders), | ||
ActionType.ResponseHeaders, | ||
); | ||
const responseHeaders = this.getAllResponseHeaders(); | ||
sendToBackground( | ||
JSON.stringify(responseHeaders), | ||
ActionType.ResponseHeaders, | ||
); | ||
|
||
let responseBody = ""; | ||
switch (this.responseType) { | ||
case "": | ||
case "text": | ||
responseBody = this.responseText; | ||
break; | ||
case "arraybuffer": { | ||
const buffer = new Uint8Array(this.response); | ||
const decoder = new TextDecoder(); | ||
responseBody = decoder.decode(buffer); | ||
break; | ||
} | ||
case "blob": { | ||
responseBody = "BLOB"; | ||
break; | ||
} | ||
case "document": { | ||
const serializer = new XMLSerializer(); | ||
responseBody = serializer.serializeToString(this.response); | ||
break; | ||
} | ||
case "json": | ||
responseBody = JSON.stringify(this.response); | ||
break; | ||
let responseBody = ""; | ||
switch (this.responseType) { | ||
case "": | ||
case "text": | ||
responseBody = this.responseText; | ||
break; | ||
case "arraybuffer": { | ||
const buffer = new Uint8Array(this.response); | ||
const decoder = new TextDecoder(); | ||
responseBody = decoder.decode(buffer); | ||
break; | ||
} | ||
if (this.responseType !== "blob") { | ||
// Ensure we do not send blob data synchronously | ||
sendToBackground(responseBody, ActionType.ResponseBody); | ||
case "blob": | ||
responseBody = "BLOB"; | ||
break; | ||
case "document": { | ||
const serializer = new XMLSerializer(); | ||
responseBody = serializer.serializeToString(this.response); | ||
break; | ||
} | ||
}); | ||
|
||
// biome-ignore lint/style/noArguments: dynamic | ||
return send.apply(this, arguments); | ||
}; | ||
case "json": | ||
responseBody = JSON.stringify(this.response); | ||
break; | ||
} | ||
if (this.responseType !== "blob") { | ||
sendToBackground(responseBody, ActionType.ResponseBody); | ||
} | ||
}); | ||
super.send(postData as XMLHttpRequestBodyInit); | ||
} | ||
} | ||
|
||
export default ApiRecorder; | ||
export default APIRecorder; |
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 |
---|---|---|
@@ -1,43 +1,51 @@ | ||
import ApiMock, { type MockResponse } from "./services/api_mock"; | ||
// Import necessary classes and types | ||
import APIRecorder from "./services/api_recorder"; | ||
|
||
// Define the mode types | ||
enum ModeType { | ||
Record = "record", | ||
Mock = "mock", | ||
Original = "original", | ||
} | ||
|
||
// biome-ignore lint/complexity/noStaticOnlyClass: wrapper got to wrap. | ||
// Declare new property on the window object | ||
declare global { | ||
interface Window { | ||
wrapper: typeof XHRWrapper; | ||
} | ||
} | ||
|
||
// Define the XHRWrapper class | ||
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation> | ||
class XHRWrapper extends XMLHttpRequest { | ||
public static originalXHR = XMLHttpRequest; | ||
// biome-ignore lint/suspicious/noExplicitAny: it's ok: | ||
public static injectedXHR: any = XMLHttpRequest; | ||
public static mockData: Record<string, MockResponse>; | ||
public static originalXHR: typeof XMLHttpRequest = XMLHttpRequest; | ||
public static injectedXHR: typeof XMLHttpRequest = XMLHttpRequest; | ||
public static mode: ModeType = ModeType.Original; | ||
static XHRWrapper: typeof XHRWrapper; | ||
|
||
// Set the mode to record | ||
static record() { | ||
XHRWrapper.injectedXHR = APIRecorder; // Assuming APIRecorder extends XMLHttpRequest | ||
XHRWrapper.wrap(ModeType.Record); | ||
} | ||
|
||
static mock(mockData: Record<string, MockResponse>) { | ||
XHRWrapper.injectedXHR = ApiMock; // Assuming ApiMock is compatible with XMLHttpRequest interface | ||
XHRWrapper.mockData = mockData; | ||
XHRWrapper.wrap(ModeType.Mock); | ||
} | ||
|
||
// Revert to the original XMLHttpRequest | ||
static remove() { | ||
XHRWrapper.injectedXHR = XMLHttpRequest; | ||
XHRWrapper.injectedXHR = XHRWrapper.originalXHR; | ||
XHRWrapper.wrap(ModeType.Original); | ||
} | ||
|
||
// Wrap the XMLHttpRequest with the specified mode | ||
static wrap(mode: ModeType) { | ||
XHRWrapper.mode = mode; | ||
const xhrFactory = () => new XHRWrapper.injectedXHR(); | ||
// Copy over all static properties and methods from XMLHttpRequest to xhrFactory | ||
Object.assign(xhrFactory, XMLHttpRequest); | ||
window.XMLHttpRequest = xhrFactory as unknown as typeof XMLHttpRequest; | ||
((xhr) => XHRWrapper.injectedXHR)(XMLHttpRequest); | ||
window.XMLHttpRequest = XMLHttpRequest; | ||
} | ||
} | ||
|
||
if (typeof window !== "undefined") { | ||
XHRWrapper.record(); // Supply necessary mock data | ||
window.wrapper = XHRWrapper; // Make the wrapper available globally on the window object | ||
} | ||
|
||
// Export the XHRWrapper class as a default export | ||
export default XHRWrapper; |
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