diff --git a/prompt-manager/interface/Prompt.ts b/prompt-manager/interface/Prompt.ts index ec800a3..3062a0f 100644 --- a/prompt-manager/interface/Prompt.ts +++ b/prompt-manager/interface/Prompt.ts @@ -1,6 +1,8 @@ export interface Prompt { - title: string; - prompt: string; - tags: string[]; - category?: string; - } + title: string; + prompt: string; + tags: string[]; + category?: string; + createdAt: Date; + website: string; +} diff --git a/prompt-manager/jest.config.mjs b/prompt-manager/jest.config.mjs index 54c9829..1bc165b 100644 --- a/prompt-manager/jest.config.mjs +++ b/prompt-manager/jest.config.mjs @@ -17,8 +17,17 @@ const config = { }), testEnvironment: "jsdom", transform: { - "^.+.tsx?$": ["ts-jest", {}], + "^.+\\.(ts|tsx|js|jsx)$": ["ts-jest", { + useESM: true, + isolatedModules: true + }], }, + transformIgnorePatterns: [ + "node_modules/(?!(@plasmohq|pify)/)" + ], + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "mjs"], + // Add this to tell Jest to mock this module + moduleDirectories: ["node_modules", ""] } -export default config +export default config \ No newline at end of file diff --git a/prompt-manager/package.json b/prompt-manager/package.json index a93e822..d9f389e 100644 --- a/prompt-manager/package.json +++ b/prompt-manager/package.json @@ -12,6 +12,10 @@ { "name": "Ife Adeyefa", "email": "iadeyefa@ufl.edu" + }, + { + "name": "Robert Barens", + "email": "rjbarnes@ufl.edu" } ], "scripts": { @@ -45,7 +49,10 @@ }, "manifest": { "host_permissions": [ - "https://*/*" + "https://chatgpt.com/*", + "https://chat.chatgpt.com/*", + "https://deepseek.com/*", + "https://chat.deepseek.com/*" ], "permissions": [ "storage", diff --git a/prompt-manager/popup.tsx b/prompt-manager/popup.tsx index 51bd70e..5036427 100644 --- a/prompt-manager/popup.tsx +++ b/prompt-manager/popup.tsx @@ -5,6 +5,7 @@ import { usePrompts } from "~hooks/usePrompts" import { ImportBtn } from "~components/ImportBtn"; import { ExportBtn } from "~components/ExportBtn"; + function IndexPopup() { const { prompts, setPrompts } = usePrompts(); const [userInput, setUserInput] = useState("") @@ -25,19 +26,31 @@ function IndexPopup() { } }, [prompts]) - const savePrompt = () => { - if (userInput.trim() !== "") { - const newPrompt: Prompt = { - title: "", - tags: [], - prompt: userInput, - category: currentCategory - } + const savePrompt = async () => { + if (userInput.trim() !== "") { + let website = "" + + try { + website = await useCurrentTabUrl() + } catch (err) { + console.error("Could not get tab URL:", err) + website = "unknown" + } - setPrompts((existingPrompts) => [...existingPrompts, newPrompt]) - setUserInput("") + const newPrompt: Prompt = { + title: "", + tags: [], + prompt: userInput, + category: currentCategory, + createdAt: new Date(), + website } + + setPrompts((existingPrompts) => [...existingPrompts, newPrompt]) + setUserInput("") } +} + const createNewCategory = () => { if (newCategory.trim() !== "" && !categories.includes(newCategory)) { @@ -171,4 +184,4 @@ function IndexPopup() { ) } -export default IndexPopup \ No newline at end of file +export default IndexPopup diff --git a/prompt-manager/tests/components/PromptList.test.tsx b/prompt-manager/tests/components/PromptList.test.tsx index f3de38d..33efb43 100644 --- a/prompt-manager/tests/components/PromptList.test.tsx +++ b/prompt-manager/tests/components/PromptList.test.tsx @@ -7,17 +7,23 @@ const mockPromptListData: Prompt[] = [ { title: "Sample Prompt", prompt: "This is a sample prompt description.", - tags: ["Test Tag"] + tags: ["Test Tag"], + createdAt: new Date(), + website: "https://chatgpt.com/" }, { title: "Another Prompt", prompt: "This is another sample prompt description.", - tags: ["Another Tag"] + tags: ["Another Tag"], + createdAt: new Date(), + website: "https://chatgpt.com/" }, { title: "Searchable Prompt", prompt: "This prompt is meant to be found.", - tags: ["Search Tag"] + tags: ["Search Tag"], + createdAt: new Date(), + website: "https://chatgpt.com/" } ]; diff --git a/prompt-manager/tests/components/PromptListItem.test.tsx b/prompt-manager/tests/components/PromptListItem.test.tsx index 6d87ec0..d5726c8 100644 --- a/prompt-manager/tests/components/PromptListItem.test.tsx +++ b/prompt-manager/tests/components/PromptListItem.test.tsx @@ -7,6 +7,8 @@ const mockPrompt: Prompt = { title: "Test Prompt", prompt: "This is a test prompt description.", tags: ["Tag1", "Tag2"], + createdAt: new Date(), + website: "https://chatgpt.com/" }; describe("PromptListItem", () => { diff --git a/prompt-manager/tests/hooks/usePrompts.test.ts b/prompt-manager/tests/hooks/usePrompts.test.ts index 74031a4..3e41133 100644 --- a/prompt-manager/tests/hooks/usePrompts.test.ts +++ b/prompt-manager/tests/hooks/usePrompts.test.ts @@ -11,7 +11,8 @@ jest.mock("@plasmohq/storage/hook", () => ({ useStorage: jest.fn() })) -const newPrompt = { title: "Test Title", prompt: "Test Prompt", tags: [] } +const newPrompt = { title: "Test Title", prompt: "Test Prompt", tags: [], createdAt: new Date(), + website: "https://chatgpt.com/" } describe("usePrompts", () => { it("should return an empty array as the initial value for prompts", () => { @@ -26,7 +27,11 @@ describe("usePrompts", () => { it("should return the stored prompts from the list if they exist", () => { const mockPrompts: Prompt[] = [ - { title: "Test Title", prompt: "Test Prompt", tags: [] } + { title: "Test Title", + prompt: "Test Prompt", + tags: [], + createdAt: new Date(), + website: "https://chatgpt.com/" } ] const mockUseStorage = jest.fn(() => [mockPrompts, jest.fn()]) ;(useStorage as jest.Mock).mockImplementation(mockUseStorage) diff --git a/prompt-manager/tests/mocks/storageMock.ts b/prompt-manager/tests/mocks/storageMock.ts new file mode 100644 index 0000000..2ee2a3e --- /dev/null +++ b/prompt-manager/tests/mocks/storageMock.ts @@ -0,0 +1,4 @@ +// Mock implementation of @plasmohq/storage/hook +export const useStorage = jest.fn().mockImplementation((key, initialValue) => { + return [initialValue, jest.fn()] + }) \ No newline at end of file diff --git a/prompt-manager/tests/popup/IndexPopup.test.tsx b/prompt-manager/tests/popup/IndexPopup.test.tsx new file mode 100644 index 0000000..b22583e --- /dev/null +++ b/prompt-manager/tests/popup/IndexPopup.test.tsx @@ -0,0 +1,60 @@ +import { describe, expect, it, jest } from "@jest/globals"; +import { render, screen, fireEvent } from "@testing-library/react"; +import IndexPopup from "~popup"; +import { usePrompts } from "~hooks/usePrompts"; + +jest.mock("~hooks/usePrompts", () => ({ + usePrompts: jest.fn() +})); + +describe("IndexPopup - Saving and Searching Prompts", () => { + const mockSetPrompts = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + (usePrompts as jest.Mock).mockReturnValue({ + prompts: [], + setPrompts: mockSetPrompts + }); + }); + + describe("Saving Prompts", () => { + it("should add a new prompt when save button is clicked", () => { + render(); + + const promptInput = screen.getByPlaceholderText("Enter Prompt"); + fireEvent.change(promptInput, { target: { value: "New Test Prompt" } }); + + const saveButton = screen.getByText("Save Prompt"); + fireEvent.click(saveButton); + + expect(mockSetPrompts).toHaveBeenCalled(); + }); + + it("should not add a prompt if the input is empty", () => { + render(); + + const saveButton = screen.getByText("Save Prompt"); + fireEvent.click(saveButton); + + expect(mockSetPrompts).not.toHaveBeenCalled(); + }); + }); + + describe("Searching Prompts", () => { + it("should render search input", () => { + (usePrompts as jest.Mock).mockReturnValue({ + prompts: [ + { title: "Work Prompt", prompt: "This is for work", tags: ["work"], category: "Work" }, + { title: "Personal Prompt", prompt: "This is personal", tags: ["personal"], category: "Personal" } + ], + setPrompts: mockSetPrompts + }); + + render(); + + const searchInput = screen.getByPlaceholderText("Search Saved Prompt"); + expect(searchInput).toBeTruthy(); + }); + }); +}); \ No newline at end of file diff --git a/prompt-manager/tests/utils/promptUtils.test.ts b/prompt-manager/tests/utils/promptUtils.test.ts index 42126bb..b56c1f7 100644 --- a/prompt-manager/tests/utils/promptUtils.test.ts +++ b/prompt-manager/tests/utils/promptUtils.test.ts @@ -4,8 +4,10 @@ import { Prompt } from "~interface/Prompt"; describe("promptUtils", () => { const mockPrompts: Prompt[] = [ - { title: "Test Prompt", prompt: "This is a test prompt.", tags: ["Tag1"] }, - { title: "Another Prompt", prompt: "This is another test prompt.", tags: ["Tag2"] }, + { title: "Test Prompt", prompt: "This is a test prompt.", tags: ["Tag1"], createdAt: new Date(), + website: "https://chatgpt.com/" }, + { title: "Another Prompt", prompt: "This is another test prompt.", tags: ["Tag2"], createdAt: new Date(), + website: "https://chatgpt.com/" }, ]; beforeEach(() => { @@ -52,4 +54,4 @@ describe("promptUtils", () => { expect(mockClick).toHaveBeenCalledTimes(1); expect(mockRevokeObjectURL).toHaveBeenCalledWith(mockCreateObjectURL.mock.results[0].value); }); -}); \ No newline at end of file +}); diff --git a/prompt-manager/utils/promptUtils.ts b/prompt-manager/utils/promptUtils.ts index e2ca565..0a77363 100644 --- a/prompt-manager/utils/promptUtils.ts +++ b/prompt-manager/utils/promptUtils.ts @@ -30,7 +30,11 @@ export function importPrompts(file: File): Promise { reader.onload = (event) => { try { const result = event.target?.result as string; - const importedPrompts: Prompt[] = JSON.parse(result); + const rawPrompts = JSON.parse(result); + const importedPrompts: Prompt[] = rawPrompts.map((p: any) => ({ + ...p, + createdAt: new Date(p.createdAt) + })); resolve(importedPrompts); } catch (error) { reject(new Error("Invalid JSON file")); @@ -43,4 +47,4 @@ export function importPrompts(file: File): Promise { reader.readAsText(file); }); -} \ No newline at end of file +}