Skip to content

Commit f0e76cf

Browse files
vinokurigclaude
andauthored
feat(ui): add detailed validation error messages to Git Config import modal (#1542)
Display specific error explanations below the file upload field when Git configuration validation fails. Error messages cover missing user section, invalid email format, name/email length violations, parse errors, and content length limits. This helps users quickly understand and fix validation issues. Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent fd91e8e commit f0e76cf

7 files changed

Lines changed: 546 additions & 34 deletions

File tree

packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/__snapshots__/index.spec.tsx.snap

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,46 @@ exports[`GitConfigForm snapshot with predefined git configuration 1`] = `
5757
onClick={[Function]}
5858
type="button"
5959
/>
60+
<input
61+
data-testid="submit-missing-user-section-git-config"
62+
onClick={[Function]}
63+
type="button"
64+
/>
65+
<input
66+
data-testid="submit-empty-email-git-config"
67+
onClick={[Function]}
68+
type="button"
69+
/>
70+
<input
71+
data-testid="submit-long-email-git-config"
72+
onClick={[Function]}
73+
type="button"
74+
/>
75+
<input
76+
data-testid="submit-parse-error-git-config"
77+
onClick={[Function]}
78+
type="button"
79+
/>
80+
<input
81+
data-testid="submit-max-length-git-config"
82+
onClick={[Function]}
83+
type="button"
84+
/>
85+
<input
86+
data-testid="submit-only-name-git-config"
87+
onClick={[Function]}
88+
type="button"
89+
/>
90+
<input
91+
data-testid="submit-only-email-git-config"
92+
onClick={[Function]}
93+
type="button"
94+
/>
95+
<input
96+
data-testid="submit-both-null-git-config"
97+
onClick={[Function]}
98+
type="button"
99+
/>
60100
</div>
61101
</div>
62102
</div>
@@ -116,6 +156,46 @@ exports[`GitConfigForm snapshot without predefined git configuration 1`] = `
116156
onClick={[Function]}
117157
type="button"
118158
/>
159+
<input
160+
data-testid="submit-missing-user-section-git-config"
161+
onClick={[Function]}
162+
type="button"
163+
/>
164+
<input
165+
data-testid="submit-empty-email-git-config"
166+
onClick={[Function]}
167+
type="button"
168+
/>
169+
<input
170+
data-testid="submit-long-email-git-config"
171+
onClick={[Function]}
172+
type="button"
173+
/>
174+
<input
175+
data-testid="submit-parse-error-git-config"
176+
onClick={[Function]}
177+
type="button"
178+
/>
179+
<input
180+
data-testid="submit-max-length-git-config"
181+
onClick={[Function]}
182+
type="button"
183+
/>
184+
<input
185+
data-testid="submit-only-name-git-config"
186+
onClick={[Function]}
187+
type="button"
188+
/>
189+
<input
190+
data-testid="submit-only-email-git-config"
191+
onClick={[Function]}
192+
type="button"
193+
/>
194+
<input
195+
data-testid="submit-both-null-git-config"
196+
onClick={[Function]}
197+
type="button"
198+
/>
119199
</div>
120200
</div>
121201
</div>

packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/index.spec.tsx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,144 @@ describe('GitConfigForm', () => {
9393
false,
9494
);
9595
});
96+
97+
describe('Error messages', () => {
98+
it('should display error message for missing user section', async () => {
99+
renderComponent();
100+
101+
const gitConfigField = screen.getByTestId('submit-missing-user-section-git-config');
102+
await userEvent.click(gitConfigField);
103+
104+
expect(
105+
screen.getByText('The [user] section with "name" and "email" fields is required.'),
106+
).toBeInTheDocument();
107+
});
108+
109+
it('should display error message for invalid email format', async () => {
110+
renderComponent();
111+
112+
const gitConfigField = screen.getByTestId('submit-invalid-email-git-config');
113+
await userEvent.click(gitConfigField);
114+
115+
expect(
116+
screen.getByText('User email must be a valid email address (e.g., user@example.com).'),
117+
).toBeInTheDocument();
118+
});
119+
120+
it('should display error message for empty name when email is valid', async () => {
121+
renderComponent();
122+
123+
const gitConfigField = screen.getByTestId('submit-empty-name-git-config');
124+
await userEvent.click(gitConfigField);
125+
126+
// Name is now required
127+
expect(screen.getByText('Username is required.')).toBeInTheDocument();
128+
});
129+
130+
it('should display error message for name exceeding max length', async () => {
131+
renderComponent();
132+
133+
const gitConfigField = screen.getByTestId('submit-long-name-git-config');
134+
await userEvent.click(gitConfigField);
135+
136+
expect(
137+
screen.getByText('User name must be between 1 and 128 characters.'),
138+
).toBeInTheDocument();
139+
});
140+
141+
it('should display error message for empty email when name is valid', async () => {
142+
renderComponent();
143+
144+
const gitConfigField = screen.getByTestId('submit-empty-email-git-config');
145+
await userEvent.click(gitConfigField);
146+
147+
// Email is now required
148+
expect(screen.getByText('User email is required.')).toBeInTheDocument();
149+
});
150+
151+
it('should display error message for email exceeding max length', async () => {
152+
renderComponent();
153+
154+
const gitConfigField = screen.getByTestId('submit-long-email-git-config');
155+
await userEvent.click(gitConfigField);
156+
157+
expect(
158+
screen.getByText('User email must be between 1 and 128 characters.'),
159+
).toBeInTheDocument();
160+
});
161+
162+
it('should display error message for malformed config', async () => {
163+
renderComponent();
164+
165+
const gitConfigField = screen.getByTestId('submit-parse-error-git-config');
166+
await userEvent.click(gitConfigField);
167+
168+
// The multi-ini parser is lenient and may parse malformed content
169+
// but it should still fail validation and show an error message
170+
expect(
171+
screen.getByText('The [user] section with "name" and "email" fields is required.'),
172+
).toBeInTheDocument();
173+
});
174+
175+
it('should display error message for max length exceeded', async () => {
176+
renderComponent();
177+
178+
const gitConfigField = screen.getByTestId('submit-max-length-git-config');
179+
await userEvent.click(gitConfigField);
180+
181+
expect(
182+
screen.getByText('The value is too long. The maximum length is 4096 characters.'),
183+
).toBeInTheDocument();
184+
});
185+
186+
it('should not display error message for valid input', async () => {
187+
renderComponent();
188+
189+
const gitConfigField = screen.getByTestId('submit-valid-git-config');
190+
await userEvent.click(gitConfigField);
191+
192+
expect(
193+
screen.queryByText('User email must be a valid email address (e.g., user@example.com).'),
194+
).not.toBeInTheDocument();
195+
expect(
196+
screen.queryByText('The [user] section with "name" and "email" fields is required.'),
197+
).not.toBeInTheDocument();
198+
});
199+
});
200+
201+
describe('Required field validation', () => {
202+
it('should reject config with only name (email is required)', async () => {
203+
renderComponent();
204+
205+
const gitConfigField = screen.getByTestId('submit-only-name-git-config');
206+
await userEvent.click(gitConfigField);
207+
208+
expect(mockOnChange).toHaveBeenCalledWith({ user: { name: 'User One' } }, false);
209+
expect(screen.getByText('User email is required.')).toBeInTheDocument();
210+
});
211+
212+
it('should reject config with only email (name is required)', async () => {
213+
renderComponent();
214+
215+
const gitConfigField = screen.getByTestId('submit-only-email-git-config');
216+
await userEvent.click(gitConfigField);
217+
218+
expect(mockOnChange).toHaveBeenCalledWith({ user: { email: 'user@test.com' } }, false);
219+
expect(screen.getByText('Username is required.')).toBeInTheDocument();
220+
});
221+
222+
it('should reject config when both name and email are null', async () => {
223+
renderComponent();
224+
225+
const gitConfigField = screen.getByTestId('submit-both-null-git-config');
226+
await userEvent.click(gitConfigField);
227+
228+
expect(mockOnChange).toHaveBeenCalledWith({ user: {} }, false);
229+
expect(
230+
screen.getByText('The [user] section with "name" and "email" fields is required.'),
231+
).toBeInTheDocument();
232+
});
233+
});
96234
});
97235

98236
function getComponent(gitConfig?: GitConfigStore.GitConfig) {

0 commit comments

Comments
 (0)