Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
Create_api_mock (#1)
Browse files Browse the repository at this point in the history
* create base tests

* start refining api_mock

* update api_mock

* lint

* make the ApiMock as robust as it can be

* update api_mock to mock xhr

* polish api_mock

* Get wrapper working. FIXME: button is not swapping wrapper modes
  • Loading branch information
elg0nz authored May 17, 2024
1 parent 26ea75e commit b5662b9
Show file tree
Hide file tree
Showing 15 changed files with 675 additions and 183 deletions.
5 changes: 4 additions & 1 deletion apps/owlserver-chrome/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"build:rollup": "pnpm exec rollup -c --bundleConfigAsCjs",
"build:copy": "cp ./src/manifest.json dist/ && cp -r ./assets dist/",
"build": "pnpm run build:clean && pnpm run build:rollup && pnpm run build:copy",
"test": "pnpm exec vitest"
"test": "pnpm exec vitest",
"lint": "biome ci .",
"format": "biome check --apply . && biome lint --apply ."
},
"keywords": [],
"author": "",
Expand All @@ -21,6 +23,7 @@
"@repo/ui": "workspace:*",
"@vscode/codicons": "^0.0.35",
"framer-motion": "^6",
"jsdom": "^24.0.0",
"next": "^14.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
13 changes: 12 additions & 1 deletion apps/owlserver-chrome/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,17 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab: chrome.tabs.Tab) => {
}
});

let webSocket = new WebSocket("ws://localhost:8080/ws");
let webSocket: WebSocket | null = null;
try {
webSocket = new WebSocket("ws://localhost:8080/ws");
} catch (error) {
console.error("websocket connection failed");
}

if (!webSocket) {
console.error("websocket connection failed");
}

webSocket.onopen = (event) => {
keepAlive();
};
Expand Down Expand Up @@ -146,6 +156,7 @@ function sendDataToServer(): void {
if (sessionId) {
payload.sessionId = sessionId;
}
console.log(`sending data to server: ${data}`);
webSocket.send(JSON.stringify(payload));
});

Expand Down
54 changes: 50 additions & 4 deletions apps/owlserver-chrome/src/components/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Button, HStack } from "@chakra-ui/react";
import { Box, Center, Text } from "@chakra-ui/react";
// biome-ignore lint/style/useImportType: false positive
import React, { useState, useEffect } from "react";
import RecordButton from "./record_button";
import RecordButton, { MOCKSI_CURRENT_STEP } from "./record_button";

function runStartRecording(isRecording: boolean) {
const action = isRecording ? "startRecording" : "stopRecording";
Expand All @@ -10,8 +11,17 @@ function runStartRecording(isRecording: boolean) {
});
}

function PopUp() {
// FIXME: create color constants
interface StepProps {
currentStep: string;
onNextStep: () => void;
}

const StepOne: React.FC<StepProps> = ({ currentStep, onNextStep }) => {
if (Number(currentStep) > 1) {
return null;
}

// FIXME: use correct color constants
return (
<Box maxW="sm" borderWidth="1px" borderRadius="lg" overflow="hidden">
<HStack>
Expand All @@ -23,13 +33,49 @@ function PopUp() {
</Center>
<Center w="80px" h="40px" bg="white" color="white">
<RecordButton onRecordChange={runStartRecording} />
<Button color="blue" isDisabled>
<Button
color="blue"
isDisabled={currentStep === "0"}
onClick={onNextStep}
>
Next
</Button>
</Center>
</HStack>
</Box>
);
};

const StepTwo: React.FC<StepProps> = ({ currentStep, onNextStep }) => {
if (Number(currentStep) !== 2) {
return null;
}
return <h1>Step two</h1>;
};

function PopUp() {
const [currentStep, setCurrentStep] = useState("0");

useEffect(() => {
const storedStep = localStorage.getItem(MOCKSI_CURRENT_STEP);
if (storedStep) {
setCurrentStep(storedStep);
}
});

const advanceToNextStep = () => {
const prevStep = Number(currentStep);
const nextStep = String(prevStep + 1);
localStorage.setItem(MOCKSI_CURRENT_STEP, nextStep);
setCurrentStep(nextStep);
};

return (
<>
<StepOne currentStep={currentStep} onNextStep={advanceToNextStep} />
<StepTwo currentStep={currentStep} onNextStep={advanceToNextStep} />
</>
);
}

export default PopUp;
24 changes: 23 additions & 1 deletion apps/owlserver-chrome/src/components/record_button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Button } from "@chakra-ui/react";
// biome-ignore lint/style/useImportType: it's ok
import React, { useState, useEffect } from "react";

const MOCKSI_RECORDING_STATE = "mocksi-recordingState";
const MOCKSI_RECORDING_STATE = "mocksi-recording-state";
export const MOCKSI_CURRENT_STEP = "mocksi-current-step";
interface RecordButtonProps {
onRecordChange: (isRecording: boolean) => void;
}
Expand All @@ -26,6 +27,27 @@ const RecordButton: React.FC<RecordButtonProps> = ({ onRecordChange }) => {
onRecordChange(newRecordingState);
setIsRecording(newRecordingState);
localStorage.setItem(MOCKSI_RECORDING_STATE, newRecordingState.toString());
if (newRecordingState) {
localStorage.setItem(MOCKSI_CURRENT_STEP, "1");
}
const type = newRecordingState
? "startRecordingAction"
: "stopRecordingAction";
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0].id) {
chrome.scripting.executeScript(
{
target: { tabId: tabs[0].id },
files: ["main.js"],
},
() => {
chrome.tabs.sendMessage(tabs[0].id, { type: type }, (response) => {
console.log("Response from content script:", response);
});
},
);
}
});
};

