Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adjust action logic #73

Merged
merged 5 commits into from Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions electron-builder.yml
Expand Up @@ -10,7 +10,7 @@ files:
- package.json
- app
- resources/python/**/*
- resources/powershell/**/*
- resources/actions/**/*
- resources/7zip/**/*
publish: null
nsis:
Expand All @@ -20,5 +20,5 @@ nsis:
asar: true
asarUnpack:
- "resources/python/**/*"
- "resources/powershell/**/*"
- "resources/actions/**/*"
- "resources/7zip/**/*"
1 change: 1 addition & 0 deletions jest.config.client.ts
Expand Up @@ -36,6 +36,7 @@ const jestConfig = {
testEnvironment: "jsdom",
transformIgnorePatterns: ["/node_modules/"],
extensionsToTreatAsEsm: [".ts", ".tsx"],
setupFilesAfterEnv: ["<rootDir>/jest.setup.client.ts"],
};

export default jestConfig;
7 changes: 7 additions & 0 deletions jest.setup.client.ts
@@ -0,0 +1,7 @@
import "@testing-library/jest-dom";

if (typeof CSS === "undefined") {
global.CSS = {
escape: (string_: string) => string_.replaceAll(/([()\\{}])/g, "\\$1"),
} as any; // Cast to 'any' to bypass TypeScript's type checking for the mock.
}
42 changes: 35 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -88,6 +88,7 @@
"dayjs": "1.11.10",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.4.0",
"dot-prop": "^8.0.2",
"electron": "30.0.0-alpha.4",
"electron-builder": "^24.13.3",
"eslint": "^8.57.0",
Expand Down
2 changes: 1 addition & 1 deletion playwright/installer.test.ts
Expand Up @@ -31,7 +31,7 @@ test.afterAll(async () => {
test("Renders the installer page", async () => {
page = await electronApp.firstWindow();
const title = await page.title();
expect(title).toBe("Blibla");
expect(title).toBe("Captain");
});

test("Allows switching the language", async () => {
Expand Down
@@ -1,8 +1,10 @@
---
id: action:window
id: userStore.set
label: Switch to Dark mode
icon: ./icons/moon.svg
action: set:color-mode:dark
icon: moon
action: function
parameters:
theme: dark
language: en
description: "Change your app's theme to Dark mode with one click."
---
Expand Down
@@ -1,8 +1,10 @@
---
id: action:window
id: userStore.set
label: Switch to Light mode
icon: ./icons/sun.svg
action: set:color-mode:light
icon: sun
action: function
parameters:
theme: light
language: en
description: "Change your app's theme to Light mode with one click."
---
Expand Down
@@ -1,9 +1,11 @@
---
id: action:user
id: userStore.set
label: Switch to German
icon: ./icons/de-flag.svg
action: set:language:de
language: en
icon: flagDe
action: function
parameters:
language: de
language: en
description: "Instantly switch the application's language to German for a seamless user experience."
---

Expand Down
@@ -1,8 +1,10 @@
---
id: action:user
id: userStore.set
label: Switch to English
icon: ./icons/english-flag.svg
action: set:language:en
icon: flagEn
action: function
parameters:
language: en
language: en
description: "Instantly switch the application's language to English for a seamless user experience."
---
Expand Down
Expand Up @@ -10,21 +10,21 @@ This document serves as an index and overview of the various settings available

## Table of Contents

- [Color Mode](./color_mode.md) - Customize the visual theme of the application according to your preference or system default.
- [Language](./language.md) - Set your preferred language for the application interface.
- [OpenAI API Integration](./openai_api.md) - Enter your OpenAI API key to enable integration with OpenAI services.
- [Color Mode](color_mode.md) - Customize the visual theme of the application according to your preference or system default.
- [Language](language.md) - Set your preferred language for the application interface.
- [OpenAI API Integration](openai_api.md) - Enter your OpenAI API key to enable integration with OpenAI services.

## Detailed Settings Guides

The following guides provide detailed instructions on configuring each specific setting:

### [Color Mode Settings](./color_mode.md)
### [Color Mode Settings](color_mode.md)
Adjust the color theme of your application interface. Options include Light, Dark, or System themes.

### [Language Settings](./language.md)
### [Language Settings](language.md)
Change the language of the application interface. A variety of languages are supported to provide a comfortable user experience.

### [OpenAI API Settings](./openai_api.md)
### [OpenAI API Settings](openai_api.md)
Integrate OpenAI's powerful AI capabilities by entering your unique API key.

For more information on each setting, please refer to the individual markdown files linked above. If you need further assistance, please visit the Help section or contact support.
Expand Down
29 changes: 0 additions & 29 deletions resources/docs/create-action/readme.md

This file was deleted.

10 changes: 0 additions & 10 deletions src/client/apps/live-painting/index.tsx
Expand Up @@ -93,16 +93,6 @@ export function LivePainting() {
setImages(previousImages => [...previousImages, { id, dataUrl: image, url }]);
}, [image, setImages, writeFile]);

useEffect(() => {
const unsubscribe = window.ipc.on("language", async locale => {
await changeLanguage(locale);
});

return () => {
unsubscribe();
};
}, [changeLanguage]);

useEffect(() => {
async function handleSave(event: KeyboardEvent) {
if (event.key === "s" && event.ctrlKey) {
Expand Down
106 changes: 106 additions & 0 deletions src/client/ions/handlers/__tests__/action.test.ts
@@ -0,0 +1,106 @@
import { handleCaptainAction, performElementAction } from "../action";

import { buildKey } from "#/build-key";
import { ID } from "#/enums";
import type { VectorStoreResponse } from "#/types/vector-store";

jest.mock("#/build-key", () => ({
buildKey: jest.fn(),
}));

describe("handleCaptainAction", () => {
// Mocking window.ipc.send
const mockSend = jest.fn();
beforeAll(() => {
// Ensure window.ipc exists
global.window.ipc = { send: mockSend } as any;
});

beforeEach(() => {
jest.clearAllMocks();
});

it("should handle function actions correctly", () => {
const response: VectorStoreResponse = {
id: "1",
score: 0.5,
payload: {
id: "testFunction",
language: "en",
action: "function",
label: "Test Function",
},
};

(buildKey as jest.Mock).mockReturnValue("functionKey");

handleCaptainAction(response);

expect(buildKey).toHaveBeenCalledWith([ID.CAPTAIN_ACTION]);
expect(mockSend).toHaveBeenCalledWith("functionKey", {
action: response.payload.action,
payload: response.payload,
});
});

it("should handle non-function actions correctly", () => {
const response: VectorStoreResponse = {
id: "2",
score: 0.5,
payload: {
id: "testApp",
language: "en",
action: "open",
label: "Test App",
},
};

(buildKey as jest.Mock).mockReturnValue("appKey:open");

handleCaptainAction(response);

expect(buildKey).toHaveBeenCalledWith([ID.APP], { suffix: ":open" });
expect(mockSend).toHaveBeenCalledWith("appKey:open", {
appId: response.payload.id,
action: response.payload.action,
});
});
});

describe("performElementAction", () => {
// Setup a DOM element for testing
beforeAll(() => {
document.body.innerHTML = `<div data-captainid="test-element"></div>`;
});

it("executes the action on an element when found", () => {
const mockAction = jest.fn();
performElementAction("test-element", mockAction);
expect(mockAction).toHaveBeenCalledTimes(1);
expect(mockAction).toHaveBeenCalledWith(expect.any(HTMLElement));
});

it("does not execute the action when the element is not found", () => {
const mockAction = jest.fn();
performElementAction("nonexistent-element", mockAction);
expect(mockAction).not.toHaveBeenCalled();
});

it("logs an error when the action function throws", () => {
const mockAction = jest.fn().mockImplementation(() => {
throw new Error("Test error");
});
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {});

performElementAction("test-element", mockAction);
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining(
"Error performing action on element with captainId=test-element:"
),
expect.any(Error)
);

// Clean up
consoleErrorSpy.mockRestore();
});
});