aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-02-19 15:30:33 +0100
committerGuillaume Jambet <guillaume.jambet@gmail.com>2018-03-01 15:21:05 +0100
commit06feeac0dccc9491021fee7f57385b1ac8012a91 (patch)
tree437f9733327c6126b8b0edaa668682abc1a47983
parent6b3f8890aceafc0cc5e307e859170c3914bf2094 (diff)
downloadsonarqube-06feeac0dccc9491021fee7f57385b1ac8012a91.tar.gz
sonarqube-06feeac0dccc9491021fee7f57385b1ac8012a91.zip
SONAR-10346 Add a shortcut to display the latest delivery of a webhook
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java2
-rw-r--r--server/sonar-web/src/main/js/app/types.ts18
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/DeliveryAccordion.tsx107
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx114
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/LatestDeliveryForm.tsx98
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/WebhookItemLatestDelivery.tsx91
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryAccordion-test.tsx58
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryItem-test.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/LatestDeliveryForm-test.tsx61
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItemLatestDelivery-test.tsx67
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveriesForm-test.tsx.snap5
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryAccordion-test.tsx.snap56
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryItem-test.tsx.snap150
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/LatestDeliveryForm-test.tsx.snap80
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap51
-rw-r--r--server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItemLatestDelivery-test.tsx.snap49
-rw-r--r--server/sonar-web/src/main/js/components/controls/ListFooter.tsx9
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties4
21 files changed, 823 insertions, 284 deletions
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java
index bca17a54cb6..1e5a225cd48 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java
@@ -92,7 +92,7 @@ public class WebhooksPage {
SelenideElement webhook = getWebhook(webhookName);
webhook.$(".dropdown-toggle").shouldBe(visible).click();
webhook.$(".js-webhook-deliveries").shouldBe(visible).click();
- modalShouldBeOpen("Recent deliveries for " + webhookName);
+ modalShouldBeOpen("Recent deliveries of " + webhookName);
return new DeliveriesForm($(".modal-body"));
}
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts
index 43f2c58bad5..c2fe0d91b1c 100644
--- a/server/sonar-web/src/main/js/app/types.ts
+++ b/server/sonar-web/src/main/js/app/types.ts
@@ -385,16 +385,16 @@ export enum Visibility {
}
export interface Webhook {
- key: string;
- latestDelivery?: WebhookDelivery;
- name: string;
- url: string;
+ key: string;
+ latestDelivery?: WebhookDelivery;
+ name: string;
+ url: string;
}
export interface WebhookDelivery {
- at: string;
- durationMs: number;
- httpStatus: number;
- id: string;
- success: boolean;
+ at: string;
+ durationMs: number;
+ httpStatus?: number;
+ id: string;
+ success: boolean;
}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
index ce12280de2f..be4c4df298e 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import DeliveryItem from './DeliveryItem';
+import DeliveryAccordion from './DeliveryAccordion';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import ListFooter from '../../../components/controls/ListFooter';
import Modal from '../../../components/controls/Modal';
@@ -96,12 +96,13 @@ export default class DeliveriesForm extends React.PureComponent<Props, State> {
<h2>{header}</h2>
</header>
<div className="modal-body modal-container">
- {deliveries.map(delivery => <DeliveryItem delivery={delivery} key={delivery.id} />)}
+ {deliveries.map(delivery => <DeliveryAccordion delivery={delivery} key={delivery.id} />)}
<div className="text-center">
<DeferredSpinner loading={loading} />
</div>
{paging !== undefined && (
<ListFooter
+ className="little-spacer-bottom"
count={deliveries.length}
loadMore={this.fetchMoreDeliveries}
ready={!loading}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryAccordion.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryAccordion.tsx
new file mode 100644
index 00000000000..c5994e7a1c1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryAccordion.tsx
@@ -0,0 +1,107 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+import DeliveryItem from './DeliveryItem';
+import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
+import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
+import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
+import BoxedGroupAccordion from '../../../components/controls/BoxedGroupAccordion';
+import { getDelivery } from '../../../api/webhooks';
+import { WebhookDelivery } from '../../../app/types';
+
+interface Props {
+ delivery: WebhookDelivery;
+}
+
+interface State {
+ loading: boolean;
+ open: boolean;
+ payload?: string;
+}
+
+export default class DeliveryAccordion extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { loading: false, open: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchPayload = ({ delivery } = this.props) => {
+ this.setState({ loading: true });
+ return getDelivery({ deliveryId: delivery.id }).then(
+ ({ delivery }) => {
+ if (this.mounted) {
+ this.setState({ payload: delivery.payload, loading: false });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ formatPayload = (payload: string) => {
+ try {
+ return JSON.stringify(JSON.parse(payload), undefined, 2);
+ } catch (error) {
+ return payload;
+ }
+ };
+
+ handleClick = () => {
+ if (!this.state.payload) {
+ this.fetchPayload();
+ }
+ this.setState(({ open }) => ({ open: !open }));
+ };
+
+ render() {
+ const { delivery } = this.props;
+ const { loading, open, payload } = this.state;
+
+ return (
+ <BoxedGroupAccordion
+ onClick={this.handleClick}
+ open={open}
+ renderHeader={() =>
+ delivery.success ? (
+ <AlertSuccessIcon className="pull-right js-success" />
+ ) : (
+ <AlertErrorIcon className="pull-right js-error" />
+ )
+ }
+ title={<DateTimeFormatter date={delivery.at} />}>
+ <DeliveryItem
+ className="big-spacer-left"
+ delivery={delivery}
+ loading={loading}
+ payload={payload}
+ />
+ </BoxedGroupAccordion>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx
index cd75390ec0f..06f661468dd 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx
@@ -18,102 +18,46 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
-import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
-import BoxedGroupAccordion from '../../../components/controls/BoxedGroupAccordion';
import CodeSnippet from '../../../components/common/CodeSnippet';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
-import { getDelivery } from '../../../api/webhooks';
import { formatMeasure } from '../../../helpers/measures';
import { translateWithParameters, translate } from '../../../helpers/l10n';
import { WebhookDelivery } from '../../../app/types';
interface Props {
+ className?: string;
delivery: WebhookDelivery;
-}
-
-interface State {
loading: boolean;
- open: boolean;
- payload?: string;
+ payload: string | undefined;
}
-export default class DeliveryItem extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: false, open: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- fetchPayload = ({ delivery } = this.props) => {
- this.setState({ loading: true });
- return getDelivery({ deliveryId: delivery.id }).then(
- ({ delivery }) => {
- if (this.mounted) {
- this.setState({ payload: delivery.payload, loading: false });
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- };
-
- formatPayload = (payload: string) => {
- try {
- return JSON.stringify(JSON.parse(payload), undefined, 2);
- } catch (error) {
- return payload;
- }
- };
-
- handleClick = () => {
- if (!this.state.payload) {
- this.fetchPayload();
- }
- this.setState(({ open }) => ({ open: !open }));
- };
-
- render() {
- const { delivery } = this.props;
- const { loading, open, payload } = this.state;
+export default function DeliveryItem({ className, delivery, loading, payload }: Props) {
+ return (
+ <div className={className}>
+ <p className="spacer-bottom">
+ {translateWithParameters(
+ 'webhooks.delivery.response_x',
+ delivery.httpStatus || translate('webhooks.delivery.server_unreachable')
+ )}
+ </p>
+ <p className="spacer-bottom">
+ {translateWithParameters(
+ 'webhooks.delivery.duration_x',
+ formatMeasure(delivery.durationMs, 'MILLISEC')
+ )}
+ </p>
+ <p className="spacer-bottom">{translate('webhooks.delivery.payload')}</p>
+ <DeferredSpinner className="spacer-left spacer-top" loading={loading}>
+ {payload && <CodeSnippet noCopy={true} snippet={formatPayload(payload)} />}
+ </DeferredSpinner>
+ </div>
+ );
+}
- return (
- <BoxedGroupAccordion
- onClick={this.handleClick}
- open={open}
- renderHeader={() =>
- delivery.success ? (
- <AlertSuccessIcon className="pull-right js-success" />
- ) : (
- <AlertErrorIcon className="pull-right js-error" />
- )
- }
- title={<DateTimeFormatter date={delivery.at} />}>
- <div className="big-spacer-left">
- <p className="spacer-bottom">
- {translateWithParameters('webhooks.delivery.response_x', delivery.httpStatus)}
- </p>
- <p className="spacer-bottom">
- {translateWithParameters(
- 'webhooks.delivery.duration_x',
- formatMeasure(delivery.durationMs, 'MILLISEC')
- )}
- </p>
- <p className="spacer-bottom">{translate('webhooks.delivery.payload')}</p>
- <DeferredSpinner className="spacer-left spacer-top" loading={loading}>
- {payload && <CodeSnippet noCopy={true} snippet={this.formatPayload(payload)} />}
- </DeferredSpinner>
- </div>
- </BoxedGroupAccordion>
- );
+function formatPayload(payload: string) {
+ try {
+ return JSON.stringify(JSON.parse(payload), undefined, 2);
+ } catch (error) {
+ return payload;
}
}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/LatestDeliveryForm.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/LatestDeliveryForm.tsx
new file mode 100644
index 00000000000..ef4bbe178cb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/LatestDeliveryForm.tsx
@@ -0,0 +1,98 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+import DeliveryItem from './DeliveryItem';
+import Modal from '../../../components/controls/Modal';
+import { Webhook, WebhookDelivery } from '../../../app/types';
+import { translateWithParameters, translate } from '../../../helpers/l10n';
+import { getDelivery } from '../../../api/webhooks';
+
+interface Props {
+ delivery: WebhookDelivery;
+ onClose: () => void;
+ webhook: Webhook;
+}
+
+interface State {
+ loading: boolean;
+ payload?: string;
+}
+
+export default class LatestDeliveryForm extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { loading: true };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchPayload();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchPayload = ({ delivery } = this.props) => {
+ return getDelivery({ deliveryId: delivery.id }).then(
+ ({ delivery }) => {
+ if (this.mounted) {
+ this.setState({ payload: delivery.payload, loading: false });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ formatPayload = (payload: string) => {
+ try {
+ return JSON.stringify(JSON.parse(payload), undefined, 2);
+ } catch (error) {
+ return payload;
+ }
+ };
+
+ render() {
+ const { delivery, webhook } = this.props;
+ const { loading, payload } = this.state;
+ const header = translateWithParameters('webhooks.latest_delivery_for_x', webhook.name);
+
+ return (
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+ <DeliveryItem
+ className="modal-body modal-container"
+ delivery={delivery}
+ loading={loading}
+ payload={payload}
+ />
+ <footer className="modal-foot">
+ <button className="button-link js-modal-close" onClick={this.props.onClose} type="button">
+ {translate('close')}
+ </button>
+ </footer>
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx
index 5016508e13c..3bd7e4aa4b2 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx
@@ -18,12 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import WebhookItemLatestDelivery from './WebhookItemLatestDelivery';
import WebhookActions from './WebhookActions';
-import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
-import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
-import { Webhook, WebhookDelivery } from '../../../app/types';
-import { translate } from '../../../helpers/l10n';
+import { Webhook } from '../../../app/types';
interface Props {
onDelete: (webhook: string) => Promise<void>;
@@ -37,7 +34,7 @@ export default function WebhookItem({ onDelete, onUpdate, webhook }: Props) {
<td>{webhook.name}</td>
<td>{webhook.url}</td>
<td>
- <LatestDelivery latestDelivery={webhook.latestDelivery} />
+ <WebhookItemLatestDelivery webhook={webhook} />
</td>
<td className="thin nowrap text-right">
<WebhookActions onDelete={onDelete} onUpdate={onUpdate} webhook={webhook} />
@@ -45,17 +42,3 @@ export default function WebhookItem({ onDelete, onUpdate, webhook }: Props) {
</tr>
);
}
-
-export function LatestDelivery({ latestDelivery }: { latestDelivery?: WebhookDelivery }) {
- if (!latestDelivery) {
- return <span>{translate('webhooks.last_execution.none')}</span>;
- }
- return (
- <>
- {latestDelivery.success ? <AlertSuccessIcon /> : <AlertErrorIcon />}
- <span className="spacer-left">
- <DateTimeFormatter date={latestDelivery.at} />
- </span>
- </>
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItemLatestDelivery.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItemLatestDelivery.tsx
new file mode 100644
index 00000000000..417790dddeb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItemLatestDelivery.tsx
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+import LatestDeliveryForm from './LatestDeliveryForm';
+import BulletListIcon from '../../../components/icons-components/BulletListIcon';
+import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
+import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
+import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
+import { ButtonIcon } from '../../../components/ui/buttons';
+import { Webhook } from '../../../app/types';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ webhook: Webhook;
+}
+
+interface State {
+ modal: boolean;
+}
+
+export default class WebhookItemLatestDelivery extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { modal: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleClick = () => {
+ this.setState({ modal: true });
+ };
+
+ handleModalClose = () => {
+ if (this.mounted) {
+ this.setState({ modal: false });
+ }
+ };
+
+ render() {
+ const { webhook } = this.props;
+ if (!webhook.latestDelivery) {
+ return <span>{translate('webhooks.last_execution.none')}</span>;
+ }
+
+ const { modal } = this.state;
+ return (
+ <>
+ {webhook.latestDelivery.success ? (
+ <AlertSuccessIcon className="text-text-top" />
+ ) : (
+ <AlertErrorIcon className="text-text-top" />
+ )}
+ <span className="spacer-left display-inline-flex-center">
+ <DateTimeFormatter date={webhook.latestDelivery.at} />
+ <ButtonIcon className="button-small little-spacer-left" onClick={this.handleClick}>
+ <BulletListIcon />
+ </ButtonIcon>
+ </span>
+
+ {modal && (
+ <LatestDeliveryForm
+ delivery={webhook.latestDelivery}
+ onClose={this.handleModalClose}
+ webhook={webhook}
+ />
+ )}
+ </>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryAccordion-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryAccordion-test.tsx
new file mode 100644
index 00000000000..47aab5ee108
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryAccordion-test.tsx
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+import { shallow } from 'enzyme';
+import DeliveryAccordion from '../DeliveryAccordion';
+import { getDelivery } from '../../../../api/webhooks';
+
+jest.mock('../../../../api/webhooks', () => ({
+ getDelivery: jest.fn(() =>
+ Promise.resolve({
+ delivery: { payload: '{ "success": true }' }
+ })
+ )
+}));
+
+const delivery = {
+ at: '12.02.2018',
+ durationMs: 20,
+ httpStatus: 200,
+ id: '2',
+ success: true
+};
+
+beforeEach(() => {
+ (getDelivery as jest.Mock<any>).mockClear();
+});
+
+it('should render correctly', async () => {
+ const wrapper = getWrapper();
+ expect(wrapper).toMatchSnapshot();
+
+ wrapper.find('BoxedGroupAccordion').prop<Function>('onClick')();
+ await new Promise(setImmediate);
+ expect(getDelivery).lastCalledWith({ deliveryId: delivery.id });
+ wrapper.update();
+ expect(wrapper).toMatchSnapshot();
+});
+
+function getWrapper(props = {}) {
+ return shallow(<DeliveryAccordion delivery={delivery} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryItem-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryItem-test.tsx
index 585df65cab5..f9a912b73ce 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryItem-test.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/DeliveryItem-test.tsx
@@ -20,15 +20,6 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import DeliveryItem from '../DeliveryItem';
-import { getDelivery } from '../../../../api/webhooks';
-
-jest.mock('../../../../api/webhooks', () => ({
- getDelivery: jest.fn(() =>
- Promise.resolve({
- delivery: { payload: '{ "success": true }' }
- })
- )
-}));
const delivery = {
at: '12.02.2018',
@@ -38,21 +29,26 @@ const delivery = {
success: true
};
-beforeEach(() => {
- (getDelivery as jest.Mock<any>).mockClear();
-});
-
-it('should render correctly', async () => {
+it('should render correctly', () => {
const wrapper = getWrapper();
expect(wrapper).toMatchSnapshot();
+});
- wrapper.find('BoxedGroupAccordion').prop<Function>('onClick')();
- await new Promise(setImmediate);
- expect(getDelivery).lastCalledWith({ deliveryId: delivery.id });
- wrapper.update();
- expect(wrapper).toMatchSnapshot();
+it('should render correctly when no payload', () => {
+ expect(getWrapper({ loading: true, payload: undefined })).toMatchSnapshot();
+});
+
+it('should render correctly when no http status', () => {
+ expect(getWrapper({ delivery: { ...delivery, httpStatus: undefined } })).toMatchSnapshot();
});
function getWrapper(props = {}) {
- return shallow(<DeliveryItem delivery={delivery} {...props} />);
+ return shallow(
+ <DeliveryItem
+ delivery={delivery}
+ loading={false}
+ payload={'{ status: "SUCCESS" }'}
+ {...props}
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/LatestDeliveryForm-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/LatestDeliveryForm-test.tsx
new file mode 100644
index 00000000000..6df63daca71
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/LatestDeliveryForm-test.tsx
@@ -0,0 +1,61 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+import { shallow } from 'enzyme';
+import LatestDeliveryForm from '../LatestDeliveryForm';
+import { getDelivery } from '../../../../api/webhooks';
+
+jest.mock('../../../../api/webhooks', () => ({
+ getDelivery: jest.fn(() =>
+ Promise.resolve({
+ delivery: { payload: '{ "success": true }' }
+ })
+ )
+}));
+
+const delivery = {
+ at: '12.02.2018',
+ durationMs: 20,
+ httpStatus: 200,
+ id: '2',
+ success: true
+};
+
+const webhook = { key: '1', name: 'foo', url: 'http://foo.bar' };
+
+beforeEach(() => {
+ (getDelivery as jest.Mock<any>).mockClear();
+});
+
+it('should render correctly', async () => {
+ const wrapper = getWrapper();
+ expect(wrapper).toMatchSnapshot();
+
+ await new Promise(setImmediate);
+ expect(getDelivery).lastCalledWith({ deliveryId: delivery.id });
+ wrapper.update();
+ expect(wrapper).toMatchSnapshot();
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+ <LatestDeliveryForm delivery={delivery} onClose={jest.fn()} webhook={webhook} {...props} />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx
index 6861daae1d2..06335817f04 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx
@@ -19,21 +19,12 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import WebhookItem, { LatestDelivery } from '../WebhookItem';
-
-const latestDelivery = {
- at: '12.02.2018',
- durationMs: 20,
- httpStatus: 200,
- id: '2',
- success: true
-};
+import WebhookItem from '../WebhookItem';
const webhook = {
key: '1',
name: 'my webhook',
- url: 'http://webhook.target',
- latestDelivery
+ url: 'http://webhook.target'
};
it('should render correctly', () => {
@@ -47,13 +38,3 @@ it('should render correctly', () => {
)
).toMatchSnapshot();
});
-
-it('should render correctly the latest delivery', () => {
- expect(shallow(<LatestDelivery latestDelivery={undefined} />)).toMatchSnapshot();
- expect(shallow(<LatestDelivery latestDelivery={latestDelivery} />)).toMatchSnapshot();
- expect(
- shallow(
- <LatestDelivery latestDelivery={{ ...latestDelivery, httpStatus: 500, success: false }} />
- )
- ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItemLatestDelivery-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItemLatestDelivery-test.tsx
new file mode 100644
index 00000000000..a705c21801c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItemLatestDelivery-test.tsx
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+import { shallow } from 'enzyme';
+import WebhookItemLatestDelivery from '../WebhookItemLatestDelivery';
+import { click } from '../../../../helpers/testUtils';
+
+const latestDelivery = {
+ at: '12.02.2018',
+ durationMs: 20,
+ httpStatus: 200,
+ id: '2',
+ success: true
+};
+
+const webhook = {
+ key: '1',
+ name: 'my webhook',
+ url: 'http://webhook.target',
+ latestDelivery
+};
+
+it('should render correctly a success delivery', () => {
+ expect(shallow(<WebhookItemLatestDelivery webhook={webhook} />)).toMatchSnapshot();
+});
+
+it('should render correctly when no latest delivery', () => {
+ expect(
+ shallow(<WebhookItemLatestDelivery webhook={{ ...webhook, latestDelivery: undefined }} />)
+ ).toMatchSnapshot();
+});
+
+it('should render correctly a failed delivery', () => {
+ expect(
+ shallow(
+ <WebhookItemLatestDelivery
+ webhook={{
+ ...webhook,
+ latestDelivery: { ...latestDelivery, httpStatus: 500, success: false }
+ }}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('should display the latest delivery form', () => {
+ const wrapper = shallow(<WebhookItemLatestDelivery webhook={webhook} />);
+ click(wrapper.find('ButtonIcon'));
+ expect(wrapper.find('LatestDeliveryForm').exists()).toBeTruthy();
+});
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveriesForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveriesForm-test.tsx.snap
index 42cff706441..10fa36144a7 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveriesForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveriesForm-test.tsx.snap
@@ -53,7 +53,7 @@ exports[`should render correctly 2`] = `
<div
className="modal-body modal-container"
>
- <DeliveryItem
+ <DeliveryAccordion
delivery={
Object {
"at": "12.02.2018",
@@ -65,7 +65,7 @@ exports[`should render correctly 2`] = `
}
key="2"
/>
- <DeliveryItem
+ <DeliveryAccordion
delivery={
Object {
"at": "11.02.2018",
@@ -86,6 +86,7 @@ exports[`should render correctly 2`] = `
/>
</div>
<ListFooter
+ className="little-spacer-bottom"
count={2}
loadMore={[Function]}
ready={true}
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryAccordion-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryAccordion-test.tsx.snap
new file mode 100644
index 00000000000..75c25d02d2c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryAccordion-test.tsx.snap
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<BoxedGroupAccordion
+ onClick={[Function]}
+ open={false}
+ renderHeader={[Function]}
+ title={
+ <DateTimeFormatter
+ date="12.02.2018"
+ />
+ }
+>
+ <DeliveryItem
+ className="big-spacer-left"
+ delivery={
+ Object {
+ "at": "12.02.2018",
+ "durationMs": 20,
+ "httpStatus": 200,
+ "id": "2",
+ "success": true,
+ }
+ }
+ loading={false}
+ />
+</BoxedGroupAccordion>
+`;
+
+exports[`should render correctly 2`] = `
+<BoxedGroupAccordion
+ onClick={[Function]}
+ open={true}
+ renderHeader={[Function]}
+ title={
+ <DateTimeFormatter
+ date="12.02.2018"
+ />
+ }
+>
+ <DeliveryItem
+ className="big-spacer-left"
+ delivery={
+ Object {
+ "at": "12.02.2018",
+ "durationMs": 20,
+ "httpStatus": 200,
+ "id": "2",
+ "success": true,
+ }
+ }
+ loading={false}
+ payload="{ \\"success\\": true }"
+ />
+</BoxedGroupAccordion>
+`;
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryItem-test.tsx.snap
index 350ab5714c6..a39dd9db7b8 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/DeliveryItem-test.tsx.snap
@@ -1,84 +1,86 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render correctly 1`] = `
-<BoxedGroupAccordion
- onClick={[Function]}
- open={false}
- renderHeader={[Function]}
- title={
- <DateTimeFormatter
- date="12.02.2018"
- />
- }
->
- <div
- className="big-spacer-left"
+<div>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.response_x.200
+ </p>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.duration_x.20ms
+ </p>
+ <p
+ className="spacer-bottom"
>
- <p
- className="spacer-bottom"
- >
- webhooks.delivery.response_x.200
- </p>
- <p
- className="spacer-bottom"
- >
- webhooks.delivery.duration_x.20ms
- </p>
- <p
- className="spacer-bottom"
- >
- webhooks.delivery.payload
- </p>
- <DeferredSpinner
- className="spacer-left spacer-top"
- loading={false}
- timeout={100}
+ webhooks.delivery.payload
+ </p>
+ <DeferredSpinner
+ className="spacer-left spacer-top"
+ loading={false}
+ timeout={100}
+ >
+ <CodeSnippet
+ noCopy={true}
+ snippet="{ status: \\"SUCCESS\\" }"
/>
- </div>
-</BoxedGroupAccordion>
+ </DeferredSpinner>
+</div>
`;
-exports[`should render correctly 2`] = `
-<BoxedGroupAccordion
- onClick={[Function]}
- open={true}
- renderHeader={[Function]}
- title={
- <DateTimeFormatter
- date="12.02.2018"
+exports[`should render correctly when no http status 1`] = `
+<div>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.response_x.webhooks.delivery.server_unreachable
+ </p>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.duration_x.20ms
+ </p>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.payload
+ </p>
+ <DeferredSpinner
+ className="spacer-left spacer-top"
+ loading={false}
+ timeout={100}
+ >
+ <CodeSnippet
+ noCopy={true}
+ snippet="{ status: \\"SUCCESS\\" }"
/>
- }
->
- <div
- className="big-spacer-left"
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`should render correctly when no payload 1`] = `
+<div>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.response_x.200
+ </p>
+ <p
+ className="spacer-bottom"
+ >
+ webhooks.delivery.duration_x.20ms
+ </p>
+ <p
+ className="spacer-bottom"
>
- <p
- className="spacer-bottom"
- >
- webhooks.delivery.response_x.200
- </p>
- <p
- className="spacer-bottom"
- >
- webhooks.delivery.duration_x.20ms
- </p>
- <p
- className="spacer-bottom"
- >
- webhooks.delivery.payload
- </p>
- <DeferredSpinner
- className="spacer-left spacer-top"
- loading={false}
- timeout={100}
- >
- <CodeSnippet
- noCopy={true}
- snippet="{
- \\"success\\": true
-}"
- />
- </DeferredSpinner>
- </div>
-</BoxedGroupAccordion>
+ webhooks.delivery.payload
+ </p>
+ <DeferredSpinner
+ className="spacer-left spacer-top"
+ loading={true}
+ timeout={100}
+ />
+</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/LatestDeliveryForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/LatestDeliveryForm-test.tsx.snap
new file mode 100644
index 00000000000..eff0d0fa8f5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/LatestDeliveryForm-test.tsx.snap
@@ -0,0 +1,80 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Modal
+ contentLabel="webhooks.latest_delivery_for_x.foo"
+ onRequestClose={[MockFunction]}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ webhooks.latest_delivery_for_x.foo
+ </h2>
+ </header>
+ <DeliveryItem
+ className="modal-body modal-container"
+ delivery={
+ Object {
+ "at": "12.02.2018",
+ "durationMs": 20,
+ "httpStatus": 200,
+ "id": "2",
+ "success": true,
+ }
+ }
+ loading={true}
+ />
+ <footer
+ className="modal-foot"
+ >
+ <button
+ className="button-link js-modal-close"
+ onClick={[MockFunction]}
+ type="button"
+ >
+ close
+ </button>
+ </footer>
+</Modal>
+`;
+
+exports[`should render correctly 2`] = `
+<Modal
+ contentLabel="webhooks.latest_delivery_for_x.foo"
+ onRequestClose={[MockFunction]}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ webhooks.latest_delivery_for_x.foo
+ </h2>
+ </header>
+ <DeliveryItem
+ className="modal-body modal-container"
+ delivery={
+ Object {
+ "at": "12.02.2018",
+ "durationMs": 20,
+ "httpStatus": 200,
+ "id": "2",
+ "success": true,
+ }
+ }
+ loading={false}
+ payload="{ \\"success\\": true }"
+ />
+ <footer
+ className="modal-foot"
+ >
+ <button
+ className="button-link js-modal-close"
+ onClick={[MockFunction]}
+ type="button"
+ >
+ close
+ </button>
+ </footer>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap
index 99338c1907f..08780ac6201 100644
--- a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap
@@ -9,14 +9,12 @@ exports[`should render correctly 1`] = `
http://webhook.target
</td>
<td>
- <LatestDelivery
- latestDelivery={
+ <WebhookItemLatestDelivery
+ webhook={
Object {
- "at": "12.02.2018",
- "durationMs": 20,
- "httpStatus": 200,
- "id": "2",
- "success": true,
+ "key": "1",
+ "name": "my webhook",
+ "url": "http://webhook.target",
}
}
/>
@@ -30,13 +28,6 @@ exports[`should render correctly 1`] = `
webhook={
Object {
"key": "1",
- "latestDelivery": Object {
- "at": "12.02.2018",
- "durationMs": 20,
- "httpStatus": 200,
- "id": "2",
- "success": true,
- },
"name": "my webhook",
"url": "http://webhook.target",
}
@@ -45,35 +36,3 @@ exports[`should render correctly 1`] = `
</td>
</tr>
`;
-
-exports[`should render correctly the latest delivery 1`] = `
-<span>
- webhooks.last_execution.none
-</span>
-`;
-
-exports[`should render correctly the latest delivery 2`] = `
-<React.Fragment>
- <AlertSuccessIcon />
- <span
- className="spacer-left"
- >
- <DateTimeFormatter
- date="12.02.2018"
- />
- </span>
-</React.Fragment>
-`;
-
-exports[`should render correctly the latest delivery 3`] = `
-<React.Fragment>
- <AlertErrorIcon />
- <span
- className="spacer-left"
- >
- <DateTimeFormatter
- date="12.02.2018"
- />
- </span>
-</React.Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItemLatestDelivery-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItemLatestDelivery-test.tsx.snap
new file mode 100644
index 00000000000..43db29698f9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItemLatestDelivery-test.tsx.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly a failed delivery 1`] = `
+<React.Fragment>
+ <AlertErrorIcon
+ className="text-text-top"
+ />
+ <span
+ className="spacer-left display-inline-flex-center"
+ >
+ <DateTimeFormatter
+ date="12.02.2018"
+ />
+ <ButtonIcon
+ className="button-small little-spacer-left"
+ onClick={[Function]}
+ >
+ <BulletListIcon />
+ </ButtonIcon>
+ </span>
+</React.Fragment>
+`;
+
+exports[`should render correctly a success delivery 1`] = `
+<React.Fragment>
+ <AlertSuccessIcon
+ className="text-text-top"
+ />
+ <span
+ className="spacer-left display-inline-flex-center"
+ >
+ <DateTimeFormatter
+ date="12.02.2018"
+ />
+ <ButtonIcon
+ className="button-small little-spacer-left"
+ onClick={[Function]}
+ >
+ <BulletListIcon />
+ </ButtonIcon>
+ </span>
+</React.Fragment>
+`;
+
+exports[`should render correctly when no latest delivery 1`] = `
+<span>
+ webhooks.last_execution.none
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/components/controls/ListFooter.tsx b/server/sonar-web/src/main/js/components/controls/ListFooter.tsx
index 3d438c9cf0e..d17a5a3385e 100644
--- a/server/sonar-web/src/main/js/components/controls/ListFooter.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ListFooter.tsx
@@ -24,6 +24,7 @@ import { formatMeasure } from '../../helpers/measures';
interface Props {
count: number;
+ className?: string;
loadMore?: () => void;
ready?: boolean;
total: number;
@@ -44,9 +45,11 @@ export default function ListFooter({ ready = true, ...props }: Props) {
{translate('show_more')}
</a>
);
- const className = classNames('spacer-top note text-center', {
- 'new-loading': !ready
- });
+ const className = classNames(
+ 'spacer-top note text-center',
+ { 'new-loading': !ready },
+ props.className
+ );
return (
<footer className={className}>
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 282bd2bf3cc..28e78bea51d 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2819,13 +2819,15 @@ webhooks.delete=Delete Webhook
webhooks.delete.confirm=Are you sure you want to delete the webhook "{0}"?
webhooks.description=Webhooks are used to notify external services when a project analysis is done. An HTTP POST request including a JSON payload is sent to each of the provided URLs. Learn more in the {url}.
webhooks.deliveries.show=Show recent deliveries
-webhooks.deliveries_for_x=Recent deliveries for {0}
+webhooks.deliveries_for_x=Recent deliveries of {0}
webhooks.delivery.duration_x=Duration: {0}
webhooks.delivery.payload=Payload:
webhooks.delivery.response_x=Response: {0}
+webhooks.delivery.server_unreachable=Server Unreachable
webhooks.documentation_link=Webhooks documentation
webhooks.last_execution=Last delivery
webhooks.last_execution.none=Never
+webhooks.latest_delivery_for_x=Last delivery of {0}
webhooks.maximum_reached=You reached your maximum number of {0} webhooks. You can still update or delete an existing one.
webhooks.name=Name
webhooks.name.required=Name is required.