Skip to content
This repository was archived by the owner on May 13, 2024. It is now read-only.

Commit a5cbcd1

Browse files
authored
Merge pull request #200 from utkarsha-deriv/utkarsha/restrictions-inside-appname
2 parents 7aaa7bd + 9e92e5d commit a5cbcd1

File tree

11 files changed

+412
-130
lines changed

11 files changed

+412
-130
lines changed

src/features/dashboard/components/AppForm/__tests__/app-form.test.tsx

Lines changed: 155 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import { Button } from '@deriv/ui';
21
import useApiToken from '@site/src/hooks/useApiToken';
32
import { render, screen, cleanup } from '@site/src/test-utils';
43
import { TTokensArrayType } from '@site/src/types';
54
import userEvent from '@testing-library/user-event';
65
import React from 'react';
76
import AppForm from '..';
7+
import { ApplicationObject } from '@deriv/api-types';
8+
import useAppManager from '@site/src/hooks/useAppManager';
89

910
jest.mock('@site/src/hooks/useApiToken');
1011
jest.mock('@site/src/utils', () => ({
1112
...jest.requireActual('@site/src/utils'),
1213
}));
14+
jest.mock('@site/src/hooks/useAppManager');
1315

1416
const mockUseApiToken = useApiToken as jest.MockedFunction<
1517
() => Partial<ReturnType<typeof useApiToken>>
@@ -20,32 +22,89 @@ mockUseApiToken.mockImplementation(() => ({
2022
updateCurrentToken: jest.fn(),
2123
}));
2224

23-
const renderButtons = () => {
24-
return (
25-
<div>
26-
<Button role='submit'>Update Application</Button>
27-
</div>
28-
);
29-
};
25+
const mockUseAppManager = useAppManager as jest.MockedFunction<
26+
() => Partial<ReturnType<typeof useAppManager>>
27+
>;
28+
mockUseAppManager.mockImplementation(() => ({
29+
apps: [],
30+
getApps: jest.fn(),
31+
}));
3032

