Skip to content

Commit

Permalink
Merge b05eea0 into a342c17
Browse files Browse the repository at this point in the history
  • Loading branch information
setchy committed Jun 6, 2024
2 parents a342c17 + b05eea0 commit 8f3a500
Show file tree
Hide file tree
Showing 11 changed files with 1,023 additions and 262 deletions.
8 changes: 8 additions & 0 deletions src/__mocks__/state-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ export const mockGitHubEnterpriseServerAccount: Account = {
user: mockGitifyUser,
};

export const mockGitHubAppAccount: Account = {
platform: 'GitHub Cloud',
method: 'GitHub App',
token: '987654321',
hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
user: mockGitifyUser,
};

export const mockAuth: AuthState = {
accounts: [mockGitHubCloudAccount, mockGitHubEnterpriseServerAccount],
};
Expand Down
10 changes: 9 additions & 1 deletion src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { Loading } from './components/Loading';
import { Sidebar } from './components/Sidebar';
import { AppContext, AppProvider } from './context/App';
import { AccountsRoute } from './routes/Accounts';
import { LoginRoute } from './routes/Login';
import { LoginWithOAuthApp } from './routes/LoginWithOAuthApp';
import { LoginWithPersonalAccessToken } from './routes/LoginWithPersonalAccessToken';
Expand Down Expand Up @@ -50,7 +51,14 @@ export const App = () => {
</RequireAuth>
}
/>

<Route
path="/accounts"
element={
<RequireAuth>
<AccountsRoute />
</RequireAuth>
}
/>
<Route path="/login" element={<LoginRoute />} />
<Route
path="/login-personal-access-token"
Expand Down
13 changes: 13 additions & 0 deletions src/context/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { useInterval } from '../hooks/useInterval';
import { useNotifications } from '../hooks/useNotifications';
import {
type Account,
type AccountNotifications,
type AuthState,
type GitifyError,
Expand All @@ -27,6 +28,7 @@ import {
authGitHub,
getToken,
getUserData,
removeAccount,
} from '../utils/auth/utils';
import { setAutoLaunch, updateTrayTitle } from '../utils/comms';
import Constants from '../utils/constants';
Expand Down Expand Up @@ -62,6 +64,7 @@ interface AppContextState {
loginWithGitHubApp: () => void;
loginWithOAuthApp: (data: LoginOAuthAppOptions) => void;
loginWithPersonalAccessToken: (data: LoginPersonalAccessTokenOptions) => void;
logoutFromAccount: (account: Account) => void;
logout: () => void;

notifications: AccountNotifications[];
Expand Down Expand Up @@ -193,6 +196,15 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
[auth, settings],
);

const logoutFromAccount = useCallback(
async (account: Account) => {
const updatedAuth = removeAccount(auth, account);
setAuth(updatedAuth);
saveState({ auth: updatedAuth, settings });
},
[auth, settings],
);

const logout = useCallback(() => {
setAuth(defaultAuth);
clearState();
Expand Down Expand Up @@ -256,6 +268,7 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
loginWithGitHubApp,
loginWithOAuthApp,
loginWithPersonalAccessToken,
logoutFromAccount,
logout,

notifications,
Expand Down
279 changes: 279 additions & 0 deletions src/routes/Accounts.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import { ipcRenderer, shell } from 'electron';
import { MemoryRouter } from 'react-router-dom';
import {
mockAuth,
mockGitHubAppAccount,
mockOAuthAccount,
mockPersonalAccessTokenAccount,
mockSettings,
} from '../__mocks__/state-mocks';
import { AppContext } from '../context/App';
import { AccountsRoute } from './Accounts';

const mockNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockNavigate,
}));

describe('routes/Accounts.tsx', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('General', () => {
it('should render itself & its children', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: {
accounts: [
mockPersonalAccessTokenAccount,
mockOAuthAccount,
mockGitHubAppAccount,
],
},
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

expect(screen.getByTestId('accounts')).toMatchSnapshot();
});

it('should go back by pressing the icon', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: mockAuth,
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByLabelText('Go Back'));
expect(mockNavigate).toHaveBeenNthCalledWith(1, -1);
});
});

describe('Account interactions', () => {
it('open profile in external browser', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: {
accounts: [mockPersonalAccessTokenAccount],
},
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByTitle('Open Profile'));

expect(shell.openExternal).toHaveBeenCalledTimes(1);
expect(shell.openExternal).toHaveBeenCalledWith(
'https://github.com/octocat',
);
});

it('open host in external browser', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: {
accounts: [mockPersonalAccessTokenAccount],
},
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByTitle('Open Host'));

expect(shell.openExternal).toHaveBeenCalledTimes(1);
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com');
});

it('open developer settings in external browser', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: {
accounts: [mockPersonalAccessTokenAccount],
},
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByTitle('Open Developer Settings'));

expect(shell.openExternal).toHaveBeenCalledTimes(1);
expect(shell.openExternal).toHaveBeenCalledWith(
'https://github.com/settings/tokens',
);
});

it('should logout', async () => {
const logoutFromAccountMock = jest.fn();
await act(async () => {
render(
<AppContext.Provider
value={{
auth: {
accounts: [mockPersonalAccessTokenAccount],
},
settings: mockSettings,
logoutFromAccount: logoutFromAccountMock,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByTitle('Logout octocat'));

expect(logoutFromAccountMock).toHaveBeenCalledTimes(1);

expect(ipcRenderer.send).toHaveBeenCalledTimes(2);
expect(ipcRenderer.send).toHaveBeenCalledWith('update-icon');
expect(ipcRenderer.send).toHaveBeenCalledWith('update-title', '');
expect(mockNavigate).toHaveBeenNthCalledWith(1, -1);
});
});

describe('Add new accounts', () => {
describe('Login with Personal Access Token', () => {
it('should show login with personal access token button if not logged in', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: { accounts: [mockOAuthAccount] },
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

expect(
screen.getByTitle('Login with Personal Access Token').hidden,
).toBe(false);

fireEvent.click(screen.getByTitle('Login with Personal Access Token'));
expect(mockNavigate).toHaveBeenNthCalledWith(
1,
'/login-personal-access-token',
{
replace: true,
},
);
});

it('should hide login with personal access token button if already logged in', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: { accounts: [mockPersonalAccessTokenAccount] },
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

expect(
screen.getByTitle('Login with Personal Access Token').hidden,
).toBe(true);
});
});

describe('Login with OAuth App', () => {
it('should show login with oauth app if not logged in', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: { accounts: [mockPersonalAccessTokenAccount] },
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

expect(screen.getByTitle('Login with OAuth App').hidden).toBe(false);

fireEvent.click(screen.getByTitle('Login with OAuth App'));
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/login-oauth-app', {
replace: true,
});
});

it('should hide login with oauth app route if already logged in', async () => {
await act(async () => {
render(
<AppContext.Provider
value={{
auth: { accounts: [mockOAuthAccount] },
settings: mockSettings,
}}
>
<MemoryRouter>
<AccountsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
});

expect(screen.getByTitle('Login with OAuth App').hidden).toBe(true);
});
});
});
});
Loading

0 comments on commit 8f3a500

Please sign in to comment.