Skip to content

Commit a02c654

Browse files
author
Mohammod Al Amin Ashik
committed
test: Add comprehensive tests for auto-start and system tray
- Add 8 Rust unit tests for StartupSettings struct - Add 12 React unit tests for Switch component - Add 13 E2E tests for startup settings functionality All unit tests passing (Rust 8/8, React 12/12) E2E tests: 21/39 passed (failures expected in web-only mode)
1 parent 22e5f36 commit a02c654

6 files changed

Lines changed: 425 additions & 2 deletions

File tree

apps/desktop/src-tauri/src/commands/settings.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,99 @@ pub fn should_start_hidden() -> bool {
119119
let args: Vec<String> = std::env::args().collect();
120120
args.contains(&"--hidden".to_string())
121121
}
122+
123+
#[cfg(test)]
124+
mod tests {
125+
use super::*;
126+
127+
#[test]
128+
fn test_startup_settings_default() {
129+
let settings = StartupSettings::default();
130+
assert_eq!(settings.auto_launch, false);
131+
assert_eq!(settings.start_minimized, false);
132+
assert_eq!(settings.close_to_tray, true);
133+
}
134+
135+
#[test]
136+
fn test_startup_settings_serialization() {
137+
let settings = StartupSettings {
138+
auto_launch: true,
139+
start_minimized: false,
140+
close_to_tray: true,
141+
};
142+
143+
let json = serde_json::to_string(&settings).unwrap();
144+
assert!(json.contains("\"autoLaunch\":true"));
145+
assert!(json.contains("\"startMinimized\":false"));
146+
assert!(json.contains("\"closeToTray\":true"));
147+
}
148+
149+
#[test]
150+
fn test_startup_settings_deserialization() {
151+
let json = r#"{"autoLaunch":true,"startMinimized":true,"closeToTray":false}"#;
152+
let settings: StartupSettings = serde_json::from_str(json).unwrap();
153+
154+
assert_eq!(settings.auto_launch, true);
155+
assert_eq!(settings.start_minimized, true);
156+
assert_eq!(settings.close_to_tray, false);
157+
}
158+
159+
#[test]
160+
fn test_should_start_hidden_without_flag() {
161+
// This test might be tricky as it depends on actual process args
162+
// In a real test environment, we'd mock std::env::args
163+
// For now, we just verify the function exists and can be called
164+
let _result = should_start_hidden();
165+
// Can't assert the actual value since it depends on how tests are run
166+
}
167+
168+
#[test]
169+
fn test_startup_settings_clone() {
170+
let settings = StartupSettings {
171+
auto_launch: true,
172+
start_minimized: false,
173+
close_to_tray: true,
174+
};
175+
176+
let cloned = settings.clone();
177+
assert_eq!(settings.auto_launch, cloned.auto_launch);
178+
assert_eq!(settings.start_minimized, cloned.start_minimized);
179+
assert_eq!(settings.close_to_tray, cloned.close_to_tray);
180+
}
181+
182+
#[test]
183+
fn test_startup_settings_debug() {
184+
let settings = StartupSettings::default();
185+
let debug_str = format!("{:?}", settings);
186+
assert!(debug_str.contains("StartupSettings"));
187+
assert!(debug_str.contains("auto_launch"));
188+
assert!(debug_str.contains("start_minimized"));
189+
assert!(debug_str.contains("close_to_tray"));
190+
}
191+
192+
#[test]
193+
fn test_startup_settings_with_all_enabled() {
194+
let settings = StartupSettings {
195+
auto_launch: true,
196+
start_minimized: true,
197+
close_to_tray: true,
198+
};
199+
200+
assert!(settings.auto_launch);
201+
assert!(settings.start_minimized);
202+
assert!(settings.close_to_tray);
203+
}
204+
205+
#[test]
206+
fn test_startup_settings_with_all_disabled() {
207+
let settings = StartupSettings {
208+
auto_launch: false,
209+
start_minimized: false,
210+
close_to_tray: false,
211+
};
212+
213+
assert!(!settings.auto_launch);
214+
assert!(!settings.start_minimized);
215+
assert!(!settings.close_to_tray);
216+
}
217+
}

