Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions apps/desktop/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}
16 changes: 13 additions & 3 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,31 @@
"test": "node -e \"require('node:fs').mkdirSync('coverage/.tmp', { recursive: true })\" && vitest run --coverage"
},
"dependencies": {
"@tauri-apps/api": "^2.8.0",
"@bandscope/shared-types": "0.1.0",
"@base-ui/react": "^1.4.1",
"@fontsource-variable/geist": "^5.2.8",
"@tauri-apps/api": "^2.8.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.11.0",
"react": "^19.2.4",
"react-dom": "^19.2.4"
"react-dom": "^19.2.4",
"shadcn": "^4.5.0",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"tailwind-merge": "^3.5.0",
"tw-animate-css": "^1.4.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.2.4",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^10.1.0",
"@vitest/coverage-v8": "^4.1.1",
"eslint": "^10.1.0",
"jsdom": "^29.0.1",
"tailwindcss": "^4.2.4",
"typescript": "^6.0.2",
"typescript-eslint": "^8.57.2",
"vite": "^8.0.2",
Expand Down
62 changes: 61 additions & 1 deletion apps/desktop/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,45 @@ describe("App", () => {
});
});

it("rejects empty YouTube URL", async () => {
render(<App />);
const input = screen.getByPlaceholderText(/YouTube URL.../i);
fireEvent.change(input, { target: { value: " " } });
const button = screen.getByRole("button", { name: /Import YouTube/i });
// Button is disabled if youtubeUrl is empty, but we simulate enabling it for coverage
// or we can test that the error is set when it somehow triggers, but actually it's disabled.
// Wait, the button is disabled if `!youtubeUrl`. `youtubeUrl` is " ", so button is NOT disabled!
fireEvent.click(button);

await waitFor(() => {
expect(screen.getByText(/Failed to import YouTube URL./i)).toBeTruthy();
});
});

it("rejects malformed YouTube URL", async () => {
render(<App />);
const input = screen.getByPlaceholderText(/YouTube URL.../i);
fireEvent.change(input, { target: { value: "not-a-url" } });
const button = screen.getByRole("button", { name: /Import YouTube/i });
fireEvent.click(button);

await waitFor(() => {
expect(screen.getByText(/Failed to import YouTube URL./i)).toBeTruthy();
});
});

it("rejects non-http YouTube URL", async () => {
render(<App />);
const input = screen.getByPlaceholderText(/YouTube URL.../i);
fireEvent.change(input, { target: { value: "ftp://youtube.com/watch?v=123" } });
const button = screen.getByRole("button", { name: /Import YouTube/i });
fireEvent.click(button);

await waitFor(() => {
expect(screen.getByText(/Failed to import YouTube URL./i)).toBeTruthy();
});
});


it("loads a project and updates the UI", async () => {
mockLoadProject.mockResolvedValueOnce(succeededResult().result);
Expand Down Expand Up @@ -681,7 +720,7 @@ describe("App", () => {
const promptSpy = vi.spyOn(window, "prompt").mockReturnValue("Dbmaj7");

// Click on the chord to edit it (assuming SectionRoadmap renders it and allows click to edit)
fireEvent.click(screen.getAllByText("C#m7", { selector: 'strong' })[0]);
fireEvent.click(screen.getAllByText("C#m7", { selector: 'button' })[0]);

// Wait for the UI to update with the new chord (which verifies handleSongUpdate was called and state updated)
await waitFor(() => {
Expand All @@ -691,9 +730,30 @@ describe("App", () => {
promptSpy.mockRestore();
});

it("handles YouTube import failure with a missing message falling back to generic", async () => {
tauriInvoke.mockResolvedValueOnce({
code: "youtube_import_failed",
message: "" // Missing message
});

render(<App />);

const input = screen.getByPlaceholderText(/YouTube URL.../i);
fireEvent.change(input, { target: { value: "https://youtube.com/watch?v=456" } });

const button = screen.getByRole("button", { name: /Import YouTube/i });
fireEvent.click(button);

await waitFor(() => {
expect(screen.getByText(/Failed to import YouTube URL./i)).toBeTruthy();
});
});

it("does nothing when Save Project is clicked but there is no jobResult", () => {
render(<App />);
const saveButton = screen.getByRole("button", { name: /save project/i });
// Remove disabled attribute to force the click for coverage
saveButton.removeAttribute("disabled");
fireEvent.click(saveButton);
expect(mockSaveProject).not.toHaveBeenCalled();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
Expand Down
Loading
Loading