first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { screen, fireEvent, render, within } from '@testing-library/react'
import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/integration-widget'
import * as eventTracking from '@/infrastructure/event-tracking'
describe('<IntegrationLinkingWidgetTest/>', function () {
const defaultProps = {
logo: <div />,
title: 'Integration',
description: 'paragraph1',
helpPath: '/learn',
linkPath: '/link',
unlinkPath: '/unlink',
unlinkConfirmationTitle: 'confirm unlink',
unlinkConfirmationText: 'you will be unlinked',
}
describe('when the feature is not available', function () {
let sendMBSpy: sinon.SinonSpy
beforeEach(function () {
sendMBSpy = sinon.spy(eventTracking, 'sendMB')
render(<IntegrationLinkingWidget {...defaultProps} hasFeature={false} />)
})
afterEach(function () {
sendMBSpy.restore()
})
it("should render 'Premium feature' label", function () {
screen.getByText('Premium feature')
})
it('should render an upgrade link and track clicks', function () {
const upgradeLink = screen.getByRole('button', { name: 'Upgrade' })
expect(upgradeLink.getAttribute('href')).to.equal(
'/user/subscription/plans'
)
fireEvent.click(upgradeLink)
expect(sendMBSpy).to.be.calledOnce
expect(sendMBSpy).calledWith('settings-upgrade-click')
})
})
describe('when the integration is not linked', function () {
beforeEach(function () {
render(
<IntegrationLinkingWidget {...defaultProps} hasFeature linked={false} />
)
})
it('should render a link to initiate integration linking', function () {
expect(
screen.getByRole('button', { name: 'Link' }).getAttribute('href')
).to.equal('/link')
})
it("should not render 'premium feature' labels", function () {
expect(screen.queryByText('premium_feature')).to.not.exist
expect(screen.queryByText('integration_is_a_premium_feature')).to.not
.exist
})
})
describe('when the integration is linked', function () {
beforeEach(function () {
render(
<IntegrationLinkingWidget
{...defaultProps}
hasFeature
linked
statusIndicator={<div>status indicator</div>}
/>
)
})
it('should render a status indicator', function () {
screen.getByText('status indicator')
})
it("should not render 'premium feature' labels", function () {
expect(screen.queryByText('premium_feature')).to.not.exist
expect(screen.queryByText('integration_is_a_premium_feature')).to.not
.exist
})
it('should display an `unlink` button', function () {
screen.getByRole('button', { name: 'Unlink' })
})
it('should open a modal with a link to confirm integration unlinking', function () {
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
const withinModal = within(screen.getByRole('dialog'))
withinModal.getByText('confirm unlink')
withinModal.getByText('you will be unlinked')
withinModal.getByRole('button', { name: 'Cancel' })
withinModal.getByRole('button', { name: 'Unlink' })
})
it('should cancel unlinking when clicking "cancel" in the confirmation modal', async function () {
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
screen.getByText('confirm unlink')
const cancelBtn = screen.getByRole('button', {
name: 'Cancel',
hidden: false,
})
fireEvent.click(cancelBtn)
await screen.findByRole('button', { name: 'Cancel', hidden: true })
})
})
})

View File

@@ -0,0 +1,136 @@
import { expect } from 'chai'
import sinon from 'sinon'
import {
screen,
fireEvent,
render,
waitFor,
within,
} from '@testing-library/react'
import { FetchError } from '../../../../../../frontend/js/infrastructure/fetch-json'
import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/sso-widget'
describe('<SSOLinkingWidget />', function () {
const defaultProps = {
providerId: 'integration_id',
title: 'integration',
description: 'integration description',
helpPath: '/help/integration',
linkPath: '/integration/link',
onUnlink: () => Promise.resolve(),
}
it('should render', function () {
render(<SSOLinkingWidget {...defaultProps} />)
screen.getByText('integration')
screen.getByText('integration description')
expect(
screen.getByRole('link', { name: 'Learn more' }).getAttribute('href')
).to.equal('/help/integration')
})
describe('when unlinked', function () {
it('should render a link to `linkPath`', function () {
render(<SSOLinkingWidget {...defaultProps} linked={false} />)
expect(
screen.getByRole('button', { name: 'Link' }).getAttribute('href')
).to.equal('/integration/link?intent=link')
})
})
describe('when linked', function () {
let unlinkFunction: sinon.SinonStub
beforeEach(function () {
unlinkFunction = sinon.stub()
render(
<SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
)
})
it('should display an `unlink` button', function () {
screen.getByRole('button', { name: 'Unlink' })
})
it('should open a modal to confirm integration unlinking', function () {
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
screen.getByText('Unlink integration Account')
screen.getByText(
'Warning: When you unlink your account from integration you will not be able to sign in using integration anymore.'
)
})
it('should cancel unlinking when clicking cancel in the confirmation modal', async function () {
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
const cancelBtn = screen.getByRole('button', {
name: 'Cancel',
hidden: false,
})
fireEvent.click(cancelBtn)
await screen.findByRole('button', { name: 'Cancel', hidden: true })
expect(unlinkFunction).not.to.have.been.called
})
})
describe('unlinking an account', function () {
let confirmBtn: HTMLElement, unlinkFunction: sinon.SinonStub
beforeEach(function () {
unlinkFunction = sinon.stub()
render(
<SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
)
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
confirmBtn = within(screen.getByRole('dialog')).getByRole('button', {
name: 'Unlink',
hidden: false,
})
})
it('should make an `unlink` request', function () {
unlinkFunction.resolves()
fireEvent.click(confirmBtn)
expect(unlinkFunction).to.have.been.called
})
it('should display feedback while the request is inflight', async function () {
unlinkFunction.returns(
new Promise<void>(resolve => {
setTimeout(resolve, 500)
})
)
fireEvent.click(confirmBtn)
await waitFor(() =>
expect(screen.getByRole('button', { name: 'Unlinking' }))
)
})
})
describe('when unlinking fails', function () {
beforeEach(function () {
const unlinkFunction = sinon
.stub()
.rejects(new FetchError('unlinking failed', ''))
render(
<SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
)
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
const confirmBtn = within(screen.getByRole('dialog')).getByRole(
'button',
{
name: 'Unlink',
hidden: false,
}
)
fireEvent.click(confirmBtn)
})
it('should display an error message ', async function () {
await screen.findByText('Something went wrong. Please try again.')
})
it('should display the unlink button ', async function () {
await screen.findByRole('button', { name: 'Unlink' })
})
})
})