Compare commits
3 Commits
codex/refa
...
codex/refa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56b644abcf | ||
|
|
a02c893aee | ||
|
|
70758ef686 |
@@ -141,6 +141,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/(shareLayout)/webapp-signin/components/mail-and-code-auth.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx": {
|
||||
"jsx_a11y/tabindex-no-positive": {
|
||||
"count": 3
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import MailAndCodeAuth from '../mail-and-code-auth'
|
||||
|
||||
const pushMock = vi.fn()
|
||||
const replaceMock = vi.fn()
|
||||
const setCountdownLeftTimeMock = vi.fn()
|
||||
const sendWebAppEMailLoginCodeMock = vi.fn()
|
||||
const searchParams = new URLSearchParams({
|
||||
redirect_url: encodeURIComponent('/chatbot/test-app'),
|
||||
})
|
||||
|
||||
vi.mock('@/next/navigation', () => ({
|
||||
useRouter: () => ({ push: pushMock, replace: replaceMock }),
|
||||
useSearchParams: () => searchParams,
|
||||
}))
|
||||
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
useLocale: () => 'en-US',
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/signin/storage', () => ({
|
||||
COUNT_DOWN_TIME_MS: 60_000,
|
||||
useSetCountdownLeftTime: () => setCountdownLeftTimeMock,
|
||||
}))
|
||||
|
||||
vi.mock('@/service/common', () => ({
|
||||
sendWebAppEMailLoginCode: (...args: unknown[]) => sendWebAppEMailLoginCodeMock(...args),
|
||||
}))
|
||||
|
||||
describe('MailAndCodeAuth', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
sendWebAppEMailLoginCodeMock.mockResolvedValue({ result: 'success', data: 'token-abc' })
|
||||
})
|
||||
|
||||
it('submits from the email field through the native form', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<MailAndCodeAuth />)
|
||||
|
||||
const emailInput = screen.getByRole('textbox', { name: 'login.email' })
|
||||
const submitButton = screen.getByRole('button', { name: 'login.signup.verifyMail' })
|
||||
|
||||
expect(emailInput).toHaveAttribute('name', 'email')
|
||||
expect(emailInput).toHaveAttribute('autocomplete', 'email')
|
||||
expect(emailInput).toHaveAttribute('spellcheck', 'false')
|
||||
expect(submitButton).toHaveAttribute('type', 'submit')
|
||||
|
||||
await user.tab()
|
||||
expect(emailInput).toHaveFocus()
|
||||
await user.type(emailInput, 'user@example.com{Enter}')
|
||||
|
||||
await waitFor(() => {
|
||||
expect(sendWebAppEMailLoginCodeMock).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
expect(pushMock).toHaveBeenCalledWith(expect.stringContaining('/webapp-signin/check-code?'))
|
||||
})
|
||||
})
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Field, FieldControl, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Form } from '@langgenius/dify-ui/form'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { resolveWebAppLoginRedirect } from '@/app/(shareLayout)/webapp-signin/login-redirect'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { COUNT_DOWN_TIME_MS, useSetCountdownLeftTime } from '@/app/components/signin/storage'
|
||||
import { emailRegex } from '@/config'
|
||||
import { useLocale } from '@/context/i18n'
|
||||
@@ -64,37 +64,33 @@ export default function MailAndCodeAuth() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Form
|
||||
onFormSubmit={() => {
|
||||
void handleGetEMailVerificationCode()
|
||||
}}
|
||||
>
|
||||
<Field name="email" className="mb-2 block">
|
||||
<FieldLabel className="my-2 py-0 system-md-semibold! text-text-secondary">
|
||||
<form onSubmit={noop}>
|
||||
<input type="text" className="hidden" />
|
||||
<div className="mb-2">
|
||||
<label htmlFor="email" className="my-2 system-md-semibold text-text-secondary">
|
||||
{t(($) => $.email, { ns: 'login' })}
|
||||
</FieldLabel>
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<FieldControl
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
spellCheck={false}
|
||||
value={email}
|
||||
placeholder={t(($) => $.emailPlaceholder, { ns: 'login' }) as string}
|
||||
onValueChange={setEmail}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<Button
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={loading || !email}
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
onClick={handleGetEMailVerificationCode}
|
||||
>
|
||||
{t(($) => $['signup.verifyMail'], { ns: 'login' })}
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
</Form>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,9 +74,7 @@ describe('AddExternalAPIModal', () => {
|
||||
|
||||
it('should render close button', () => {
|
||||
render(<AddExternalAPIModal {...defaultProps} />)
|
||||
// Close button is rendered in a portal
|
||||
const closeButton = document.body.querySelector('.action-btn')
|
||||
expect(closeButton)!.toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'common.operation.close' })).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -388,9 +386,7 @@ describe('AddExternalAPIModal', () => {
|
||||
const onCancel = vi.fn()
|
||||
render(<AddExternalAPIModal {...defaultProps} onCancel={onCancel} />)
|
||||
|
||||
// Close button is rendered in a portal
|
||||
const closeButton = document.body.querySelector('.action-btn')!
|
||||
fireEvent.click(closeButton)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'common.operation.close' }))
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { FC } from 'react'
|
||||
import type { CreateExternalAPIReq, FormSchema } from '../declarations'
|
||||
import {
|
||||
AlertDialog,
|
||||
@@ -13,7 +12,6 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Dialog, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { RiBook2Line, RiCloseLine, RiInformation2Line, RiLock2Fill } from '@remixicon/react'
|
||||
import { memo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
@@ -66,14 +64,14 @@ const emptyExternalAPIFormData: CreateExternalAPIReq = {
|
||||
},
|
||||
}
|
||||
|
||||
const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({
|
||||
const AddExternalAPIModal = ({
|
||||
data,
|
||||
onSave,
|
||||
onCancel,
|
||||
datasetBindings,
|
||||
isEditMode,
|
||||
onEdit,
|
||||
}) => {
|
||||
}: AddExternalAPIModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [showConfirm, setShowConfirm] = useState(false)
|
||||
@@ -154,7 +152,7 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({
|
||||
type="button"
|
||||
className="flex size-3.5 items-center justify-center rounded-sm outline-hidden hover:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
>
|
||||
<RiInformation2Line className="size-3.5" />
|
||||
<span aria-hidden className="i-ri-information-2-line size-3.5" />
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
@@ -171,7 +169,10 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({
|
||||
key={binding.id}
|
||||
className="flex items-center gap-1 self-stretch px-2 py-1"
|
||||
>
|
||||
<RiBook2Line className="size-4 text-text-secondary" />
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-ri-book-2-line size-4 text-text-secondary"
|
||||
/>
|
||||
<div className="system-sm-medium text-text-secondary">
|
||||
{binding.name}
|
||||
</div>
|
||||
@@ -184,8 +185,12 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ActionButton className="absolute top-5 right-5" onClick={onCancel}>
|
||||
<RiCloseLine className="h-4.5 w-4.5 shrink-0 text-text-tertiary" />
|
||||
<ActionButton
|
||||
aria-label={t(($) => $['operation.close'], { ns: 'common' })}
|
||||
className="absolute top-5 right-5"
|
||||
onClick={onCancel}
|
||||
>
|
||||
<span aria-hidden className="i-ri-close-line h-4.5 w-4.5 shrink-0 text-text-tertiary" />
|
||||
</ActionButton>
|
||||
<Form
|
||||
value={formData}
|
||||
@@ -211,7 +216,7 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center justify-center gap-1 self-stretch rounded-b-2xl border-t-[0.5px] border-divider-subtle bg-background-soft px-2 py-3 system-xs-regular text-text-tertiary">
|
||||
<RiLock2Fill className="size-3 text-text-quaternary" />
|
||||
<span aria-hidden className="i-ri-lock-2-fill size-3 text-text-quaternary" />
|
||||
{t(($) => $['externalAPIForm.encrypted.front'], { ns: 'dataset' })}
|
||||
<a
|
||||
className="text-text-accent"
|
||||
|
||||
Reference in New Issue
Block a user