Skip to content

Commit

Permalink
Merge 4757dc5 into 7f04b75
Browse files Browse the repository at this point in the history
  • Loading branch information
afonsojramos committed Dec 6, 2023
2 parents 7f04b75 + 4757dc5 commit 7bff9ae
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 29 deletions.
3 changes: 3 additions & 0 deletions src/components/fields/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IFieldCheckbox {
checked: boolean;
onChange: any;
placeholder?: string;
disabled?: boolean;
}

export const FieldCheckbox = (props: IFieldCheckbox) => {
Expand All @@ -18,13 +19,15 @@ export const FieldCheckbox = (props: IFieldCheckbox) => {
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
checked={props.checked}
onChange={props.onChange}
disabled={props.disabled}
/>
</div>

<div className="ml-3 text-sm">
<label
htmlFor={props.name}
className="font-medium text-gray-700 dark:text-gray-200"
style={{ textDecoration: props.disabled && 'line-through' }}
>
{props.label}
</label>
Expand Down
4 changes: 2 additions & 2 deletions src/context/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ describe('context/App.tsx', () => {
participating: true,
playSound: true,
showNotifications: true,
colors: true,
colors: false,
},
);
});
Expand Down Expand Up @@ -302,7 +302,7 @@ describe('context/App.tsx', () => {
participating: false,
playSound: true,
showNotifications: true,
colors: true,
colors: false,
},
);
});
Expand Down
2 changes: 1 addition & 1 deletion src/context/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const defaultSettings: SettingsState = {
markOnClick: false,
openAtStartup: false,
appearance: Appearance.SYSTEM,
colors: true,
colors: false,
};

interface AppContextState {
Expand Down
164 changes: 147 additions & 17 deletions src/routes/Settings.test.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,43 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import TestRenderer, { act } from 'react-test-renderer';
import { render, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import TestRenderer, { act } from 'react-test-renderer';

const { ipcRenderer } = require('electron');

import { SettingsRoute } from './Settings';
import { AxiosResponse } from 'axios';
import { mockAccounts, mockSettings } from '../__mocks__/mock-state';
import { AppContext } from '../context/App';
import { mockSettings } from '../__mocks__/mock-state';
import * as apiRequests from '../utils/api-requests';
import Constants from '../utils/constants';
import { SettingsRoute } from './Settings';

const mockNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockNavigate,
}));
jest.spyOn(apiRequests, 'apiRequestAuth').mockResolvedValue({
headers: {
'x-oauth-scopes': Constants.AUTH_SCOPE.join(', '),
},
} as unknown as AxiosResponse);

