import userEvent from '@testing-library/user-event';
import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
import ComputeEngineServiceMock from '../../../api/mocks/ComputeEngineServiceMock';
-import { renderAppWithAdminContext } from '../../../helpers/testReactTestingUtils';
+import { mockAppState } from '../../../helpers/testMocks';
+import { renderAppWithAdminContext, RenderContext } from '../../../helpers/testReactTestingUtils';
+import { EditionKey } from '../../../types/editions';
import { TaskStatuses, TaskTypes } from '../../../types/tasks';
import routes from '../routes';
expect(await screen.findAllByRole('row')).toHaveLength(2);
});
+ it.each([[EditionKey.community], [EditionKey.developer], [EditionKey.enterprise]])(
+ 'Editions %s should not display node name',
+ async (editionKey: EditionKey) => {
+ givenOneTaskWithoutNodeNameAndOneWithNodeName();
+
+ renderGlobalBackgroundTasksApp({ appState: mockAppState({ edition: editionKey }) });
+
+ await waitFor(() => {
+ expect(screen.getAllByRole('row')).toHaveLength(3); // including header
+ });
+
+ expect(screen.queryByText('background_tasks.table.nodeName')).not.toBeInTheDocument();
+ expect(screen.queryByText('best_node_ever')).not.toBeInTheDocument();
+ }
+ );
+
+ it('Node name should be shown in DCE edition', async () => {
+ givenOneTaskWithoutNodeNameAndOneWithNodeName();
+
+ renderGlobalBackgroundTasksApp({ appState: mockAppState({ edition: EditionKey.datacenter }) });
+
+ await waitFor(async () => {
+ expect(await screen.findByText('background_tasks.table.nodeName')).toBeInTheDocument();
+ });
+
+ expect(
+ within(await screen.getAllByRole('row')[1]).getByText('best_node_ever')
+ ).toBeInTheDocument();
+ });
+
it('should handle task pagination', async () => {
const user = userEvent.setup();
*/
});
+function givenOneTaskWithoutNodeNameAndOneWithNodeName() {
+ computeEngineServiceMock.clearTasks();
+ computeEngineServiceMock.addTask({
+ executedAt: '2022-02-03T11:45:36+0200',
+ submittedAt: '2022-02-03T11:45:35+0200',
+ executionTimeMs: 167,
+ status: TaskStatuses.InProgress,
+ type: TaskTypes.IssueSync,
+ nodeName: 'best_node_ever',
+ });
+ computeEngineServiceMock.addTask({
+ executedAt: '2022-02-03T11:45:35+0200',
+ submittedAt: '2022-02-03T11:45:34+0200',
+ executionTimeMs: 167,
+ status: TaskStatuses.InProgress,
+ type: TaskTypes.IssueSync,
+ });
+}
+
async function changeTaskFilter(user: UserEvent, fieldLabel: string, value: string) {
await user.click(screen.getByLabelText(fieldLabel, { selector: 'input' }));
await user.click(screen.getByText(value));
}
-function renderGlobalBackgroundTasksApp() {
- renderAppWithAdminContext('admin/background_tasks', routes, {});
+function renderGlobalBackgroundTasksApp(context: RenderContext = {}) {
+ renderAppWithAdminContext('admin/background_tasks', routes, context);
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { AppState } from '../../../types/appstate';
+import { EditionKey } from '../../../types/editions';
import { Task as ITask } from '../../../types/tasks';
import TaskActions from './TaskActions';
import TaskComponent from './TaskComponent';
import TaskDay from './TaskDay';
import TaskExecutionTime from './TaskExecutionTime';
import TaskId from './TaskId';
+import TaskNodeName from './TaskNodeName';
import TaskStatus from './TaskStatus';
import TaskSubmitter from './TaskSubmitter';
onFilterTask: (task: ITask) => void;
task: ITask;
previousTask?: ITask;
+ appState: AppState;
}
export default function Task(props: Props) {
- const { task, component, onCancelTask, onFilterTask, previousTask } = props;
+ const { task, component, onCancelTask, onFilterTask, previousTask, appState } = props;
return (
<tr>
<TaskComponent task={task} />
<TaskId id={task.id} />
<TaskSubmitter submitter={task.submitterLogin} />
+ {appState?.edition === EditionKey.datacenter && <TaskNodeName nodeName={task.nodeName} />}
<TaskDay
prevSubmittedAt={previousTask && previousTask.submittedAt}
submittedAt={task.submittedAt}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+
+interface Props {
+ nodeName?: string;
+}
+
+export default function TaskNodeName({ nodeName }: Props) {
+ return (
+ <td className="thin">
+ <div className="note">{nodeName}</div>
+ </td>
+ );
+}
*/
import classNames from 'classnames';
import * as React from 'react';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { translate } from '../../../helpers/l10n';
+import { AppState } from '../../../types/appstate';
+import { EditionKey } from '../../../types/editions';
import { Task as ITask } from '../../../types/tasks';
import Task from './Task';
loading: boolean;
onCancelTask: (task: ITask) => Promise<void>;
onFilterTask: (task: ITask) => void;
+ appState: AppState;
}
-export default function Tasks({ tasks, component, loading, onCancelTask, onFilterTask }: Props) {
+export function Tasks({ tasks, component, loading, onCancelTask, onFilterTask, appState }: Props) {
const className = classNames('data zebra zebra-hover background-tasks', {
'new-loading': loading,
});
<th>{translate('background_tasks.table.task')}</th>
<th>{translate('background_tasks.table.id')}</th>
<th>{translate('background_tasks.table.submitter')}</th>
+ {appState?.edition === EditionKey.datacenter && (
+ <th>{translate('background_tasks.table.nodeName')}</th>
+ )}
<th> </th>
<th className="text-right">{translate('background_tasks.table.submitted')}</th>
<th className="text-right">{translate('background_tasks.table.started')}</th>
onFilterTask={onFilterTask}
previousTask={index > 0 ? tasks[index - 1] : undefined}
task={task}
+ appState={appState}
/>
))}
</tbody>
</div>
);
}
+
+export default withAppStateContext(Tasks);
{
id: 'AWkGcOThOiAPiP5AE-kM',
type: 'VIEW_REFRESH',
+ nodeName: 'node_CE_server_1',
componentId: 'AWBLZYhGOUrjxRA-u6ex',
componentKey: 'sonar-csharp',
componentName: 'SonarC#',
}
}
/>
- <Tasks
+ <withAppStateContext(Tasks)
component={
{
"breadcrumbs": [],
"hasScannerContext": false,
"id": "AWkGcOThOiAPiP5AE-kM",
"logs": false,
+ "nodeName": "node_CE_server_1",
"startedAt": "2019-02-19T16:47:36+0100",
"status": "FAILED",
"submittedAt": "2019-02-19T16:47:35+0100",
export interface Task {
analysisId?: string;
branch?: string;
+ nodeName?: string;
componentKey?: string;
componentName?: string;
componentQualifier?: string;
background_tasks.table.started=Started
background_tasks.table.finished=Finished
background_tasks.table.duration=Duration
+background_tasks.table.nodeName=Node
background_tasks.filter_by_component_x=Filter by Component "{0}"
background_tasks.cancel_task=Cancel Task