Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AppContext, AppProvider } from './context/App';
import { Loading } from './components/Loading';
import { LoginEnterpriseRoute } from './routes/LoginEnterprise';
import { LoginRoute } from './routes/Login';
import { LoginWithToken } from './routes/LoginWithToken';
import { NotificationsRoute } from './routes/Notifications';
import { SettingsRoute } from './routes/Settings';
import { Sidebar } from './components/Sidebar';
Expand Down Expand Up @@ -45,7 +46,8 @@ export const App = () => {
<PrivateRoute path="/" exact component={NotificationsRoute} />
<PrivateRoute path="/settings" exact component={SettingsRoute} />
<Route path="/login" component={LoginRoute} />
<Route path="/enterpriselogin" component={LoginEnterpriseRoute} />
<Route path="/login-enterprise" component={LoginEnterpriseRoute} />
<Route path="/login-token" component={LoginWithToken} />
</Switch>
</div>
</Router>
Expand Down
79 changes: 42 additions & 37 deletions src/components/fields/FieldInput.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
import * as React from 'react';
import React from 'react';
import { Field } from 'react-final-form';

export interface IProps {
name: string;
type: string;
type?: string;
label: string;
placeholder?: string;
helpText?: React.ReactNode | string;
required?: boolean;
}

export class FieldInput extends React.PureComponent<IProps> {
public static defaultProps = {
type: 'text',
placeholder: '',
required: false,
};
export const FieldInput: React.FC<IProps> = ({
label,
name,
placeholder = '',
helpText,
type = 'text',
required = false,
}) => {
return (
<Field name={name}>
{({ input, meta: { touched, error } }) => (
<div className="mt-2">
<label
className="block tracking-wide text-grey-dark text-sm font-semibold mb-2"
htmlFor={input.name}
>
{label}
</label>

render() {
const { label, name, placeholder } = this.props;
<input
type={type}
className="appearance-none block w-full dark:text-gray-800 bg-gray-100 border border-red rounded py-1.5 px-4 mb-2 focus:bg-gray-200 focus:outline-none"
id={input.name}
placeholder={placeholder}
required={required}
{...input}
/>

return (
<Field name={name}>
{({ input, meta: { touched, error } }) => (
<div className="mt-2">
<label
className="block tracking-wide text-grey-dark text-sm font-semibold mb-2"
htmlFor={input.name}
>
{label}
</label>
{helpText && (
<div className="mt-3 text-gray-700 dark:text-gray-200 text-xs">
{helpText}
</div>
)}

<input
type="text"
className="appearance-none block w-full dark:text-gray-800 bg-gray-100 border border-red rounded py-2 px-4 mb-2 focus:bg-gray-200 focus:outline-none"
id={input.name}
placeholder={placeholder}
{...input}
/>

{touched && error && (
<div className="text-red-500 text-xs italic">{error}</div>
)}
</div>
)}
</Field>
);
}
}
{touched && error && (
<div className="mt-2 text-red-500 text-xs italic">{error}</div>
)}
</div>
)}
</Field>
);
};
53 changes: 53 additions & 0 deletions src/context/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AppContext, AppProvider } from './App';
import { AuthState, SettingsState } from '../types';
import { mockAccounts, mockSettings } from '../__mocks__/mock-state';
import { useNotifications } from '../hooks/useNotifications';
import * as apiRequests from '../utils/api-requests';
import * as comms from '../utils/comms';
import * as storage from '../utils/storage';