packages/ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"dev": "tsc --watch",
1919
"lint": "eslint src",
2020
"lint:fix": "eslint src --fix",
21-
"test": "echo \"No tests yet\""
21+
"test": "vitest run",
22+
"test:watch": "vitest"
2223
},
2324
"peerDependencies": {
2425
"react": "^19.0.0",
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* Tests for Switch component
3+
*/
4+
5+
import { describe, it, expect, vi } from 'vitest';
6+
import { render, screen, fireEvent } from '@testing-library/react';
7+
import { Switch } from './Switch';
8+
9+
describe('Switch', () => {
10+
it('renders with unchecked state', () => {
11+
const mockHandler = vi.fn();
12+
render(<Switch checked={false} onCheckedChange={mockHandler} />);
13+
14+
const button = screen.getByRole('switch');
15+
expect(button).toBeInTheDocument();
16+
expect(button).toHaveAttribute('aria-checked', 'false');
17+
});
18+
19+
it('renders with checked state', () => {
20+
const mockHandler = vi.fn();
21+
render(<Switch checked={true} onCheckedChange={mockHandler} />);
22+
23+
const button = screen.getByRole('switch');
24+
expect(button).toHaveAttribute('aria-checked', 'true');
25+
});
26+
27+
it('calls onCheckedChange when clicked', () => {
28+
const mockHandler = vi.fn();
29+
render(<Switch checked={false} onCheckedChange={mockHandler} />);
30+
31+
const button = screen.getByRole('switch');
32+
fireEvent.click(button);
33+
34+
expect(mockHandler).toHaveBeenCalledWith(true);
35+
expect(mockHandler).toHaveBeenCalledTimes(1);
36+
});
37+
38+
it('toggles from checked to unchecked', () => {
39+
const mockHandler = vi.fn();
40+
render(<Switch checked={true} onCheckedChange={mockHandler} />);
41+
42+
const button = screen.getByRole('switch');
43+
fireEvent.click(button);
44+
45+
expect(mockHandler).toHaveBeenCalledWith(false);
46+
});
47+
48+
it('does not call handler when disabled', () => {
49+
const mockHandler = vi.fn();
50+
render(<Switch checked={false} onCheckedChange={mockHandler} disabled={true} />);
51+
52+
const button = screen.getByRole('switch');
53+
fireEvent.click(button);
54+
55+
expect(mockHandler).not.toHaveBeenCalled();
56+
});
57+
58+
it('applies disabled attribute when disabled', () => {
59+
const mockHandler = vi.fn();
60+
render(<Switch checked={false} onCheckedChange={mockHandler} disabled={true} />);
61+
62+
const button = screen.getByRole('switch');
63+
expect(button).toBeDisabled();
64+
});
65+
66+
it('applies custom className', () => {
67+
const mockHandler = vi.fn();
68+
render(<Switch checked={false} onCheckedChange={mockHandler} className="custom-class" />);
69+
70+
const button = screen.getByRole('switch');
71+
expect(button).toHaveClass('custom-class');
72+
});
73+
74+
it('applies data-testid when provided', () => {
75+
const mockHandler = vi.fn();
76+
render(<Switch checked={false} onCheckedChange={mockHandler} data-testid="test-switch" />);
77+
78+
const button = screen.getByTestId('test-switch');
79+
expect(button).toBeInTheDocument();
80+
});
81+
82+
it('has correct styles for checked state', () => {
83+
const mockHandler = vi.fn();
84+
render(<Switch checked={true} onCheckedChange={mockHandler} />);
85+
86+
const button = screen.getByRole('switch');
87+
expect(button.className).toMatch(/bg-\[rgb\(var\(--primary\)\)\]/);
88+
});
89+
90+
it('has correct styles for unchecked state', () => {
91+
const mockHandler = vi.fn();
92+
render(<Switch checked={false} onCheckedChange={mockHandler} />);
93+
94+
const button = screen.getByRole('switch');
95+
expect(button.className).toMatch(/bg-gray-300/);
96+
});
97+
98+
it('has disabled styling when disabled', () => {
99+
const mockHandler = vi.fn();
100+
render(<Switch checked={false} onCheckedChange={mockHandler} disabled={true} />);
101+
102+
const button = screen.getByRole('switch');
103+
expect(button.className).toMatch(/opacity-50/);
104+
});
105+
106+
it('can be toggled multiple times', () => {
107+
const mockHandler = vi.fn();
108+
const { rerender } = render(<Switch checked={false} onCheckedChange={mockHandler} />);
109+
110+
const button = screen.getByRole('switch');
111+
112+
// First click - should call with true
113+
fireEvent.click(button);
114+
expect(mockHandler).toHaveBeenCalledWith(true);
115+
expect(mockHandler).toHaveBeenCalledTimes(1);
116+
117+
// Simulate parent updating the prop
118+
rerender(<Switch checked={true} onCheckedChange={mockHandler} />);
119+
120+
// Second click - should call with false
121+
fireEvent.click(button);
122+
expect(mockHandler).toHaveBeenCalledWith(false);
123+
expect(mockHandler).toHaveBeenCalledTimes(2);
124+
125+
// Simulate parent updating the prop again
126+
rerender(<Switch checked={false} onCheckedChange={mockHandler} />);
127+
128+
// Third click - should call with true again
129+
fireEvent.click(button);
130+
expect(mockHandler).toHaveBeenCalledWith(true);
131+
expect(mockHandler).toHaveBeenCalledTimes(3);
132+
});
133+
});

packages/ui/src/components/common/Switch.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export function Switch({
2222
}: SwitchProps) {
2323
const handleClick = () => {
2424
if (!disabled) {
25-
console.log('[Switch] Clicked, current:', checked, 'will become:', !checked);
2625
onCheckedChange(!checked);
2726
}
2827
};

packages/ui/vitest.config.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { defineConfig } from 'vitest/config';
2+
import react from '@vitejs/plugin-react';
3+
import path from 'path';
4+
5+
export default defineConfig({
6+
plugins: [react()],
7+
test: {
8+
environment: 'jsdom',
9+
globals: true,
10+
setupFiles: [path.resolve(__dirname, '../../tests/ts/setup.ts')],
11+
include: ['src/**/*.test.{ts,tsx}'],
12+
exclude: ['**/node_modules/**', 'dist/**'],
13+
},
14+
resolve: {
15+
alias: {
16+
'@': path.resolve(__dirname, './src'),
17+
},
18+
},
19+
});

0 commit comments

Comments
 (0)