Skip to content

Commit d1ba3ca

Browse files
authored
feat(accounts): warn when missing scopes (#1688)
* feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> * feat(accounts): warn when missing scopes Signed-off-by: Adam Setch <[email protected]> --------- Signed-off-by: Adam Setch <[email protected]>
1 parent 9a7d1b4 commit d1ba3ca

File tree

7 files changed

+872
-3
lines changed

7 files changed

+872
-3
lines changed

src/renderer/__mocks__/partial-mocks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export function partialMockNotification(
1313
hostname: Constants.GITHUB_API_BASE_URL as Hostname,
1414
token: mockToken,
1515
user: mockGitifyUser,
16+
hasRequiredScopes: true,
1617
},
1718
subject: subject as Subject,
1819
};

src/renderer/__mocks__/state-mocks.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const mockPersonalAccessTokenAccount: Account = {
3434
token: 'token-123-456' as Token,
3535
hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
3636
user: mockGitifyUser,
37+
hasRequiredScopes: true,
3738
};
3839

3940
export const mockOAuthAccount: Account = {
@@ -42,6 +43,7 @@ export const mockOAuthAccount: Account = {
4243
token: '1234568790' as Token,
4344
hostname: 'github.gitify.io' as Hostname,
4445
user: mockGitifyUser,
46+
hasRequiredScopes: true,
4547
};
4648

4749
export const mockGitHubCloudAccount: Account = {
@@ -51,6 +53,7 @@ export const mockGitHubCloudAccount: Account = {
5153
hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
5254
user: mockGitifyUser,
5355
version: 'latest',
56+
hasRequiredScopes: true,
5457
};
5558

5659
export const mockGitHubEnterpriseServerAccount: Account = {
@@ -59,6 +62,7 @@ export const mockGitHubEnterpriseServerAccount: Account = {
5962
token: '1234568790' as Token,
6063
hostname: 'github.gitify.io' as Hostname,
6164
user: mockGitifyUser,
65+
hasRequiredScopes: true,
6266
};
6367

6468
export const mockGitHubAppAccount: Account = {
@@ -67,6 +71,7 @@ export const mockGitHubAppAccount: Account = {
6771
token: '987654321' as Token,
6872
hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
6973
user: mockGitifyUser,
74+
hasRequiredScopes: true,
7075
};
7176

7277
export const mockAuth: AuthState = {

src/renderer/routes/Accounts.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,45 @@ describe('renderer/routes/Accounts.tsx', () => {
5757
expect(screen.getByTestId('accounts')).toMatchSnapshot();
5858
});
5959

60+
it('should render with PAT scopes warning', async () => {
61+
const openExternalLinkMock = jest
62+
.spyOn(comms, 'openExternalLink')
63+
.mockImplementation();
64+
65+
await act(async () => {
66+
render(
67+
<AppContext.Provider
68+
value={{
69+
auth: {
70+
accounts: [
71+
{
72+
...mockPersonalAccessTokenAccount,
73+
hasRequiredScopes: false,
74+
},
75+
mockOAuthAccount,
76+
mockGitHubAppAccount,
77+
],
78+
},
79+
settings: mockSettings,
80+
}}
81+
>
82+
<MemoryRouter>
83+
<AccountsRoute />
84+
</MemoryRouter>
85+
</AppContext.Provider>,
86+
);
87+
});
88+
89+
expect(screen.getByTestId('accounts')).toMatchSnapshot();
90+
91+
fireEvent.click(screen.getByLabelText('missing-scopes'));
92+
93+
expect(openExternalLinkMock).toHaveBeenCalledTimes(1);
94+
expect(openExternalLinkMock).toHaveBeenCalledWith(
95+
'https://github.com/settings/tokens',
96+
);
97+
});
98+
6099
it('should go back by pressing the icon', async () => {
61100
await act(async () => {
62101
render(

src/renderer/routes/Accounts.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
AlertFillIcon,
23
FeedPersonIcon,
34
KeyIcon,
45
MarkGithubIcon,
@@ -23,6 +24,7 @@ import { type Account, IconColor, Size } from '../types';
2324
import { getAccountUUID, refreshAccount } from '../utils/auth/utils';
2425
import { cn } from '../utils/cn';
2526
import { updateTrayIcon, updateTrayTitle } from '../utils/comms';
27+
import { Constants } from '../utils/constants';
2628
import {
2729
openAccountProfile,
2830
openDeveloperSettings,
@@ -78,7 +80,7 @@ export const AccountsRoute: FC = () => {
7880
className="mb-4 flex items-center justify-between rounded-md bg-gray-100 p-2 dark:bg-gray-sidebar"
7981
>
8082
<div className="ml-2 text-xs">
81-
<div>
83+
<div className="flex flex-1 items-center gap-2">
8284
<button
8385
type="button"
8486
className="flex flex-1 gap-2 items-center justify-center mb-1 cursor-pointer text-sm font-semibold"
@@ -99,6 +101,23 @@ export const AccountsRoute: FC = () => {
99101
({account.user?.name})
100102
</span>
101103
</button>
104+
105+
{account.hasRequiredScopes === false && (
106+
<span className="text-xs font-medium italic">
107+
<button
108+
type="button"
109+
className="cursor-pointer"
110+
title={`This account is missing one or more required scopes: \n - ${Constants.AUTH_SCOPE.join('\n - ')}`}
111+
aria-label="missing-scopes"
112+
onClick={() => openDeveloperSettings(account)}
113+
>
114+
<AlertFillIcon
115+
size={Size.XSMALL}
116+
className={IconColor.RED}
117+
/>
118+
</button>
119+
</span>
120+
)}
102121
</div>
103122
<div>
104123
<button

0 commit comments

Comments
 (0)