Expand All @@ -29,6 +30,7 @@ describe('context/App.tsx', () => {

describe('api methods', () => {
const updateSettingMock = jest.fn();
const apiRequestAuthMock = jest.spyOn(apiRequests, 'apiRequestAuth');

const fetchNotificationsMock = jest.fn();
const markNotificationMock = jest.fn();
Expand Down Expand Up @@ -164,6 +166,57 @@ describe('context/App.tsx', () => {
'github.com'
);
});

it('should call validateToken', async () => {
apiRequestAuthMock.mockResolvedValueOnce(null);

const TestComponent = () => {
const { validateToken } = useContext(AppContext);

return (
<button
onClick={() =>
validateToken({ hostname: 'github.com', token: '123-456' })
}
>
Test Case
</button>
);
};

const { getByText } = customRender(<TestComponent />);

fireEvent.click(getByText('Test Case'));

await waitFor(() =>
expect(fetchNotificationsMock).toHaveBeenCalledTimes(2)
);

expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
expect(apiRequestAuthMock).toHaveBeenCalledWith(
'https://api.github.com/notifications',
'HEAD',
'123-456'
);
});
});

it('should call logout', async () => {
const clearStateMock = jest.spyOn(storage, 'clearState');

const TestComponent = () => {
const { logout } = useContext(AppContext);

return <button onClick={logout}>Test Case</button>;
};

const { getByText } = customRender(<TestComponent />);

act(() => {
fireEvent.click(getByText('Test Case'));
});

expect(clearStateMock).toHaveBeenCalledTimes(1);
});

it('should call updateSetting', async () => {
Expand Down
31 changes: 25 additions & 6 deletions src/context/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import {
Appearance,
AuthOptions,
AuthState,
AuthTokenOptions,
SettingsState,
} from '../types';
import { authGitHub, getToken } from '../utils/auth';
import { apiRequestAuth } from '../utils/api-requests';
import { addAccount, authGitHub, getToken } from '../utils/auth';
import { clearState, loadState, saveState } from '../utils/storage';
import { setAppearance } from '../utils/appearance';
import { setAutoLaunch } from '../utils/comms';
Expand All @@ -39,6 +41,7 @@ interface AppContextState {
isLoggedIn: boolean;
login: () => void;
loginEnterprise: (data: AuthOptions) => void;
validateToken: (data: AuthTokenOptions) => void;
logout: () => void;

notifications: AccountNotifications[];
Expand Down Expand Up @@ -110,16 +113,31 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
const { token } = await getToken(authCode);
setAccounts({ ...accounts, token });
saveState({ ...accounts, token }, settings);
}, [accounts]);
}, [accounts, settings]);

const loginEnterprise = useCallback(
async (data: AuthOptions) => {
const { authOptions, authCode } = await authGitHub(data);
const { token } = await getToken(authCode, authOptions);
setAccounts({ ...accounts, token });
saveState({ ...accounts, token }, settings);
const { token, hostname } = await getToken(authCode, authOptions);
const updatedAccounts = addAccount(accounts, token, hostname);
setAccounts(updatedAccounts);
saveState(updatedAccounts, settings);
},
[accounts, settings]
);

const validateToken = useCallback(
async ({ token, hostname }: AuthTokenOptions) => {
await apiRequestAuth(
`https://api.${hostname}/notifications`,
'HEAD',
token
);
const updatedAccounts = addAccount(accounts, token, hostname);
setAccounts(updatedAccounts);
saveState(updatedAccounts, settings);
},
[accounts]
[accounts, settings]
);

const logout = useCallback(() => {
Expand Down Expand Up @@ -169,6 +187,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
isLoggedIn,
login,
loginEnterprise,
validateToken,
logout,

notifications,
Expand Down
2 changes: 1 addition & 1 deletion src/routes/Login.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ describe('routes/Login.tsx', () => {
fireEvent.click(getByLabelText('Login with GitHub Enterprise'));

expect(pushMock).toHaveBeenCalledTimes(1);
expect(pushMock).toHaveBeenCalledWith('/enterpriselogin');
expect(pushMock).toHaveBeenCalledWith('/login-enterprise');
});
});
14 changes: 11 additions & 3 deletions src/routes/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,23 @@ export const LoginRoute: React.FC = () => {
onClick={loginUser}
aria-label="Login with GitHub"
>
<span>Login to GitHub</span>
Login to GitHub
</button>

<button
className={loginButtonClass}
onClick={() => history.push('/enterpriselogin')}
onClick={() => history.push('/login-enterprise')}
aria-label="Login with GitHub Enterprise"
>
<span>Login to GitHub Enterprise</span>
Login to GitHub Enterprise
</button>

<button
className="bg-none hover:text-gray-800 dark:text-gray-100 dark:hover:text-gray-300 mt-4 focus:outline-none"
onClick={() => history.push('/login-token')}
aria-label="Login with Personal Token"
>
<small>or login with a personal token</small>
</button>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/routes/LoginEnterprise.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const LoginEnterpriseRoute: React.FC = () => {
type="submit"
title="Login Button"
>
<span>Login</span>
Login
</button>
</form>
);
Expand Down
Loading