const label = isRecording ? "stop" : "start";
Expand Down
37 changes: 28 additions & 9 deletions apps/owlserver-chrome/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ import PopUp from "src/components/Popup";
const s = document.createElement("script");
s.src = chrome.runtime.getURL("wrappers.js");
(document.head || document.documentElement).appendChild(s);
let wrapperInstance = null;

s.onload = () => {
wrapperInstance = window.wrapper;
s.remove();
};

const root = document.getElementById("popup-container");
const rootDiv = ReactDOM.createRoot(root || document.createElement("div"));
rootDiv.render(
<React.StrictMode>
<ChakraProvider>
<PopUp />
</ChakraProvider>
</React.StrictMode>,
);
const rootDiv = document.getElementById("popup-container");
if (rootDiv) {
rootDiv.id = "popup-container";
const reactRoot = ReactDOM.createRoot(rootDiv);
reactRoot.render(
<React.StrictMode>
<ChakraProvider>
<PopUp />
</ChakraProvider>
</React.StrictMode>,
);
}

document.addEventListener("wrapperToBackground", (e): void => {
const jsonHolder = document.getElementById("jsonHolder");
Expand All @@ -33,3 +39,16 @@ document.addEventListener("wrapperToBackground", (e): void => {
},
);
});

// FIXME: this is not working
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.message === "startRecordingAction") {
wrapperInstance?.record();
}
if (message.message === "stopRecordingAction") {
wrapperInstance?.remove();
}
console.log("window.wrapper mode", wrapperInstance?.mode);
const status = wrapperInstance ? "success" : "error";
sendResponse({ status: status });
});
9 changes: 8 additions & 1 deletion apps/owlserver-chrome/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
"background": {
"service_worker": "background.js"
},
"permissions": ["activeTab", "webRequest", "downloads", "storage"],
"permissions": [
"activeTab",
"webRequest",
"downloads",
"storage",
"tabs",
"scripting"
],
"host_permissions": ["<all_urls>"]
}
112 changes: 112 additions & 0 deletions apps/owlserver-chrome/src/services/api_mock.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* @vitest-environment jsdom
*/

import { afterAll, beforeAll, describe, expect, test } from "vitest";
import ApiMock from "./api_mock";

// Mock setup to simulate a browser XMLHttpRequest environment
beforeAll(() => {
const config = { delay: 0, mockData };
const apiMock = new ApiMock(config);
globalThis.XMLHttpRequest =
apiMock.createFakeXHR() as unknown as typeof XMLHttpRequest;
});

afterAll(() => {
// biome-ignore lint/performance/noDelete: <explanation>
delete globalThis.XMLHttpRequest;
});

// Define mock data with various response types and scenarios
const mockData = {
"GET /api/text": {
ResponseBody: "Simple text response",
ResponseStatus: 200,
ResponseHeaders: { "Content-Type": "text/plain" },
ResponseType: "text",
},
"POST /api/json": {
ResponseBody: { message: "Data received" },
ResponseStatus: 201,
ResponseHeaders: { "Content-Type": "application/json" },
ResponseType: "json",
},
"PUT /api/blob": {
ResponseBody: new Blob(["binary data"], {
type: "application/octet-stream",
}),
ResponseStatus: 200,
ResponseHeaders: { "Content-Type": "application/octet-stream" },
ResponseType: "blob",
},
"DELETE /api/formData": {
ResponseBody: new FormData(),
ResponseStatus: 204,
ResponseHeaders: { "Content-Type": "multipart/form-data" },
ResponseType: "formData",
},
};

// Tests for different response types using XMLHttpRequest
describe("ApiMock tests for different HTTP methods and response types", () => {
test("should handle text response correctly", (done) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/text");
xhr.onload = () => {
expect(xhr.responseText).toBe("Simple text response");
expect(xhr.getResponseHeader("Content-Type")).toBe("text/plain");
done();
};
xhr.send();
});

test("should handle JSON response correctly", (done) => {
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/json");
xhr.onload = () => {
expect(JSON.parse(xhr.responseText)).toEqual({
message: "Data received",
});
expect(xhr.status).toBe(201);
done();
};
xhr.send();
});

test("should handle Blob response correctly", (done) => {
const xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.open("PUT", "/api/blob");
xhr.onload = () => {
expect(xhr.response.size).toBeGreaterThan(0);
expect(xhr.getResponseHeader("Content-Type")).toBe(
"application/octet-stream",
);
done();
};
xhr.send();
});

test("should handle FormData response correctly", (done) => {
const xhr = new XMLHttpRequest();
xhr.open("DELETE", "/api/formData");
xhr.onload = () => {
expect(xhr.status).toBe(204);
// FormData is not directly inspectable in the response, just check for correct handling
done();
};
xhr.send();
});

test("should handle aborted requests", (done) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/text");
xhr.onerror = () => {
expect(xhr.statusText).toBe("AbortError");
done();
};
xhr.send();
xhr.abort();
});
});
Loading

0 comments on commit b5662b9

Please sign in to comment.