3133
describe('App Form', () => {
3234
const mockOnSubmit = jest.fn();
3335

3436
beforeEach(() => {
35-
render(<AppForm renderButtons={renderButtons} submit={mockOnSubmit} />);
37+
render(<AppForm submit={mockOnSubmit} />);
3638
});
3739

3840
afterEach(() => {
3941
cleanup();
4042
jest.clearAllMocks();
4143
});
4244

45+
it('Should show error message for using an appname that already exists', async () => {
46+
const fakeApps: ApplicationObject[] = [
47+
{
48+
active: 1,
49+
app_id: 12345,
50+
app_markup_percentage: 0,
51+
appstore: '',
52+
github: '',
53+
googleplay: '',
54+
homepage: '',
55+
name: 'duplicate_app',
56+
redirect_uri: 'https://example.com',
57+
scopes: ['read', 'trade', 'trading_information'],
58+
verification_uri: 'https://example.com',
59+
last_used: '',
60+
},
61+
{
62+
active: 1,
63+
app_id: 12345,
64+
app_markup_percentage: 0,
65+
appstore: '',
66+
github: '',
67+
googleplay: '',
68+
homepage: '',
69+
name: 'testApp',
70+
redirect_uri: 'https://example.com',
71+
scopes: ['read', 'trade'],
72+
verification_uri: 'https://example.com',
73+
last_used: '',
74+
},
75+
];
76+
const mockGetApps = jest.fn();
77+
78+
mockUseAppManager.mockImplementation(() => ({
79+
apps: fakeApps,
80+
getApps: mockGetApps,
81+
}));
82+
83+
const submitButton = screen.getByText('Register Application');
84+
85+
const tokenNameInput = screen.getByRole<HTMLInputElement>('textbox', {
86+
name: 'App name (required)',
87+
});
88+
89+
await userEvent.type(tokenNameInput, 'duplicate_app');
90+
91+
await userEvent.click(submitButton);
92+
93+
await userEvent.clear(tokenNameInput);
94+
95+
await userEvent.type(tokenNameInput, 'duplicate_app');
96+
97+
const appNameErrorText = await screen.findByText('That name is taken. Choose another.');
98+
99+
expect(appNameErrorText).toBeInTheDocument();
100+
});
101+
43102
it('Should show error message for having no admin token', async () => {
44103
const fakeTokens: TTokensArrayType = [
45104
{
46105
display_name: 'first',
47106
last_used: '',
48-
scopes: ['read', 'trade'],
107+
scopes: ['read', 'trade', 'admin'],
49108
token: 'first_token',
50109
valid_for_ip: '',
51110
},
@@ -71,8 +130,11 @@ describe('App Form', () => {
71130
});
72131

73132
it('Should show error message for empty app name', async () => {
74-
const submitButton = screen.getByText('Update Application');
75-
133+
const submitButton = screen.getByText('Register Application');
134+
const tokenNameInput = screen.getByRole<HTMLInputElement>('textbox', {
135+
name: 'App name (required)',
136+
});
137+
await userEvent.clear(tokenNameInput);
76138
await userEvent.click(submitButton);
77139

78140
const appNameErrorText = await screen.findByText('Enter your app name.');
@@ -81,7 +143,7 @@ describe('App Form', () => {
81143
});
82144

83145
it('Should show error for long app name', async () => {
84-
const submitButton = screen.getByText('Update Application');
146+
const submitButton = screen.getByText('Register Application');
85147

86148
const tokenNameInput = screen.getByRole<HTMLInputElement>('textbox', {
87149
name: 'App name (required)',
@@ -99,8 +161,26 @@ describe('App Form', () => {
99161
expect(appNameErrorText).toBeInTheDocument();
100162
});
101163

164+
it('Should show error for using non alphanumeric characters except underscore or space', async () => {
165+
const submitButton = screen.getByText('Register Application');
166+
167+
const tokenNameInput = screen.getByRole<HTMLInputElement>('textbox', {
168+
name: 'App name (required)',
169+
});
170+
171+
await userEvent.type(tokenNameInput, 'invalid-token...');
172+
173+
await userEvent.click(submitButton);
174+
175+
const appNameErrorText = await screen.findByText(
176+
'Only alphanumeric characters with spaces and underscores are allowed. (Example: my_application)',
177+
);
178+
179+
expect(appNameErrorText).toBeInTheDocument();
180+
});
181+
102182
it('Should show error message for long app markup percentage', async () => {
103-
const submitButton = screen.getByText('Update Application');
183+
const submitButton = screen.getByText('Register Application');
104184

105185
const appMarkupPercentageInput = screen.getByRole<HTMLInputElement>('spinbutton', {
106186
name: 'Markup percentage (optional)',
@@ -117,6 +197,42 @@ describe('App Form', () => {
117197
expect(appMarkupPercentageError).toBeInTheDocument();
118198
});
119199

200+
it('Should show error for invalid Auth url', async () => {
201+
const submitButton = screen.getByText('Register Application');
202+
203+
const authURLInput = screen.getByRole('textbox', {
204+
name: 'Authorization URL (optional)',
205+
});
206+
207+
await userEvent.type(authURLInput, 'http:invalidAUTHurl.com');
208+
209+
await userEvent.click(submitButton);
210+
211+
const authURLInputError = await screen.queryByText(
212+
'Enter a valid URL. (Example: https://www.[YourDomainName].com)',
213+
);
214+
215+
expect(authURLInputError).toBeInTheDocument();
216+
});
217+
218+
it('Should show error for invalid Verification url', async () => {
219+
const submitButton = screen.getByText('Register Application');
220+
221+
const authURLInput = screen.getByRole('textbox', {
222+
name: 'Verification URL (optional)',
223+
});
224+
225+
await userEvent.type(authURLInput, 'http:invalidVERIurl.com');
226+
227+
await userEvent.click(submitButton);
228+
229+
const authURLInputError = await screen.queryByText(
230+
'Enter a valid URL. (Example: https://www.[YourDomainName].com)',
231+
);
232+
233+
expect(authURLInputError).toBeInTheDocument();
234+
});
235+
120236
it('Should show error message for wrong value', async () => {
121237
const fakeTokens: TTokensArrayType = [
122238
{
@@ -147,7 +263,7 @@ describe('App Form', () => {
147263
updateCurrentToken: jest.fn(),
148264
}));
149265

150-
const submitButton = screen.getByText('Update Application');
266+
const submitButton = screen.getByText('Register Application');
151267

152268
const appMarkupPercentageInput = screen.getByRole<HTMLInputElement>('spinbutton', {
153269
name: 'Markup percentage (optional)',
@@ -165,7 +281,7 @@ describe('App Form', () => {
165281
});
166282

167283
it('Should call onSubmit on submitting the form', async () => {
168-
const submitButton = screen.getByText('Update Application');
284+
const submitButton = screen.getByText('Register Application');
169285

170286
const selectTokenOption = screen.getByTestId('select-token');
171287

@@ -193,4 +309,27 @@ describe('App Form', () => {
193309

194310
expect(mockOnSubmit).toHaveBeenCalledTimes(1);
195311
});
312+
313+
it('Should display restrictions when app name is in focus and disappear if error occurs', async () => {
314+
const submitButton = screen.getByText('Register Application');
315+
316+
const tokenNameInput = screen.getByRole<HTMLInputElement>('textbox', {
317+
name: 'App name (required)',
318+
});
319+
320+
await userEvent.type(tokenNameInput, 'Lorem ipsum dolor sit amet');
321+
322+
const restrictionsList = screen.queryByRole('list');
323+
expect(restrictionsList).toBeInTheDocument();
324+
325+
await userEvent.clear(tokenNameInput);
326+
327+
await userEvent.type(
328+
tokenNameInput,
329+
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi corrupti neque ratione repudiandae in dolores reiciendis sequi nvrohgoih iuhwr uiwhrug uwhiog iouwhg ouwhg',
330+
);
331+
332+
await userEvent.click(submitButton);
333+
expect(restrictionsList).not.toBeInTheDocument();
334+
});
196335
});

src/features/dashboard/components/AppForm/app-form.module.scss

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ fieldset .customTextInput:last-child {
5959
}
6060

6161
.helperMargin {
62-
margin: rem(1) 0;
62+
margin: rem(1) 0 0;
6363
}
6464
.verificationMargin {
6565
margin: rem(2) 0;
@@ -122,6 +122,7 @@ fieldset .customTextInput:last-child {
122122
}
123123
.formsubHeading {
124124
padding: rem(1.6);
125+
display: inline-block;
125126
}
126127
.wrapperHeading {
127128
padding-left: rem(1.6);
@@ -174,3 +175,27 @@ fieldset .customTextInput:last-child {
174175
margin-left: rem(4);
175176
margin-top: rem(6);
176177
}
178+
179+
.buttons {
180+
display: flex;
181+
gap: rem(1);
182+
}
183+
184+
.errorAppname {
185+
border-color: var(--colors-coral500) !important;
186+
&:focus-within {
187+
border-color: var(--colors-coral500) !important;
188+
}
189+
input[type='text'],
190+
input[type='number'] {
191+
&:not(:placeholder-shown) ~ label {
192+
color: var(--colors-coral500) !important;
193+
}
194+
&:focus {
195+
outline: var(--colors-coral500) !important;
196+
& ~ label {
197+
color: var(--colors-coral500) !important;
198+
}
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)