describe('routes/Settings.tsx', () => {
const updateSetting = jest.fn();

beforeEach(() => {
mockNavigate.mockReset();
updateSetting.mockReset();
jest.clearAllMocks();
});

it('should render itself & its children', async () => {
let tree: TestRenderer;

await act(async () => {
tree = TestRenderer.create(
<AppContext.Provider value={{ settings: mockSettings }}>
<AppContext.Provider
value={{ settings: mockSettings, accounts: mockAccounts }}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -45,7 +54,11 @@ describe('routes/Settings.tsx', () => {
await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider
value={{ settings: mockSettings, logout: logoutMock }}
value={{
settings: mockSettings,
accounts: mockAccounts,
logout: logoutMock,
}}
>
<MemoryRouter>
<SettingsRoute />
Expand All @@ -70,7 +83,12 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -88,7 +106,13 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -110,7 +134,13 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -132,7 +162,13 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -154,7 +190,13 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -176,7 +218,13 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -198,7 +246,13 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -218,7 +272,12 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -238,7 +297,9 @@ describe('routes/Settings.tsx', () => {

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AppContext.Provider
value={{ settings: mockSettings, accounts: mockAccounts }}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
Expand All @@ -250,4 +311,73 @@ describe('routes/Settings.tsx', () => {
fireEvent.click(getByLabelText('Quit Gitify'));
expect(ipcRenderer.send).toHaveBeenCalledWith('app-quit');
});

it('should be able to enable colors', async () => {
let getByLabelText;
let findByLabelText;

jest.spyOn(apiRequests, 'apiRequestAuth').mockResolvedValue({
headers: {
'x-oauth-scopes': Constants.AUTH_SCOPE.join(', '),
},
} as unknown as AxiosResponse);

await act(async () => {
const {
getByLabelText: getByLabelTextLocal,
findByLabelText: findByLabelTextLocal,
} = render(
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
getByLabelText = getByLabelTextLocal;
findByLabelText = findByLabelTextLocal;
});

await findByLabelText('Use GitHub-like state colors');

fireEvent.click(getByLabelText('Use GitHub-like state colors'));

expect(updateSetting).toHaveBeenCalledTimes(1);
expect(updateSetting).toHaveBeenCalledWith('colors', true);
});

it('should not be able to disable colors', async () => {
let queryByLabelText;

jest.spyOn(apiRequests, 'apiRequestAuth').mockResolvedValue({
headers: {
'x-oauth-scopes': 'read:user, notifications',
},
} as unknown as AxiosResponse);

await act(async () => {
const { queryByLabelText: queryByLabelLocal } = render(
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
queryByLabelText = queryByLabelLocal;
});

expect(
queryByLabelText('Use GitHub-like state colors (requires re-auth)'),
).toBeDefined();
});
});
44 changes: 36 additions & 8 deletions src/routes/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import React, { useCallback, useContext, useState, useEffect } from 'react';
import { ArrowLeftIcon } from '@primer/octicons-react';
import { ipcRenderer } from 'electron';
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { useNavigate } from 'react-router-dom';
import { ArrowLeftIcon } from '@primer/octicons-react';

import { AppContext } from '../context/App';
import { Appearance } from '../types';
import { FieldCheckbox } from '../components/fields/Checkbox';
import { FieldRadioGroup } from '../components/fields/RadioGroup';
import { AppContext } from '../context/App';
import { IconAddAccount } from '../icons/AddAccount';
import { IconLogOut } from '../icons/Logout';
import { IconQuit } from '../icons/Quit';
import { updateTrayIcon } from '../utils/comms';
import { Appearance } from '../types';
import { apiRequestAuth } from '../utils/api-requests';
import { setAppearance } from '../utils/appearance';
import { updateTrayIcon } from '../utils/comms';
import Constants from '../utils/constants';
import { generateGitHubAPIUrl } from '../utils/helpers';

export const SettingsRoute: React.FC = () => {
const { settings, updateSetting, logout } = useContext(AppContext);
const { accounts, settings, updateSetting, logout } = useContext(AppContext);
const navigate = useNavigate();

const [isLinux, setIsLinux] = useState<boolean>(false);
const [appVersion, setAppVersion] = useState<string | null>(null);
const [colorScope, setColorScope] = useState<boolean>(false);

useEffect(() => {
ipcRenderer.invoke('get-platform').then((result: string) => {
Expand All @@ -30,6 +40,21 @@ export const SettingsRoute: React.FC = () => {
});
}, []);

useMemo(() => {
(async () => {
const response = await apiRequestAuth(
`${generateGitHubAPIUrl(Constants.DEFAULT_AUTH_OPTIONS.hostname)}`,
'GET',
accounts.token,
);

console.info("Token's scopes:", response.headers['x-oauth-scopes']);

if (response.headers['x-oauth-scopes'].includes('repo'))
setColorScope(true);
})();
}, [accounts.token]);

ipcRenderer.on('update-native-theme', (_, updatedAppearance: Appearance) => {
if (settings.appearance === Appearance.SYSTEM) {
setAppearance(updatedAppearance);
Expand Down Expand Up @@ -84,9 +109,12 @@ export const SettingsRoute: React.FC = () => {

<FieldCheckbox
name="colors"
label="Use colors to indicate state"
checked={settings.colors}
label={`Use GitHub-like state colors${
!colorScope ? ' (requires repo scope)' : ''
}`}
checked={colorScope && settings.colors}
onChange={(evt) => updateSetting('colors', evt.target.checked)}
disabled={!colorScope}
/>
<FieldCheckbox
name="showOnlyParticipating"
Expand Down
Loading

0 comments on commit 7bff9ae

Please sign in to comment.