SelenideElement webhook = getWebhook(webhookName); | SelenideElement webhook = getWebhook(webhookName); | ||||
webhook.$(".dropdown-toggle").shouldBe(visible).click(); | webhook.$(".dropdown-toggle").shouldBe(visible).click(); | ||||
webhook.$(".js-webhook-deliveries").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")); | return new DeliveriesForm($(".modal-body")); | ||||
} | } | ||||
} | } | ||||
export interface Webhook { | export interface Webhook { | ||||
key: string; | |||||
latestDelivery?: WebhookDelivery; | |||||
name: string; | |||||
url: string; | |||||
key: string; | |||||
latestDelivery?: WebhookDelivery; | |||||
name: string; | |||||
url: string; | |||||
} | } | ||||
export interface WebhookDelivery { | export interface WebhookDelivery { | ||||
at: string; | |||||
durationMs: number; | |||||
httpStatus: number; | |||||
id: string; | |||||
success: boolean; | |||||
at: string; | |||||
durationMs: number; | |||||
httpStatus?: number; | |||||
id: string; | |||||
success: boolean; | |||||
} | } |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import DeliveryItem from './DeliveryItem'; | |||||
import DeliveryAccordion from './DeliveryAccordion'; | |||||
import DeferredSpinner from '../../../components/common/DeferredSpinner'; | import DeferredSpinner from '../../../components/common/DeferredSpinner'; | ||||
import ListFooter from '../../../components/controls/ListFooter'; | import ListFooter from '../../../components/controls/ListFooter'; | ||||
import Modal from '../../../components/controls/Modal'; | import Modal from '../../../components/controls/Modal'; | ||||
<h2>{header}</h2> | <h2>{header}</h2> | ||||
</header> | </header> | ||||
<div className="modal-body modal-container"> | <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"> | <div className="text-center"> | ||||
<DeferredSpinner loading={loading} /> | <DeferredSpinner loading={loading} /> | ||||
</div> | </div> | ||||
{paging !== undefined && ( | {paging !== undefined && ( | ||||
<ListFooter | <ListFooter | ||||
className="little-spacer-bottom" | |||||
count={deliveries.length} | count={deliveries.length} | ||||
loadMore={this.fetchMoreDeliveries} | loadMore={this.fetchMoreDeliveries} | ||||
ready={!loading} | ready={!loading} |
/* | |||||
* 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> | |||||
); | |||||
} | |||||
} |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | 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 CodeSnippet from '../../../components/common/CodeSnippet'; | ||||
import DeferredSpinner from '../../../components/common/DeferredSpinner'; | import DeferredSpinner from '../../../components/common/DeferredSpinner'; | ||||
import { getDelivery } from '../../../api/webhooks'; | |||||
import { formatMeasure } from '../../../helpers/measures'; | import { formatMeasure } from '../../../helpers/measures'; | ||||
import { translateWithParameters, translate } from '../../../helpers/l10n'; | import { translateWithParameters, translate } from '../../../helpers/l10n'; | ||||
import { WebhookDelivery } from '../../../app/types'; | import { WebhookDelivery } from '../../../app/types'; | ||||
interface Props { | interface Props { | ||||
className?: string; | |||||
delivery: WebhookDelivery; | delivery: WebhookDelivery; | ||||
} | |||||
interface State { | |||||
loading: boolean; | 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; | |||||
} | } | ||||
} | } |
/* | |||||
* 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> | |||||
); | |||||
} | |||||
} |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import WebhookItemLatestDelivery from './WebhookItemLatestDelivery'; | |||||
import WebhookActions from './WebhookActions'; | 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 { | interface Props { | ||||
onDelete: (webhook: string) => Promise<void>; | onDelete: (webhook: string) => Promise<void>; | ||||
<td>{webhook.name}</td> | <td>{webhook.name}</td> | ||||
<td>{webhook.url}</td> | <td>{webhook.url}</td> | ||||
<td> | <td> | ||||
<LatestDelivery latestDelivery={webhook.latestDelivery} /> | |||||
<WebhookItemLatestDelivery webhook={webhook} /> | |||||
</td> | </td> | ||||
<td className="thin nowrap text-right"> | <td className="thin nowrap text-right"> | ||||
<WebhookActions onDelete={onDelete} onUpdate={onUpdate} webhook={webhook} /> | <WebhookActions onDelete={onDelete} onUpdate={onUpdate} webhook={webhook} /> | ||||
</tr> | </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> | |||||
</> | |||||
); | |||||
} |
/* | |||||
* 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} | |||||
/> | |||||
)} | |||||
</> | |||||
); | |||||
} | |||||
} |
/* | |||||
* 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} />); | |||||
} |
import * as React from 'react'; | import * as React from 'react'; | ||||
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import DeliveryItem from '../DeliveryItem'; | import DeliveryItem from '../DeliveryItem'; | ||||
import { getDelivery } from '../../../../api/webhooks'; | |||||
jest.mock('../../../../api/webhooks', () => ({ | |||||
getDelivery: jest.fn(() => | |||||
Promise.resolve({ | |||||
delivery: { payload: '{ "success": true }' } | |||||
}) | |||||
) | |||||
})); | |||||
const delivery = { | const delivery = { | ||||
at: '12.02.2018', | at: '12.02.2018', | ||||
success: true | success: true | ||||
}; | }; | ||||
beforeEach(() => { | |||||
(getDelivery as jest.Mock<any>).mockClear(); | |||||
}); | |||||
it('should render correctly', async () => { | |||||
it('should render correctly', () => { | |||||
const wrapper = getWrapper(); | const wrapper = getWrapper(); | ||||
expect(wrapper).toMatchSnapshot(); | 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 = {}) { | function getWrapper(props = {}) { | ||||
return shallow(<DeliveryItem delivery={delivery} {...props} />); | |||||
return shallow( | |||||
<DeliveryItem | |||||
delivery={delivery} | |||||
loading={false} | |||||
payload={'{ status: "SUCCESS" }'} | |||||
{...props} | |||||
/> | |||||
); | |||||
} | } |
/* | |||||
* 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} /> | |||||
); | |||||
} |
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { shallow } from 'enzyme'; | 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 = { | const webhook = { | ||||
key: '1', | key: '1', | ||||
name: 'my webhook', | name: 'my webhook', | ||||
url: 'http://webhook.target', | |||||
latestDelivery | |||||
url: 'http://webhook.target' | |||||
}; | }; | ||||
it('should render correctly', () => { | it('should render correctly', () => { | ||||
) | ) | ||||
).toMatchSnapshot(); | ).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(); | |||||
}); |
/* | |||||
* 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(); | |||||
}); |
<div | <div | ||||
className="modal-body modal-container" | className="modal-body modal-container" | ||||
> | > | ||||
<DeliveryItem | |||||
<DeliveryAccordion | |||||
delivery={ | delivery={ | ||||
Object { | Object { | ||||
"at": "12.02.2018", | "at": "12.02.2018", | ||||
} | } | ||||
key="2" | key="2" | ||||
/> | /> | ||||
<DeliveryItem | |||||
<DeliveryAccordion | |||||
delivery={ | delivery={ | ||||
Object { | Object { | ||||
"at": "11.02.2018", | "at": "11.02.2018", | ||||
/> | /> | ||||
</div> | </div> | ||||
<ListFooter | <ListFooter | ||||
className="little-spacer-bottom" | |||||
count={2} | count={2} | ||||
loadMore={[Function]} | loadMore={[Function]} | ||||
ready={true} | ready={true} |
// 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> | |||||
`; |
// Jest Snapshot v1, https://goo.gl/fbAQLP | // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||
exports[`should render correctly 1`] = ` | 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> | |||||
`; | `; |
// 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> | |||||
`; |
http://webhook.target | http://webhook.target | ||||
</td> | </td> | ||||
<td> | <td> | ||||
<LatestDelivery | |||||
latestDelivery={ | |||||
<WebhookItemLatestDelivery | |||||
webhook={ | |||||
Object { | Object { | ||||
"at": "12.02.2018", | |||||
"durationMs": 20, | |||||
"httpStatus": 200, | |||||
"id": "2", | |||||
"success": true, | |||||
"key": "1", | |||||
"name": "my webhook", | |||||
"url": "http://webhook.target", | |||||
} | } | ||||
} | } | ||||
/> | /> | ||||
webhook={ | webhook={ | ||||
Object { | Object { | ||||
"key": "1", | "key": "1", | ||||
"latestDelivery": Object { | |||||
"at": "12.02.2018", | |||||
"durationMs": 20, | |||||
"httpStatus": 200, | |||||
"id": "2", | |||||
"success": true, | |||||
}, | |||||
"name": "my webhook", | "name": "my webhook", | ||||
"url": "http://webhook.target", | "url": "http://webhook.target", | ||||
} | } | ||||
</td> | </td> | ||||
</tr> | </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> | |||||
`; |
// 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> | |||||
`; |
interface Props { | interface Props { | ||||
count: number; | count: number; | ||||
className?: string; | |||||
loadMore?: () => void; | loadMore?: () => void; | ||||
ready?: boolean; | ready?: boolean; | ||||
total: number; | total: number; | ||||
{translate('show_more')} | {translate('show_more')} | ||||
</a> | </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 ( | return ( | ||||
<footer className={className}> | <footer className={className}> |
webhooks.delete.confirm=Are you sure you want to delete the webhook "{0}"? | 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.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.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.duration_x=Duration: {0} | ||||
webhooks.delivery.payload=Payload: | webhooks.delivery.payload=Payload: | ||||
webhooks.delivery.response_x=Response: {0} | webhooks.delivery.response_x=Response: {0} | ||||
webhooks.delivery.server_unreachable=Server Unreachable | |||||
webhooks.documentation_link=Webhooks documentation | webhooks.documentation_link=Webhooks documentation | ||||
webhooks.last_execution=Last delivery | webhooks.last_execution=Last delivery | ||||
webhooks.last_execution.none=Never | 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.maximum_reached=You reached your maximum number of {0} webhooks. You can still update or delete an existing one. | ||||
webhooks.name=Name | webhooks.name=Name | ||||
webhooks.name.required=Name is required. | webhooks.name.required=Name is required. |