*/ | */ | ||||
import { differenceInMilliseconds, isAfter, isBefore } from 'date-fns'; | import { differenceInMilliseconds, isAfter, isBefore } from 'date-fns'; | ||||
import { cloneDeep, groupBy, sortBy } from 'lodash'; | import { cloneDeep, groupBy, sortBy } from 'lodash'; | ||||
import { PAGE_SIZE } from '../../apps/background-tasks/constants'; | |||||
import { parseDate } from '../../helpers/dates'; | import { parseDate } from '../../helpers/dates'; | ||||
import { mockTask } from '../../helpers/mocks/tasks'; | import { mockTask } from '../../helpers/mocks/tasks'; | ||||
import { ActivityRequestParameters, Task, TaskStatuses, TaskTypes } from '../../types/tasks'; | import { ActivityRequestParameters, Task, TaskStatuses, TaskTypes } from '../../types/tasks'; | ||||
const RANDOM_RADIX = 36; | const RANDOM_RADIX = 36; | ||||
const RANDOM_PREFIX = 2; | const RANDOM_PREFIX = 2; | ||||
const PAGE_SIZE = 100; | |||||
const TASK_TYPES = [ | const TASK_TYPES = [ | ||||
TaskTypes.Report, | TaskTypes.Report, | ||||
TaskTypes.IssueSync, | TaskTypes.IssueSync, |
import { | import { | ||||
AddRemoveNotificationParameters, | AddRemoveNotificationParameters, | ||||
Notification, | Notification, | ||||
NotificationGlobalType, | |||||
NotificationProjectType, | |||||
NotificationsResponse, | NotificationsResponse, | ||||
} from '../../types/notifications'; | } from '../../types/notifications'; | ||||
import { addNotification, getNotifications, removeNotification } from '../notifications'; | import { addNotification, getNotifications, removeNotification } from '../notifications'; | ||||
/* Constants */ | /* Constants */ | ||||
const channels = ['EmailNotificationChannel']; | const channels = ['EmailNotificationChannel']; | ||||
const globalTypes = ['CeReportTaskFailure', 'ChangesOnMyIssue', 'NewAlerts', 'SQ-MyNewIssues']; | |||||
const perProjectTypes = [ | |||||
'CeReportTaskFailure', | |||||
'ChangesOnMyIssue', | |||||
'NewAlerts', | |||||
'NewFalsePositiveIssue', | |||||
'NewIssues', | |||||
'SQ-MyNewIssues', | |||||
]; | |||||
const defaultNotifications: Notification[] = [ | const defaultNotifications: Notification[] = [ | ||||
{ channel: 'EmailNotificationChannel', type: 'ChangesOnMyIssue' }, | { channel: 'EmailNotificationChannel', type: 'ChangesOnMyIssue' }, | ||||
]; | ]; | ||||
handleGetNotifications: () => Promise<NotificationsResponse> = () => { | handleGetNotifications: () => Promise<NotificationsResponse> = () => { | ||||
return Promise.resolve({ | return Promise.resolve({ | ||||
channels: [...channels], | channels: [...channels], | ||||
globalTypes: [...globalTypes], | |||||
globalTypes: Object.values(NotificationGlobalType), | |||||
notifications: cloneDeep(this.notifications), | notifications: cloneDeep(this.notifications), | ||||
perProjectTypes: [...perProjectTypes], | |||||
perProjectTypes: Object.values(NotificationProjectType), | |||||
}); | }); | ||||
}; | }; | ||||
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { mockComponent } from '../../../../../../../helpers/mocks/component'; | import { mockComponent } from '../../../../../../../helpers/mocks/component'; | ||||
import { | |||||
NotificationGlobalType, | |||||
NotificationProjectType, | |||||
} from '../../../../../../../types/notifications'; | |||||
import { ProjectNotifications } from '../ProjectNotifications'; | import { ProjectNotifications } from '../ProjectNotifications'; | ||||
jest.mock('react', () => { | jest.mock('react', () => { | ||||
addNotification={jest.fn()} | addNotification={jest.fn()} | ||||
channels={['channel1', 'channel2']} | channels={['channel1', 'channel2']} | ||||
component={mockComponent({ key: 'foo' })} | component={mockComponent({ key: 'foo' })} | ||||
globalTypes={['type-global', 'type-common']} | |||||
globalTypes={[NotificationGlobalType.CeReportTaskFailure, NotificationGlobalType.NewAlerts]} | |||||
loading={false} | loading={false} | ||||
notifications={[ | notifications={[ | ||||
{ | { | ||||
projectName: 'Qux', | projectName: 'Qux', | ||||
}, | }, | ||||
]} | ]} | ||||
perProjectTypes={['type-common']} | |||||
perProjectTypes={[NotificationProjectType.CeReportTaskFailure]} | |||||
removeNotification={jest.fn()} | removeNotification={jest.fn()} | ||||
{...props} | {...props} | ||||
/> | /> |
project={true} | project={true} | ||||
types={ | types={ | ||||
Array [ | Array [ | ||||
"type-common", | |||||
"CeReportTaskFailure", | |||||
] | ] | ||||
} | } | ||||
/> | /> |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { screen, waitFor, within } from '@testing-library/react'; | |||||
import { screen, within } from '@testing-library/react'; | |||||
import userEvent from '@testing-library/user-event'; | import userEvent from '@testing-library/user-event'; | ||||
import { UserEvent } from '@testing-library/user-event/dist/types/setup'; | import { UserEvent } from '@testing-library/user-event/dist/types/setup'; | ||||
import selectEvent from 'react-select-event'; | import selectEvent from 'react-select-event'; | ||||
import { byLabelText, byRole, byText } from 'testing-library-selector'; | |||||
import { getMyProjects, getScannableProjects } from '../../../api/components'; | import { getMyProjects, getScannableProjects } from '../../../api/components'; | ||||
import NotificationsMock from '../../../api/mocks/NotificationsMock'; | import NotificationsMock from '../../../api/mocks/NotificationsMock'; | ||||
import UserTokensMock from '../../../api/mocks/UserTokensMock'; | import UserTokensMock from '../../../api/mocks/UserTokensMock'; | ||||
import { setKeyboardShortcutEnabled } from '../../../helpers/preferences'; | import { setKeyboardShortcutEnabled } from '../../../helpers/preferences'; | ||||
import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; | import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; | ||||
import { renderAppRoutes } from '../../../helpers/testReactTestingUtils'; | import { renderAppRoutes } from '../../../helpers/testReactTestingUtils'; | ||||
import { NotificationGlobalType, NotificationProjectType } from '../../../types/notifications'; | |||||
import { Permissions } from '../../../types/permissions'; | import { Permissions } from '../../../types/permissions'; | ||||
import { TokenType } from '../../../types/token'; | import { TokenType } from '../../../types/token'; | ||||
import { CurrentUser } from '../../../types/users'; | import { CurrentUser } from '../../../types/users'; | ||||
}); | }); | ||||
describe('notifications page', () => { | describe('notifications page', () => { | ||||
const projectUI = { | |||||
title: byRole('button', { name: 'my_profile.per_project_notifications.add' }), | |||||
addButton: byRole('button', { name: 'my_profile.per_project_notifications.add' }), | |||||
addModalButton: byRole('button', { name: 'add_verb' }), | |||||
searchInput: byLabelText('search_verb', { selector: 'input' }), | |||||
sonarQubeProject: byRole('heading', { name: 'SonarQube' }), | |||||
checkbox: (type: NotificationProjectType) => | |||||
byRole('checkbox', { | |||||
name: `notification.dispatcher.descrption_x.notification.dispatcher.${type}.project`, | |||||
}), | |||||
}; | |||||
const globalUI = { | |||||
title: byRole('heading', { name: 'my_profile.overall_notifications.title' }), | |||||
noNotificationForProject: byText('my_account.no_project_notifications'), | |||||
checkbox: (type: NotificationGlobalType) => | |||||
byRole('checkbox', { | |||||
name: `notification.dispatcher.descrption_x.notification.dispatcher.${type}`, | |||||
}), | |||||
}; | |||||
let notificationsMock: NotificationsMock; | let notificationsMock: NotificationsMock; | ||||
beforeAll(() => { | beforeAll(() => { | ||||
notificationsMock = new NotificationsMock(); | notificationsMock = new NotificationsMock(); | ||||
const notificationsPagePath = 'account/notifications'; | const notificationsPagePath = 'account/notifications'; | ||||
it('should display global notifications status and allow edits', async () => { | it('should display global notifications status and allow edits', async () => { | ||||
const user = userEvent.setup(); | |||||
const user = userEvent.setup({ delay: null }); | |||||
renderAccountApp(mockLoggedInUser(), notificationsPagePath); | renderAccountApp(mockLoggedInUser(), notificationsPagePath); | ||||
expect( | |||||
await screen.findByRole('heading', { name: 'my_profile.overall_notifications.title' }) | |||||
).toBeInTheDocument(); | |||||
expect(screen.getAllByRole('row')).toHaveLength(5); // 4 + header | |||||
expect(await globalUI.title.find()).toBeInTheDocument(); | |||||
/* | /* | ||||
* Verify Checkbox statuses | * Verify Checkbox statuses | ||||
*/ | */ | ||||
expect(getCheckboxByRowName('notification.dispatcher.ChangesOnMyIssue')).toBeChecked(); | |||||
// first row is the header: skip it! | |||||
const otherRows = screen | |||||
.getAllByRole('row', { | |||||
name: (n: string) => n !== 'notification.dispatcher.ChangesOnMyIssue', | |||||
}) | |||||
.slice(1); | |||||
otherRows.forEach((row) => { | |||||
expect(within(row).getByRole('checkbox')).not.toBeChecked(); | |||||
}); | |||||
// Make sure the second block is empty | |||||
expect(screen.getByText('my_account.no_project_notifications')).toBeInTheDocument(); | |||||
expect(globalUI.checkbox(NotificationGlobalType.ChangesOnMyIssue).get()).toBeChecked(); | |||||
expect(globalUI.checkbox(NotificationGlobalType.CeReportTaskFailure).get()).not.toBeChecked(); | |||||
expect(globalUI.checkbox(NotificationGlobalType.NewAlerts).get()).not.toBeChecked(); | |||||
expect(globalUI.checkbox(NotificationGlobalType.MyNewIssues).get()).not.toBeChecked(); | |||||
/* | /* | ||||
* Update notifications | * Update notifications | ||||
*/ | */ | ||||
await user.click(getCheckboxByRowName('notification.dispatcher.ChangesOnMyIssue')); | |||||
expect(getCheckboxByRowName('notification.dispatcher.ChangesOnMyIssue')).not.toBeChecked(); | |||||
await user.click(globalUI.checkbox(NotificationGlobalType.ChangesOnMyIssue).get()); | |||||
expect(globalUI.checkbox(NotificationGlobalType.ChangesOnMyIssue).get()).not.toBeChecked(); | |||||
await user.click(getCheckboxByRowName('notification.dispatcher.NewAlerts')); | |||||
expect(getCheckboxByRowName('notification.dispatcher.NewAlerts')).toBeChecked(); | |||||
await user.click(globalUI.checkbox(NotificationGlobalType.NewAlerts).get()); | |||||
expect(globalUI.checkbox(NotificationGlobalType.NewAlerts).get()).toBeChecked(); | |||||
}); | }); | ||||
it('should allow adding notifications for a project', async () => { | it('should allow adding notifications for a project', async () => { | ||||
renderAccountApp(mockLoggedInUser(), notificationsPagePath); | renderAccountApp(mockLoggedInUser(), notificationsPagePath); | ||||
await user.click( | |||||
await screen.findByRole('button', { name: 'my_profile.per_project_notifications.add' }) | |||||
); | |||||
expect(await screen.findByLabelText('search_verb', { selector: 'input' })).toBeInTheDocument(); | |||||
expect(screen.getByRole('button', { name: 'add_verb' })).toBeDisabled(); | |||||
await user.keyboard('sonar'); | |||||
await user.click(await projectUI.addButton.find()); | |||||
expect(projectUI.addModalButton.get()).toBeDisabled(); | |||||
await user.type(projectUI.searchInput.get(), 'sonar'); | |||||
// navigate within the two results, choose the first: | // navigate within the two results, choose the first: | ||||
await user.keyboard('[ArrowDown][ArrowDown][ArrowUp][Enter]'); | await user.keyboard('[ArrowDown][ArrowDown][ArrowUp][Enter]'); | ||||
await user.click(projectUI.addModalButton.get()); | |||||
const addButton = screen.getByRole('button', { name: 'add_verb' }); | |||||
expect(addButton).toBeEnabled(); | |||||
await user.click(addButton); | |||||
expect(screen.getByRole('heading', { name: 'SonarQube' })).toBeInTheDocument(); | |||||
expect(projectUI.sonarQubeProject.get()).toBeInTheDocument(); | |||||
expect( | expect( | ||||
getCheckboxByRowName('notification.dispatcher.NewFalsePositiveIssue.project') | |||||
projectUI.checkbox(NotificationProjectType.NewFalsePositiveIssue).get() | |||||
).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
await user.click(getCheckboxByRowName('notification.dispatcher.NewAlerts.project')); | |||||
expect(getCheckboxByRowName('notification.dispatcher.NewAlerts.project')).toBeChecked(); | |||||
expect(screen.getAllByRole('checkbox', { checked: true })).toHaveLength(2); | |||||
await user.click(projectUI.checkbox(NotificationProjectType.NewAlerts).get()); | |||||
expect(projectUI.checkbox(NotificationProjectType.NewAlerts).get()).toBeChecked(); | |||||
await user.click(getCheckboxByRowName('notification.dispatcher.NewAlerts.project')); | |||||
expect(getCheckboxByRowName('notification.dispatcher.NewAlerts.project')).not.toBeChecked(); | |||||
await user.click(projectUI.checkbox(NotificationProjectType.NewAlerts).get()); | |||||
expect(projectUI.checkbox(NotificationProjectType.NewAlerts).get()).not.toBeChecked(); | |||||
}); | }); | ||||
it('should allow searching for projects', async () => { | it('should allow searching for projects', async () => { | ||||
await user.click(screen.getByRole('searchbox')); | await user.click(screen.getByRole('searchbox')); | ||||
await user.keyboard('bla'); | await user.keyboard('bla'); | ||||
await waitFor(() => { | |||||
expect(screen.queryByRole('heading', { name: 'SonarQube' })).not.toBeInTheDocument(); | |||||
}); | |||||
expect(screen.queryByRole('heading', { name: 'SonarQube' })).not.toBeInTheDocument(); | |||||
await user.keyboard('[Backspace>3/]'); | await user.keyboard('[Backspace>3/]'); | ||||
return result; | return result; | ||||
} | } | ||||
function getCheckboxByRowName(name: string) { | |||||
return within(screen.getByRole('row', { name })).getByRole('checkbox'); | |||||
} | |||||
function renderAccountApp(currentUser: CurrentUser, navigateTo?: string) { | function renderAccountApp(currentUser: CurrentUser, navigateTo?: string) { | ||||
renderAppRoutes('account', routes, { currentUser, navigateTo }); | renderAppRoutes('account', routes, { currentUser, navigateTo }); | ||||
} | } |
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { Notification } from '../../../types/notifications'; | |||||
import { Notification, NotificationGlobalType } from '../../../types/notifications'; | |||||
import NotificationsList from './NotificationsList'; | import NotificationsList from './NotificationsList'; | ||||
interface Props { | interface Props { | ||||
channels: string[]; | channels: string[]; | ||||
notifications: Notification[]; | notifications: Notification[]; | ||||
removeNotification: (n: Notification) => void; | removeNotification: (n: Notification) => void; | ||||
types: string[]; | |||||
types: NotificationGlobalType[]; | |||||
} | } | ||||
export default function GlobalNotifications(props: Props) { | export default function GlobalNotifications(props: Props) { |
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import Checkbox from '../../../components/controls/Checkbox'; | import Checkbox from '../../../components/controls/Checkbox'; | ||||
import { hasMessage, translate } from '../../../helpers/l10n'; | |||||
import { Notification } from '../../../types/notifications'; | |||||
import { hasMessage, translate, translateWithParameters } from '../../../helpers/l10n'; | |||||
import { | |||||
Notification, | |||||
NotificationGlobalType, | |||||
NotificationProjectType, | |||||
} from '../../../types/notifications'; | |||||
interface Props { | interface Props { | ||||
onAdd: (n: Notification) => void; | onAdd: (n: Notification) => void; | ||||
channels: string[]; | channels: string[]; | ||||
checkboxId: (type: string, channel: string) => string; | checkboxId: (type: string, channel: string) => string; | ||||
project?: boolean; | project?: boolean; | ||||
types: string[]; | |||||
types: (NotificationGlobalType | NotificationProjectType)[]; | |||||
notifications: Notification[]; | notifications: Notification[]; | ||||
} | } | ||||
{channels.map((channel) => ( | {channels.map((channel) => ( | ||||
<td className="text-center" key={channel}> | <td className="text-center" key={channel}> | ||||
<Checkbox | <Checkbox | ||||
label={translateWithParameters( | |||||
'notification.dispatcher.descrption_x', | |||||
this.getDispatcherLabel(type) | |||||
)} | |||||
checked={this.isEnabled(type, channel)} | checked={this.isEnabled(type, channel)} | ||||
id={checkboxId(type, channel)} | id={checkboxId(type, channel)} | ||||
onCheck={(checked) => this.handleCheck(type, channel, checked)} | onCheck={(checked) => this.handleCheck(type, channel, checked)} |
import * as React from 'react'; | import * as React from 'react'; | ||||
import BoxedGroupAccordion from '../../../components/controls/BoxedGroupAccordion'; | import BoxedGroupAccordion from '../../../components/controls/BoxedGroupAccordion'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { Notification, NotificationProject } from '../../../types/notifications'; | |||||
import { | |||||
Notification, | |||||
NotificationProject, | |||||
NotificationProjectType, | |||||
} from '../../../types/notifications'; | |||||
import NotificationsList from './NotificationsList'; | import NotificationsList from './NotificationsList'; | ||||
interface Props { | interface Props { | ||||
notifications: Notification[]; | notifications: Notification[]; | ||||
project: NotificationProject; | project: NotificationProject; | ||||
removeNotification: (n: Notification) => void; | removeNotification: (n: Notification) => void; | ||||
types: string[]; | |||||
types: NotificationProjectType[]; | |||||
} | } | ||||
export default function ProjectNotifications(props: Props) { | export default function ProjectNotifications(props: Props) { |
import { Button } from '../../../components/controls/buttons'; | import { Button } from '../../../components/controls/buttons'; | ||||
import SearchBox from '../../../components/controls/SearchBox'; | import SearchBox from '../../../components/controls/SearchBox'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { Notification, NotificationProject } from '../../../types/notifications'; | |||||
import { | |||||
Notification, | |||||
NotificationProject, | |||||
NotificationProjectType, | |||||
} from '../../../types/notifications'; | |||||
import ProjectModal from './ProjectModal'; | import ProjectModal from './ProjectModal'; | ||||
import ProjectNotifications from './ProjectNotifications'; | import ProjectNotifications from './ProjectNotifications'; | ||||
channels: string[]; | channels: string[]; | ||||
notifications: Notification[]; | notifications: Notification[]; | ||||
removeNotification: (n: Notification) => void; | removeNotification: (n: Notification) => void; | ||||
types: string[]; | |||||
types: NotificationProjectType[]; | |||||
} | } | ||||
const THRESHOLD_COLLAPSED = 3; | const THRESHOLD_COLLAPSED = 3; |
jest.mock('../../../api/ce'); | jest.mock('../../../api/ce'); | ||||
jest.mock('../constants', () => { | |||||
const contants = jest.requireActual('../constants'); | |||||
return { ...contants, PAGE_SIZE: 9 }; | |||||
}); | |||||
let computeEngineServiceMock: ComputeEngineServiceMock; | let computeEngineServiceMock: ComputeEngineServiceMock; | ||||
beforeAll(() => { | beforeAll(() => { | ||||
const user = userEvent.setup(); | const user = userEvent.setup(); | ||||
computeEngineServiceMock.clearTasks(); | computeEngineServiceMock.clearTasks(); | ||||
computeEngineServiceMock.createTasks(101); | |||||
computeEngineServiceMock.createTasks(10); | |||||
renderGlobalBackgroundTasksApp(); | renderGlobalBackgroundTasksApp(); | ||||
await screen.findByRole('heading', { name: 'background_tasks.page' }) | await screen.findByRole('heading', { name: 'background_tasks.page' }) | ||||
).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
expect(screen.getAllByRole('row')).toHaveLength(101); // including header | |||||
expect(screen.getAllByRole('row')).toHaveLength(10); // including header | |||||
user.click(screen.getByRole('button', { name: 'show_more' })); | user.click(screen.getByRole('button', { name: 'show_more' })); | ||||
await waitFor(() => { | await waitFor(() => { | ||||
expect(screen.getAllByRole('row')).toHaveLength(102); // including header | |||||
expect(screen.getAllByRole('row')).toHaveLength(11); // including header | |||||
}); | }); | ||||
}); | }); | ||||
import { Task, TaskStatuses } from '../../../types/tasks'; | import { Task, TaskStatuses } from '../../../types/tasks'; | ||||
import { Component, Paging, RawQuery } from '../../../types/types'; | import { Component, Paging, RawQuery } from '../../../types/types'; | ||||
import '../background-tasks.css'; | import '../background-tasks.css'; | ||||
import { CURRENTS, DEBOUNCE_DELAY, DEFAULT_FILTERS } from '../constants'; | |||||
import { CURRENTS, DEBOUNCE_DELAY, DEFAULT_FILTERS, PAGE_SIZE } from '../constants'; | |||||
import { mapFiltersToParameters, Query, updateTask } from '../utils'; | import { mapFiltersToParameters, Query, updateTask } from '../utils'; | ||||
import Header from './Header'; | import Header from './Header'; | ||||
import Search from './Search'; | import Search from './Search'; | ||||
types?: string[]; | types?: string[]; | ||||
} | } | ||||
const PAGE_SIZE = 100; | |||||
export class BackgroundTasksApp extends React.PureComponent<Props, State> { | export class BackgroundTasksApp extends React.PureComponent<Props, State> { | ||||
loadTasksDebounced: () => void; | loadTasksDebounced: () => void; | ||||
mounted = false; | mounted = false; |
export const DATE_FORMAT = 'YYYY-MM-DD'; | export const DATE_FORMAT = 'YYYY-MM-DD'; | ||||
export const DEBOUNCE_DELAY = 250; | export const DEBOUNCE_DELAY = 250; | ||||
export const PAGE_SIZE = 100; |
import { uniqWith } from 'lodash'; | import { uniqWith } from 'lodash'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { addNotification, getNotifications, removeNotification } from '../../api/notifications'; | import { addNotification, getNotifications, removeNotification } from '../../api/notifications'; | ||||
import { Notification } from '../../types/notifications'; | |||||
import { | |||||
Notification, | |||||
NotificationGlobalType, | |||||
NotificationProjectType, | |||||
} from '../../types/notifications'; | |||||
import { getWrappedDisplayName } from './utils'; | import { getWrappedDisplayName } from './utils'; | ||||
interface State { | interface State { | ||||
channels: string[]; | channels: string[]; | ||||
globalTypes: string[]; | |||||
globalTypes: NotificationGlobalType[]; | |||||
loading: boolean; | loading: boolean; | ||||
notifications: Notification[]; | notifications: Notification[]; | ||||
perProjectTypes: string[]; | |||||
perProjectTypes: NotificationProjectType[]; | |||||
} | } | ||||
export interface WithNotificationsProps { | export interface WithNotificationsProps { | ||||
addNotification: (added: Notification) => void; | addNotification: (added: Notification) => void; | ||||
channels: string[]; | channels: string[]; | ||||
globalTypes: string[]; | |||||
globalTypes: NotificationGlobalType[]; | |||||
loading: boolean; | loading: boolean; | ||||
notifications: Notification[]; | notifications: Notification[]; | ||||
perProjectTypes: string[]; | |||||
perProjectTypes: NotificationProjectType[]; | |||||
removeNotification: (removed: Notification) => void; | removeNotification: (removed: Notification) => void; | ||||
} | } | ||||
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
export enum NotificationGlobalType { | |||||
CeReportTaskFailure = 'CeReportTaskFailure', | |||||
ChangesOnMyIssue = 'ChangesOnMyIssue', | |||||
NewAlerts = 'NewAlerts', | |||||
MyNewIssues = 'SQ-MyNewIssues', | |||||
} | |||||
export enum NotificationProjectType { | |||||
CeReportTaskFailure = 'CeReportTaskFailure', | |||||
ChangesOnMyIssue = 'ChangesOnMyIssue', | |||||
NewAlerts = 'NewAlerts', | |||||
NewFalsePositiveIssue = 'NewFalsePositiveIssue', | |||||
NewIssues = 'NewIssues', | |||||
MyNewIssues = 'SQ-MyNewIssues', | |||||
} | |||||
export interface Notification { | export interface Notification { | ||||
channel: string; | channel: string; | ||||
project?: string; | project?: string; | ||||
export interface NotificationsResponse { | export interface NotificationsResponse { | ||||
channels: string[]; | channels: string[]; | ||||
globalTypes: string[]; | |||||
globalTypes: NotificationGlobalType[]; | |||||
notifications: Notification[]; | notifications: Notification[]; | ||||
perProjectTypes: string[]; | |||||
perProjectTypes: NotificationProjectType[]; | |||||
} | } | ||||
export interface AddRemoveNotificationParameters { | export interface AddRemoveNotificationParameters { |
notification.dispatcher.SQ-MyNewIssues=My new issues | notification.dispatcher.SQ-MyNewIssues=My new issues | ||||
notification.dispatcher.CeReportTaskFailure=Background tasks in failure on my administered projects | notification.dispatcher.CeReportTaskFailure=Background tasks in failure on my administered projects | ||||
notification.dispatcher.CeReportTaskFailure.project=Background tasks in failure | notification.dispatcher.CeReportTaskFailure.project=Background tasks in failure | ||||
notification.dispatcher.descrption_x=Check to receive notification for {0} | |||||
#------------------------------------------------------------------------------ | #------------------------------------------------------------------------------ | ||||
# | # |